quant的全新时代:机器人智能+人类智慧
简单地说,如果我们买入/持有近段时间跑赢大盘指数的股票,那么收益曲线也会跑赢大盘。 如下图是一只近期跑赢大盘的股票,无论是在A、B、C、D点买入,无论买入点是基于基本面、技术面、消息面、X面,无论是根据股评大师们口中的金叉、死叉、OOXX、X叉,无论是打算做长线、短线、X线,在个股跑赢大盘期间,收益曲线都是跑赢大盘指数的🙂。
相反,如果我们买入/持有近段时间跑输大盘指数的股票。。。基本面、技术面、消息面、X面。。。金叉、死叉、OOXX、X叉。。。长线、短线、X线,在个股跑输大盘期间,收益曲线都是跑输大盘指数的🙂。
总的来说 买入/持有近段时间跑赢大盘指数的股票,那么收益曲线也会跑赢大盘; 买入/持有近段时间跑输大盘指数的股票,那么收益曲线也会跑输大盘指数 <- 天呐,这不是21世纪最废的废话了吗??!!
没错,这篇文章就是研究这个问题的: 假如我们要求即将买入(无论是基于什么原因买入)的股票在近期是跑赢大盘的,并且在股票不再跑赢大盘指数了的时候卖出(即便后面还有上涨空间,但只要它跑输大盘就要卖出了,避免它拖累收益曲线跑输大盘指数) 会是一副什么样的情景呢?
下面开始做实验。
等等,上面的 近期是跑赢大盘 的“近期”是多久呢? 1天,10天,or 100天?“跑赢大盘”跑赢多少算数呢? 0.01%,1%,or 10%+? 用全市场5天内最能跑赢大盘的前5个股票行不行呢?
有人做过实验,结果显示不行!
那怎么办呢? 这个神圣的任务就交给机器学习吧。它的工作原理是这样的:
step 1. 计算全部交割单买入点前n*1, n*2, ...n*x天至买入当天的涨速(可以使用收盘价的线性拟合角度,或者均线的线性拟合角度,或者干脆统计价格涨幅百分比)。
step 2. 将它们分组,比如说买入点前n*1天 跌5%-10%, n*2天跌2%-5%的为一组; n*1天 涨0%-5%, n*2涨2%-5%的为一组 等等。
step 3. 假如它发现 买入点前n*1天 跌5%-10%, n*2天跌2%-5% 这一组的交易90+%都是亏钱的,那么下次它在下次发现发出特征类似这一组的交易时就拦截。我们(人类)如果有兴趣可以去深扒成功/错误的命中率都集中在哪里,如果没有兴趣也可以不管了,反正裁判默认只会拦截失败可能性>成功可能性的交易。
但是裁判是基于机器学习的,如果我们不告诉它有“买入/并持有近段时间跑赢大盘指数的股票,那么收益曲线也会跑赢大盘”这么一回事,它是不懂得这么观察和分析交易的。
if I say Audi and BMW, Boeing, Airbus, and Titanic belongs to 2 different groups, It would predict Audi and BMW belongs to group 1 and Boeing, Airbus, and Titanic belongs to group 2. But it cannot say what does group 1 and group 2 actually refers to, some human need to look at them and say — hey these are cars. Machine learning obviously can’t gure it out. If I say those objects belong to 3 di erent groups it can say Titanic belongs to group 3 because it’s features are drastically different from others, but can never say it is a ship.
如果我(人类)告诉机器人奥迪和宝马、波音、空客和泰坦尼克号属于两个个不同的组,那么奥迪和宝马属于第一组,波音、空客和泰坦尼克号属于第二组。但是机器学习并不知道第一组和第二组实际上指的是什么,人类一看就明白 ——嘿,这些是汽车。机器学习显然没有这个能力。如果我说这些物体属于三个不同的组,它会把泰坦尼克号归类到第三组,因为它的功能与其他物体截然不同,但它不知道是泰坦尼克号一艘船。
-- https://medium.com/@sapy/get-over-the-machine-learning-hype-79abcbe37272
下面是1个我们只需要提供一些idea,然后由机器学习完成精细的定量分析,并指导交易的例子。
* 这一拨新裁判是刚刚完成“代码工程”和训练的,由于笔者迫不及待看到它们的效果,所以让它们一起上阵了,没有深扒其中单独的一个(例如上面提到的观察个股近期是否跑赢大盘的那一个)对整个收益曲线的影响。但是它们十几个裁判一起把本来亏损的策略改善到年化30%左右,盈亏比3%+ 显然已经很了不起了。
接上一篇《弱人工智能否帮助无知小散从市场获利?答案是:甚至能帮助小狗获利》
本次有以下不同:
1.更改卖出因子的配置参数,只为了使交易更频繁(结果没有裁判参与交易的情况下当然是亏损更严重),制造更多交割单给裁判们学习。 <- 这不是重点。
2.更改了仓位管理策略。 <- 这也不是重点。
3.新增了好几个自定义的机器人裁判扭转局势:
# 机器人裁判,通过对比个股买入点前n*1,n*2,n*x天至买入当天的均线角度 和 对应大盘
# 的均线角度 得知该股是否跑赢大盘,以及跑赢百分比
ump.manager.append_user_ump(WzUmpEdgeMaSpeed)
# 机器人裁判,观察买入点前n*1,n*2,n*x天至买入当天的价格rank(相当于压力位/支撑位)和
# 均线角度
ump.manager.append_user_ump(WzUmpEdgePriceWithMaDeg)
# 机器人裁判,观察买入点前n*1,n*2,n*x天至买入当天的个股对应
# 大盘指数(上证50,上证,深证,创业)的均线角度
ump.manager.append_user_ump(WzUmpEdgeIndexMaDeg)
# 机器人裁判,观察买入点前n*1,n*2,n*x天至买入当天的个股对应大盘指数的处于
# 趋势or震荡(采用多项式拟合技术)
ump.manager.append_user_ump(WzUmpEdgeIndexPoly)
# 下面就不一样介绍了
ump.manager.append_user_ump(WzUmpEdgeMaDeg)
ump.manager.append_user_ump(WzUmpEdgePoly)
ump.manager.append_user_ump(WzUmpEdgeMaSpeedWithIndMaDegAndSmlr)
ump.manager.append_user_ump(WzUmpEdgeIndexPolyAndMaDeg)
ump.manager.append_user_ump(WzUmpEdgePolyWithMaDeg)
新增裁判较多,这里挑选WzUmpEdgeMaSpeed
这个出来讲解。这个裁判通过对比个股买入点前n*1,n*2,n*x天至买入当天的均线角度 和 对应大盘的均线角度, 从而得知该股是否跑赢大盘,以及跑赢百分比。
由于abupy内置的裁判都没有通过这种视觉观察交易的, 我们需要编辑代码(相当于特征工程)来指导裁判从这个视觉观察和分析交易:
def calc_feature(self, kl_pd, combine_kl_pd, day_ind, buy_feature):
"""
根据买入或者卖出时的金融时间序列,以及交易日信息构造拟合角度特征
:param kl_pd: 择时阶段金融时间序列
:param combine_kl_pd: 合并择时阶段之前1年的金融时间序列
:param day_ind: 交易发生的时间索引,即对应self.kl_pd.key
:param buy_feature: 是否是买入特征构造(bool)
:return: 构造角度特征的键值对字典
"""
# 返回的角度特征键值对字典
deg_dict = {}
for dk in self.keys:
xd,ma_timeperiod = dk.split('/')
xd = int(xd)
ma_timeperiod = int(ma_timeperiod)
# 迭代预设角度周期,计算构建特征
ma_deg_key = xd + ma_timeperiod
if day_ind - ma_deg_key >= 0:
# 如果择时时间序列够提取特征,使用kl_pd截取特征交易周期收盘价格
# deg_close = kl_pd[day_ind - ma_deg_key + 1:day_ind + 1].close
kl_local = kl_pd[day_ind - ma_deg_key + 1:day_ind + 1]
else:
# 如果择时时间序列不够提取特征,使用combine_kl_pd截取特征交易周期,首先截取直到day_ind的时间序列
combine_kl_pd = combine_kl_pd.loc[:kl_pd.index[day_ind]]
# 如combine_kl_pd长度大于特征周期长度-> 截取combine_kl_pd[-dk:].close,否则取combine_kl_pd所有交易收盘价格
# deg_close = combine_kl_pd[-ma_deg_key:].close if combine_kl_pd.shape[0] > ma_deg_key else combine_kl_pd.close
kl_local= combine_kl_pd[-ma_deg_key:] if combine_kl_pd.shape[0] > ma_deg_key else combine_kl_pd
# 准备获取大盘指数k线图数据
start = kl_local.head(1).date[0]
end= kl_local.tail(1).date[0]
start_date = moment.date(str(start), "%Y%m%d").format("YYYY-MM-DD")
end_date = moment.date(str(end), "%Y%m%d").format("YYYY-MM-DD")
index_symbol = self.detect_index_symbol(kl_pd.name) # <- 返回大盘指数symbol
# 开始获取大盘指数k线图数据
index_kl = ABuSymbolPd.make_kl_df(index_symbol, start=start_date, end=end_date)
# 计算近期大盘均线
index_ma = MaService.calc_ma(index_symbol, index_kl, ma_timeperiod, MA_Type.EMA)
# 计算近期大盘均线角度
index_ang = ABuRegUtil.calc_regress_deg(index_ma.tail(xd), show=False)
# 标准化拟合角度值
index_ang = 0 if np.isnan(index_ang) else round(index_ang, 3)
# 计算个股均线
ma = MaService.calc_ma(kl_pd.name, kl_local, ma_timeperiod, MA_Type.EMA)
# print('index_ma:', kl_pd.name, ma_timeperiod, index_ma)
# print('ma', kl_pd.name, ma_timeperiod , ma)
# 计算均线拟合角度
ang = ABuRegUtil.calc_regress_deg(ma.tail(xd), show=False)
# 标准化拟合角度值
ang = 0 if np.isnan(ang) else round(ang, 3)
diff = ang - index_ang
diff = 0 if np.isnan(diff) else round(diff, 3)
# print(dk , diff)
# 角度特征键值对字典添加拟合角度周期key和对应的拟合角度值
deg_dict['{}speed_diff_deg{}'.format(self.feature_prefix(buy_feature=buy_feature), dk)] = diff
return deg_dict
以上是特征class的关键代码。还有裁判class的代码也只是几十行,这里就不贴上来了。
到此,人类要做的事情就差不多完成了,剩下的交给机器。
以下为jupyter notebook预览,:
在新的窗口中打开 | 下载jupyter notebook在本地浏览
本策略实盘演示:
在新的窗口中打开 | 下载jupyter notebook在本地浏览
ps: 由于整个研究比较沉长,可以点击上面链接下载jupyter notebook在本地浏览。 the easiest way to 搭建和配置jupyter研究环境是使用一行command。 在jupyter中可以借助左边的toc(table of content)导航能很方便浏览整个research:
总结: