最近要开始研究期货上的机器学习(Machine Learning)策略了。相信关注我的朋友已经多多少少知道一些有关机器学习的基础知识了,对于机器学习的基础知识我这边不会介绍,只会简单的提一下,不懂的朋友自行网上搜索资料学习。未来一段时间里,我将会利用所学的知识开发一个期货上的ML策略。
策略思路如下:交易时间内每分钟从主力合约(可以理解为交易活跃的股票)中选取未来几分钟内极大可能有较大的涨幅或跌幅的合约,对他们进行做多或做空。非常像我之前做的股票策略一样,挑一只未来很大可能上涨的股票对它进行买入操作(送你一份年化100%+的策略)。
那我需要如何实现这个策略呢。主要分为四个步骤:(1)获取期货主力合约数据;(2)构建机器学习模型,利用数据训练机器学习模型;(3)回测检验模型;(4)模拟盘检验模型。我大概会分4块部分写期货策略的文章,并且我会开源自己的期货ML策略代码,但是核心内容我不会分享,我只会分享我的策略框架。
今天这篇文章将会讲如何获取期货主力合约数据。对于一个ML策略来说,历史数据一定要获取下来存本地,这样才会方便我们的训练。可能有些策略并不需要将数据存本地,但是我们这个ML策略是必须的。
但是期货数据的获取里面有很多坑。首先,期货的数据不像股票,期货的合约经常是变化的,每个合约大概只有1年不到的数据,而大部分股票数据基本都是有好几年或是几十年。其次,不同合约的交易时间也不同,有些合约是有夜盘交易时间,有些则没有。总之,期货的数据获取还是有很多坑的。接下来我将告诉大家如何进行获取数据。
首先我们需要知道不同时间段哪些合约是主力合约。这里tushare提供了很好的接口获取。我们可以利用fut_basic接口获取有哪些合约类别,然后利用fut_mapping接口获取不同合约类别下不同时间内的主力合约名称。核心代码如下:
# 获取各个时段主力合约
df_list = []
for exchange in ['DCE', 'CZCE', 'SHFE', 'INE']:
df = pro.fut_basic(exchange=exchange, fut_type='2', fields='ts_code,symbol,name')
df['is_zhuli'] = df['name'].apply(myfun1)
df = df[df['is_zhuli'] == True]
df['exchange'] = exchange
df_list.append(df[['ts_code', 'symbol', 'name', 'exchange']])
df_list = pd.concat(df_list).reset_index(drop=True)
df_zhuli = []
for i in tqdm(range(len(df_list))):
code = df_list['ts_code'][i]
exchange = df_list['exchange'][i]
symbol = df_list['symbol'][i]
# 获取主力合约TF.CFX每日对应的月合约
df = pro.fut_mapping(ts_code=code)
df['trade_date'] = pd.to_datetime(df['trade_date'], infer_datetime_format=True)
# 筛选到制定时间段
df = df[(df['trade_date'].dt.date >= trade_date_min) & (df['trade_date'].dt.date <= trade_date_max)]
# 获得成为主力合约的时间段
df = df.groupby('mapping_ts_code')['trade_date'].agg({'max', 'min'}).reset_index()
df['exchange'] = exchange
df['symbol'] = symbol
# df.columns = ['code', 'date_max', 'date_min', 'exchange', 'symbol']
df = df.rename(columns={'mapping_ts_code': 'code', 'max': 'date_max', 'min': 'date_min'})
df_zhuli.append(df)
df_zhuli = pd.concat(df_zhuli).reset_index(drop=True)
主力合约获取完成之后我们就可以获取其对应主力合约日线信息(开盘价,收盘价等信息)。这个信息同样可以利用tushare获取。核心代码如下:
def get_day(df_zhuli):
for i in tqdm(range(len(df_zhuli))):
# if i < 299: continue
day1 = df_zhuli['date_max'][i]
et = str(day1.year) + str(day1.month).zfill(2) + str(day1.day).zfill(2)
day2 = df_zhuli['date_min'][i]
st = str(day2.year) + str(day2.month).zfill(2) + str(day2.day).zfill(2)
num = myfun2(df_zhuli['code'][i])
symbol = df_zhuli['symbol'][i]
exchange = df_zhuli['exchange'][i]
#
if exchange != 'CZCE':
code = exchange + '.' + symbol.lower() + num
save_path = os.path.join('data/day', code + ".csv")
else:
code = exchange + '.' + symbol + num[1:]
save_path = os.path.join('data/day', code + ".csv")
code = df_zhuli['code'][i]
df = pro.fut_daily(ts_code=code, start_date=st, end_date=et)
df.to_csv(save_path)
time.sleep(0.3)
日线行情获取完之后,我们就需要获取分时数据。分时数据,我们可以利用天勤的api获取。因为后续会进行一系列的匹配问题,所以我们需要确定比较精确的主力合约分时行情的起始时间和终止时间(起始时间是前一个交易日的21:00,终止时间是当个交易日的15:00)。这样的话当后面匹配出问题时也能知道哪块出问题了。具体,大家自己看代码吧,我就不多说了。这里需要注意一下,我在获取数据的时候发现CZCE.JR003这个合约数据获取不到,不知道为什么,不过问题不大。
def get_1min(df_zhuli, day2idx, idx2day):
# # 1分钟k线数据下载
for i in tqdm(range(len(df_zhuli))):
# if i >= 173:
# continue
# break
day1 = df_zhuli['date_max'][i]
et = datetime.datetime(day1.year, day1.month, day1.day, 16)
# 获取分时数据时,要从前一个交易日的21点开始
day2 = df_zhuli['date_min'][i]
st = day2.year*10000 + day2.month*100 + day2.day
idx = day2idx[st]
if idx == 0:
st = datetime.datetime(st // 10000, st % 10000 // 100, st % 10000 % 100, 8)
else:
st = idx2day[idx-1]
st = datetime.datetime(st//10000, st % 10000//100, st % 10000 % 100, 20)
num = myfun2(df_zhuli['code'][i])
symbol = df_zhuli['symbol'][i]
exchange = df_zhuli['exchange'][i]
#
if exchange != 'CZCE':
code = exchange + '.' + symbol.lower() + num
else:
code = exchange + '.' + symbol + num[1:]
if code == 'CZCE.JR003': continue # 这个文件有问题
# print(code, st, et)
save_path = os.path.join('data/1minute', code+".csv")
# if code not in ['CZCE.JR009', 'SHFE.cu1902', 'SHFE.wr2005', 'SHFE.wr2101']:
# continue
kd = DataDownloader(api, symbol_list=code, dur_sec=60,
start_dt=st, end_dt=et, csv_file_name=save_path)
try:
while not kd.is_finished():
api.wait_update()
# print("progress: kline: %.2f" % (kd.get_progress()))
kd.get_progress()
except Exception as e:
print(code)
print(e)
在股票投资的过程中,如果大盘行情好的话个股基本都好。期货应该也一样(我不太清楚),那如何获取一个期货对应的指数呢?有一些财经软件会有自己的一个指数,比如文华财经会有文华指数。这些不同软件的指数数据只能在各自软件上使用,且实盘或模拟盘的过程中不好取指数数据,因此我们需要自己合成一个指数行情。具体计算公式如下:
当日指数涨幅=SUM(主力合约涨幅*主力合约当日累计成交量)/SUM(主力合约当日累计成交量)
这个公式计算可能不太合理,大家自己有更好的想法可以用自己的指数计算公式,也可以添加更多的非主力合约一起计算。
核心代码如下:
def get_cumvol(x):
x = x.sort_values('datetime', ascending=True).reset_index(drop=True)
x['cumvol'] = x['volume'].cumsum()
return x[['datetime', 'cumvol']]
tmp_df = df_min.groupby(['trade_date', 'ts_code']).apply(get_cumvol).reset_index()
df_min = df_min.merge(tmp_df[['datetime', 'ts_code', 'cumvol']], on=['datetime', 'ts_code'], how='left')
def get_index(x):
index_rate = np.sum(x['rate'] * x['cumvol']) / np.sum(x['cumvol'])
return index_rate
index = df_min.groupby('datetime').apply(get_index).reset_index()
index.columns = ['datetime', 'rate']
以上就是数据获取的全部内容。
所有代码均已上传至github:
https://github.com/wbbhcb/futures_strategy/tree/master/ML_Strategy
对量化、数据挖掘、深度学习感兴趣的可以关注公众号,本人不定期分享有关这些方面的研究。
个人知乎:
https://www.zhihu.com/people/e-zhe-shi-wo/activities