Python 基于北向资金的择时买入卖出策略复现
大家好,我是安果!
最近自己一直在学习理财相关的东西,所以后面一段时间,可能会多分享一些这方面的学习笔记
其实在学习之前,我一直在思考一个问题:为什么我们在市场上赚不了钱?可能基金还好点,只要行情不是特别差,你一直定投总会有多多少少有点收益的,但是其他市场就真不好说了。
观察了一下身边的朋友和同事,其实大多数人都是入门的。比如说有坚信价值投资的,拿着白马一拿就是好几年的;也有观察市场热点的,追涨杀跌玩的不亦乐乎。但是大家的收益都很一般,甚至亏钱的也不少。
所以,我在思考的时候就在想 价值投资和短线热点 这事。其实价值投资没有问题,但是拿的太久就是问题;短线热点也没有问题,但是没选对具体的个股基金就是问题。
并且,我自己是研究数据分析的,是不是可以在市场的基础上加一些数据分析导向的内容?或者是在数据的基础上,偏向于热点市场的板块?
目前来看,基于上面这个问题的思考,我是有一些小小的收获的。
但是我也还在学习中,今天的文章甚至后期的文章都只是一个阶段性的学习笔记,不敢说文章的方法适合于所有人。所以大家抄作业的时候也多多思考一下,毕竟方法是我的,但是钱是你的啊!
ok,说了些闲话,开始今天的正文。
今天的文章是对一个择时策略的复现,数据是基于每日大A北上资金进行的。这个策略很简单的一个应用:当市场处于持续低谷的时候,你可以加大你定投的比例;当市场开始火热一段时间了,你需要慢慢减少你定投的金额。
就像那句话:在别人贪婪的时候我恐惧,在别人空据的时候我贪婪。
以下是真正的正文:
在文章开始之前,先给大家普及一下北上资金的概念,懂的同学可以跳过去看下一节
北上资金?
在中国股市中有“南北”之称,一般“北”指的是沪深两市的股票,“南”指的是香港股票。因此,北上资金就是指从香港股票中流入大陆股市的资金,一般为香港资金以及国际资金。
从历史数据来看, 不管是长线短线,北上资金都比A股大部分投资者聪明。原因有很多,比如说:可能是外资的投资经验丰富信息渠道广,也有可能是内地部分游资通过北向资金通道进来,也就是换了一层马甲。
所以,在每天实盘的时候,大家可以关注北向资金的买入情况,偷懒一点的也可以跟着交易。但是但是但是,这里面有两个概念特别容易被大家混淆:净流入和净买入,其实它的公式很简单:
给大家解释下,外资每天买卖咱们A股都是有额度限制的,目前沪股通和深股通的额度都是每个交易日 520亿,而且是只要挂单就会占用额度,无论是否成交。
所以,当日资金净流入会包含当日成交买入额和当日申报但未成交的买单金额,那么净流入金额一定会大于或等于净买入金额,这是两者最大的区别。
建议大家看北向资金的时候还是要看净买入金额,因为净流入金额只能代表北向资金的一个购买意愿,并不能代表真实交易。
常见的,一般 标准的 财经网站你应该会看到下面这种图:
但是很多时候你看到的都只是一个净流入的数据,并没有直接显示净买入的数据。
其实也不是说这个数据不对,只是说净买入数据会更准确些,更能反映当前北上资金的真实情况。这个数据最准确的就是在港交所了,而且是有盘中实时动态播报的,大家可以去了解一下。
说完基础知识,再来说今天的重点:基于北向资金的择时策略实现:
其实用一句话就可以解释所谓的择时选股策略:基于指标,对指数进行择时(即判断指数方向),如果方向向上,就进行选股。
下图是华泰证券研究所的一篇报告,其中提到了一个基于北上资金的择时策略。正如文章开头所说,北上资金目前作为大A的风向标,具有一定的可参考性。
而报告中通过北上资金与沪深300的相关系数,也完美阐释了这一点:
该择时策略的具体内容是这样的:
策略理解起来很容易,但是怎么实现呢?
以下源码拿去即用(除了tushare需要安装,不过我想这个不用我教了吧),算是开源分享给大家。
1. 导入相应的库
# 导入相应的库
import tushare as ts
import datetime
import pandas as pd
import numpy as np
2. 通过接口 token 链接到 tushare
# 将 token 替换成你自己的就行
token = '替换成你自己的token'
pro = ts.pro_api(token)
3. 获取交易日的日期列表
# 获取所有交易日数据
trade_date = pro.trade_cal(start_date='20180101',end_date=datetime.datetime.today().strftime('%Y%m%d'))
date_list = list(trade_date[trade_date.is_open==1]['cal_date'].values)
4. 获取北上资金的日粒度数据
因为单次请求限制为300条,所以这一步可以分多次获取
# 单次请求限制为300条,分两次获取
df_data1 = pro.moneyflow_hsgt(start_date=date_list[0:300][0], end_date=date_list[0:300][-1])
df_data2 = pro.moneyflow_hsgt(start_date=date_list[300:600][0], end_date=date_list[300:600][-1])
df_data3 = pro.moneyflow_hsgt(start_date=date_list[600:][0], end_date=date_list[600:][-1])
# 合并数据
df_data = df_data1.append([df_data2, df_data3], ignore_index=True)
df_data = df_data.sort_values('trade_date',ascending=True).reset_index(drop=True)
# 重命名
df_data = df_data.rename(columns={'ggt_ss':'港股通-上海', 'ggt_sz':'港股通-深圳', 'hgt':'沪股通', 'sgt':'深股通', 'north_money':'北向资金', 'south_money':'南向资金'})
资金对应的单位是 百万,为了方便查看,这里需要进行单位的转换
# 单位换算:百万->亿
df_data['港股通-上海'] = df_data['港股通-上海']*0.01
df_data['港股通-深圳'] = df_data['港股通-深圳']*0.01
df_data['沪股通'] = df_data['沪股通']*0.01
df_data['深股通'] = df_data['深股通']*0.01
df_data['北向资金'] = df_data['北向资金']*0.01
df_data['南向资金'] = df_data['南向资金']*0.01
这里有一点需要注意,因为有个别日期北上资金是无法进行交易的。例如:6月30日-7月1日香港特别行政区纪念日、重阳节、圣诞节等均会有休市情况发生。
所以,有必要剔除掉这些北上资金休市的情况。
观察数据你会发现,如果根据 北上资金=0 这个条件去筛选,那恰好某一天的买入卖出刚好相等,这种情况也会被过滤掉,这明显是不合理的。
解决方法:根据 df_data['沪股通'].isna() 字段是否为空进行判断,代码如下:
# 剔除北上暂停交易的交易日
df_data2 = df_data.loc[~df_data['沪股通'].isna(), :].reset_index(drop=True)
数据处理完毕之后,对应的数据应该是这个样子的:
5. 核心策略实现
再来回顾一下策略的内容:
对了,图中的 252 表示大A一年中的交易日,你没看错,就这么多
而 1.5 倍标准差则是研报中规定的,至于为什么选这个数而不是其他 1倍、2倍呢?
研报中也有解释原因:其中一共选取了10组不同的上下限标注差,并且分别进行了回测,最终 上限+1.5 下限-1.5 的年化收益率最高,达到了 37.54%
ok,既然人家已经都做过了充分的回测,那我们就直接实现拿来用就好
核心代码如下:
"""遍历每一个交易日,对北上进行分析"""
signal = '无信号'
for index, row in df_data2.iterrows():
if index<252:
continue
df_data_temp = df_data2.iloc[index-252:index]
# 计算近 252 天的平均数和标准差
average = df_data_temp['北向资金'].sum()/252
std = df_data_temp['北向资金'].std()
# 计算上下限
up_line = float(format(average + std * 1.5, '.4f'))
down_line = float(format(average - std * 1.5, '.4f'))
以上分别是计算出当天(从数据开始交易日的第252天起)以前 252 天的平均值、标准差、上限和下限
剩下的就是对结果进行判断和输出就ok,代码如下:
# 判断并输出
if row['北向资金'] >= up_line:
signal = '看多'
print('{0}:<{1}> 北上净买入:{2}亿元,看多线:{3}亿元, 看空线:{4}亿元'.format(row['trade_date'], signal, format(row['北向资金'], '.4f'), up_line, down_line))
elif row['北向资金'] <= down_line:
signal = '看空'
print('{0}:<{1}> 北上净买入:{2}亿元,看多线:{3}亿元, 看空线:{4}亿元'.format(row['trade_date'], signal, format(row['北向资金'], '.4f'), up_line, down_line))
if index == df_data2.shape[0]-1:
print('\n最新数据\n{0}: <{1}> \n北上净买入:{2}亿元,看多线:{3}亿元, 看空线:{4}亿元\n'.format(row['trade_date'], signal, format(row['北向资金'], '.4f'), up_line, down_line))
截取了输出结果的今年的部分,如下图:
可以看到,截止到目前(8月30日收盘),策略给出的观点是看空,并且是从 7月26日 起就一直看空了,直到今天也是看空阶段。
去券商网站上看一下北上资金的历史数据,图是这样的:
从7月26号之后的(图中的红箭头)该策略给出的观点就是看空,但是你仔细看一下研报给的策略条件是,人家说的是:该日北向资金流入规模,而我们通过 tushare 获取到的是:北向资金的净买入金额
因为每日的净流入较净买入大很多,所以对应的 1.5倍标准差就需要相应的改动一下。
比如说,当你改成 上限+0.8 下限-0.8,它对应的策略是这样的:
当然了,这里的 0.8 其实是我自己乱改的,并没有经过回测验证,在这只是为了说明对应的数据不同,参数也要有所调整。
感兴趣的话大家可以去回测一下净买入金额对应的不同参数的收益结果,选一个最大的结果对应的参数,然后就可以开启轻松的定投模式了。
其实,我是有回测出最优参数的,但是担心你们用我的参数到时候亏钱了输不起,目前还是不公开出来了。
建议自己多试试,投资本就没有不劳而获的东西!或许你的参数收益会比我更优呢!