5分钟极速入坑量化交易

In [13]:
# 安装必须的库
# 这里是在web浏览器中运行的jupyter lab, "!" 后面紧接着shell command
! pip install abupy  -i "https://mirrors.aliyun.com/pypi/simple/"
! pip install tushare  -i "https://mirrors.aliyun.com/pypi/simple/"
! pip install mplfinance  -i "https://mirrors.aliyun.com/pypi/simple/"
Looking in indexes: https://mirrors.aliyun.com/pypi/simple/
Collecting mplfinance
  Downloading https://mirrors.aliyun.com/pypi/packages/6a/d7/6cf850a7d033e997b6b0f934840b4540d054c26f809d158b33dc94e6d1fb/mplfinance-0.12.3a3-py3-none-any.whl
Requirement already satisfied: matplotlib in /opt/conda/lib/python3.7/site-packages (from mplfinance) (3.1.1)
Requirement already satisfied: pandas in /opt/conda/lib/python3.7/site-packages (from mplfinance) (0.25.3)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.7/site-packages (from matplotlib->mplfinance) (0.10.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.7/site-packages (from matplotlib->mplfinance) (1.1.0)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /opt/conda/lib/python3.7/site-packages (from matplotlib->mplfinance) (2.4.6)
Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/lib/python3.7/site-packages (from matplotlib->mplfinance) (2.8.1)
Requirement already satisfied: numpy>=1.11 in /opt/conda/lib/python3.7/site-packages (from matplotlib->mplfinance) (1.18.1)
Requirement already satisfied: pytz>=2017.2 in /opt/conda/lib/python3.7/site-packages (from pandas->mplfinance) (2019.3)
Requirement already satisfied: six in /opt/conda/lib/python3.7/site-packages (from cycler>=0.10->matplotlib->mplfinance) (1.13.0)
Requirement already satisfied: setuptools in /opt/conda/lib/python3.7/site-packages (from kiwisolver>=1.0.1->matplotlib->mplfinance) (44.0.0.post20200106)
Installing collected packages: mplfinance
Successfully installed mplfinance-0.12.3a3
In [8]:
# 
import os
import sys
# 使用insert 0即只使用github,避免交叉使用了pip安装的abupy,导致的版本不一致问题
sys.path.insert(0, os.path.abspath('../'))
import abupy

from abupy import env
abupy.env.disable_example_env_ipython()
disable example env

量化交易可以简单的理解为“用计算机程式代码分析行情和执行交易”。 so,你得先有行情数据。

行情数据

Wind(万得)是知名的行情数据提供商,主要服务于机构投资者,且是收费的。

免费的alternative有Tushare, Akshare等。 下面使用Tushare。

In [4]:
import tushare as ts
from config import tushare_config 
ts.set_token(tushare_config.get_token())
pro = ts.pro_api()

# kl_df: KLine pandas dataFrame 
# 600519是贵州茅台哈
kl_df = ts.pro_bar(ts_code= '600519.sh', adj='qfq', asset='E',
                     start_date='2018-08-08', end_date='2020-02-22')
kl_df
Out[4]:
ts_code trade_date open high low close pre_close change pct_chg vol amount
0 600519.SH 20200221 1111.4600 1123.5400 1110.0100 1112.8800 1118.0000 -5.1200 -0.4580 28940.08 3.2253e+06
1 600519.SH 20200220 1096.7000 1124.0000 1095.4100 1118.0000 1096.5000 21.5000 1.9608 46802.62 5.2207e+06
2 600519.SH 20200219 1085.0000 1101.5000 1078.0000 1096.5000 1084.0000 12.5000 1.1531 37051.47 4.0524e+06
3 600519.SH 20200218 1090.0100 1096.8800 1083.0000 1084.0000 1093.8200 -9.8200 -0.8978 26664.96 2.8977e+06
4 600519.SH 20200217 1082.5000 1096.1900 1082.4000 1093.8200 1088.0000 5.8200 0.5349 27028.22 2.9462e+06
... ... ... ... ... ... ... ... ... ... ... ...
367 600519.SH 20180814 669.2902 674.4930 663.6636 670.1869 669.2902 0.8967 0.1340 21696.83 1.4741e+06
368 600519.SH 20180813 673.0149 673.0149 656.9335 669.2902 677.1338 -7.8436 -1.1584 34570.41 2.3293e+06
369 600519.SH 20180810 681.7651 684.6128 674.1973 677.1338 681.7651 -4.6313 -0.6793 23817.67 1.6440e+06
370 600519.SH 20180809 661.1903 684.3468 660.3232 681.7651 666.3340 15.4311 2.3158 34485.75 2.3685e+06
371 600519.SH 20180808 676.9761 678.9272 662.0476 666.3340 676.2469 -9.9129 -1.4659 29685.44 2.0164e+06

372 rows × 11 columns

对,就是这些数据。我们在炒股app看到的各种花狸狐哨/huā lí hú shào/(胡里花俏/胡里花哨)的图表基本都是用这样包含了 open(开盘价)、high(最高价)、low(最低价)、close(收盘价)、vol(成交量)、 amount(成交额)的数据计算出来的。

不过数据是给计算机读的,人类一般看行情图。

行情图

In [25]:
from abupy import abu, ABuSymbolPd
# from  abupy.MarketBu import  ABuMarketDrawing
import mplfinance as mpf
from wz.TushareKlineDs import TushareKlineDs

abupy.env.g_private_data_source = TushareKlineDs

kl_pd = ABuSymbolPd.make_kl_df('600519',  start='2016-06-06', end='2020-02-22')

kl_pd = kl_pd[['open', 'close', 'high', 'low', 'volume', 'date']]
kl_pd.columns = ['Open', 'Close', 'High', 'Low', 'Volume', 'Date']
# 最近100天K线图,现在是夜间,不想亮瞎眼就用夜间主题了
mpf.plot(kl_pd[:100], type='candle', style='nightclouds')

是简陋了点, 不过可以加上均线和成交量。

By the way,这种简单画法叫“调包侠(自己不懂太多知识,也不做太多活,工作全靠调用别人懂得开发包完成)”。 故作高深一步一步学习画图请查看 数据科学实战的《手把手教你使用Matplotlib绘图|实战》

In [29]:
# 最近200个交易日K线、成交量和10,20,60日简单移动均线(SMA)
mpf.plot(kl_pd[:200], type='candle', style='nightclouds', mav=(10,20,60), volume=True)

还可以继续加各种技术指标等花狸狐哨/huā lí hú shào/(胡里花俏/胡里花哨)的,就像各类炒股app看起来的样子,不过这不是这里的重点,就不举例了。我们进入下一个主题。

回测 (历史回溯测试)

假如在书上/网上看到“大师”说:“x日均线上穿y日均线买入,z日均线下穿y日均线卖出(或者金叉/死叉理论)能赚钱。”, 信还是不信呢? 这是个问题。对于很多人,这个得靠信仰!!

对于程序员,这个当然得靠代码了。试一下:

In [53]:
from abupy import AbuDoubleMaBuy, AbuDoubleMaSell
from abupy import AbuFactorCloseAtrNStop, AbuFactorAtrNStop, AbuFactorPreAtrNStop
from abupy import abu, ABuProgress, AbuMetricsBase, EMarketDataFetchMode, EMarketTargetType, nd

from abupy import slippage 
# 开启针对非集合竞价阶段的涨停,滑点买入价格以高概率在接近涨停的价格买入 
slippage.sbb.g_enable_limit_up = True 
# 将集合竞价阶段的涨停买入成功概率设置为0,如果设置为0.2即20%概率成功买入 
slippage.sbb.g_pre_limit_up_rate = 0 
# 开启针对非集合竞价阶段的跌停,滑点卖出价格以高概率在接近跌停的价格卖出 
slippage.ssb.g_enable_limit_down = True 
# 将集合竞价阶段的跌停卖出成功概率设置为0, 如果设置为0.2即20%概率成功卖出 
slippage.ssb.g_pre_limit_down_rate = 0

abupy.env.g_data_fetch_mode = EMarketDataFetchMode.E_DATA_FETCH_FORCE_LOCAL
abupy.env.g_market_target = EMarketTargetType.E_MARKET_TARGET_CN

# 总资金
cash = 200000
# 单次买入最大仓位
o_pos_base =0.1 
abupy.beta.atr.g_atr_pos_base =  o_pos_base  * 8

def run_loo_back(choice_symbols, ps=None, n_folds=2, start='2012-12-12', end='2020-01-20', only_info=False):
    """封装一个回测函数,返回回测结果,以及回测度量对象"""
    abu_result_tuple, _ = abu.run_loop_back(cash,
                                           buy_factors,
                                           sell_factors,
                                           ps,
                                           start=start,
                                           end=end,
                                           n_folds=n_folds,
                                           choice_symbols=choice_symbols,
                                           n_process_kl = 1,
                                            n_process_pick= 1,
                                           )
    ABuProgress.clear_output()
    metrics = AbuMetricsBase.show_general(*abu_result_tuple, returns_cmp=only_info, 
                                only_info=only_info,
                                only_show_returns=True)
    return abu_result_tuple, metrics

# 买入卖出因子
buy_factors = [{'fast': 20, 'slow': 60, 'class': AbuDoubleMaBuy}]
sell_factors = [{'fast': 20, 'slow': 60, 'class': AbuDoubleMaSell},
                {'stop_loss_n': 2.2, #'stop_win_n': 3.0,
                 'class': AbuFactorAtrNStop},
                {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 2.2}]

abu_result_tuple, metrics = run_loo_back(['601318'])
买入后卖出的交易数量:10
买入后尚未卖出的交易数量:0
胜率:60.0000%
平均获利期望:13.7619%
平均亏损期望:-1.7437%
盈亏比:11.8621
策略收益: 53.3029%
基准收益: 49.6493%
策略年化收益: 7.9107%
基准年化收益: 7.3684%
策略买入成交比例:100.0000%
策略资金利用率比例:11.5117%
策略共执行1698个交易日

上面只是测试了使用20日/60日均线金叉/死叉策略买卖601318(中国平安)这只股票,看起来好像能赚钱。但是单只不能说明什么问题。 以下从市场随机选择66只股试一下。

In [54]:
import random
from  stock_pool import stock_pool

symbols = [] 
# 中国A股全市科创版除外
symbols =stock_pool.get('cn')
symbols = random.sample(list(symbols), 66)

print(symbols)
/opt/notebooks/stock_pool/data/cn/1.md
['000973', '002618', '601218', '002542', '600546', '300512', '603877', '000982', '000898', '300370', '002524', '600530', '601333', '000806', '000963', '600375', '002946', '600312', '600639', '600335', '600845', '603022', '000821', '300663', '002919', '600981', '603730', '300666', '300284', '600011', '000762', '603815', '002571', '300600', '002537', '600958', '300231', '300705', '603103', '002321', '002271', '600268', '002575', '600987', '600000', '300351', '600037', '603169', '000755', '002909', '600084', '603058', '603233', '002343', '601118', '601225', '000402', '002087', '601717', '601098', '300187', '603721', '600531', '002106', '603867', '603992']
In [56]:
from picker.WzPickNonNew import WzPickNonNew
from abupy import slippage 

# 开启针对非集合竞价阶段的涨停,滑点买入价格以高概率在接近涨停的价格买入 
slippage.sbb.g_enable_limit_up = True 
# 将集合竞价阶段的涨停买入成功概率设置为0,如果设置为0.2即20%概率成功买入 
slippage.sbb.g_pre_limit_up_rate = 0 
# 开启针对非集合竞价阶段的跌停,滑点卖出价格以高概率在接近跌停的价格卖出 
slippage.ssb.g_enable_limit_down = True 
# 将集合竞价阶段的跌停卖出成功概率设置为0, 如果设置为0.2即20%概率成功卖出 
slippage.ssb.g_pre_limit_down_rate = 0

o_pos_base =0.1 
print('o_pos_base:', o_pos_base)
abupy.beta.atr.g_atr_pos_base =  o_pos_base  * 1.0

# 选股因子,不交易24周内ipo新股
pickers = [
    {'class': WzPickNonNew,
     'pick_period': 'week',
     'weeks': 24},
]

buy_factors = [{'class': AbuDoubleMaBuy, 'fast': 20, 'slow': 60, 'stock_pickers': pickers,}]

abu_result_tuple, metrics = run_loo_back(symbols)
买入后卖出的交易数量:580
买入后尚未卖出的交易数量:30
胜率:36.7241%
平均获利期望:17.4993%
平均亏损期望:-8.6223%
盈亏比:1.2624
策略收益: 109.6186%
基准收益: 49.6493%
策略年化收益: 16.2685%
基准年化收益: 7.3684%
策略买入成交比例:59.3443%
策略资金利用率比例:54.6709%
策略共执行1698个交易日

如果从2012-12-12传说中的世界末日后开始至2020-01-20疑似世界末日,一直坚持用这个均线金叉/死叉策略交易中国A股股票似乎可以获得年化收益16.2685%的收益。

但是最倒霉的情况会亏掉:

In [57]:
metrics.plot_max_draw_down()
最大回撤: 0.416925
最大回测启始时间:2015-06-12, 结束时间2019-01-02, 共回测239219.575000

小结: 相比靠信仰,程序员可以靠代码。 尽管上面例子的选股、择时、仓位管理都是使用abupy内置的因子,但作为程序员,自己有什么idea可以编写因子代码,然后用历史数据测试,看看获利效果。

蒙特卡罗模拟

上面策略的收益并不算好,最大回撤0.416925也挺可怕的, 会不会是我们用的参数 'fast': 20, 'slow': 60,(20/60日均线金叉/死叉)并不是最好的? 非常值得怀疑! 作为程序员,遍历近可能的参数组合运行上面的过程可以简单理解为蒙特卡罗。由于这种方法落后了,这里就不演示了。

量化交易行业制高点 —— 人工智能(机器学习)

机器学习的发展也对量化投资起了促进作用 —— 百度百科

常常关注这个博客就可以看到很多这方面文章了哈。 好了5分钟的时间也差不多结束了。

结束了

以上我们刚刚接触了一点量化交易的皮毛,但是量化交易之门已经开启了。