QMT自带策略解析系列之网格交易(期货)回测示例
本期为本系列第1篇,网格交易(期货)回测示例,我们首先来看看示例策略的说明。
#在期货的1min线下运行'''回测模型示例(非实盘交易策略)本策略首先计算了过去300个价格数据的均值和标准差并根据均值加减标准差得到网格的区间分界线,并分别配以0.3和0.5的仓位权重然后根据价格所在的区间来配置仓位(+/-40为上下界,无实际意义):(-40,-3],(-3,-2],(-2,2],(2,3],(3,40](具体价格等于均值+数字倍标准差)[0.25, 0.15, 0.0, 0.15, 0.25](资金比例)'''
注意这里是回测模型示例,非实盘交易策略。
import numpy as np
import pandas as pd
import time
import datetime
# 上面是导入相关库,如果这里还有不清楚的可以学习下基础知识
def init(ContextInfo):
#设置图为标的
ContextInfo.tradefuture = ContextInfo.stockcode+"."+ContextInfo.market
# 将 ContextInfo 中的 stockcode 和 market 属性拼接在一起,
# 中间用一个点号 . 分隔,结果赋值给 ContextInfo.tradefuture。
ContextInfo.set_universe([ContextInfo.tradefuture])
# 这里首先说下set_universe这个函数
# 释义:设定股票池
# 用法:
# ContextInfo.set_universe(stocklist)
# 参数:
# list
# 返回:
# 无
# 示例:
# def init(ContextInfo):
# stocklist = ['000300.SH','000004.SZ']
# ContextInfo.set_universe(stocklist)
# 上面函数是在qmt界面函数库查询出来,大家可以按照此方法去研究
# 调用 ContextInfo 的 set_universe 方法,
# 并将包含 ContextInfo.tradefuture 的列表作为参数传递给该方法。
# 这通常用于设置交易环境中的目标标的。
print(ContextInfo.get_universe())
# 调用 ContextInfo 的 get_universe 方法,并打印返回的结果。
# 这里用于验证当前的目标标的设置是否正确。
ContextInfo.timeseries = pd.DataFrame()
# 创建一个空的 Pandas 数据框,并将其赋值给
# ContextInfo.timeseries。用于存储时间序列数据。
ContextInfo.band = np.zeros(5)
# 创建一个包含 5 个元素的 NumPy 零数组,并将其赋值给 ContextInfo.band。
# 设置网格的仓位
ContextInfo.weight = [0.25, 0.15, 0.0, 0.15, 0.25]
# 这行代码为网格交易策略设置了仓位权重。ContextInfo.weight 是一个列表,
# 包含五个浮点数,分别为 0.25, 0.15, 0.0, 0.15, 0.25,
# 表示每个网格区间的仓位比例。
# 这些比例通常用于确定每个区间在交易策略中的资金分配。
# 获取多仓仓位
ContextInfo.position_long = 0
# 这行代码初始化了多头仓位,ContextInfo.position_long
# 被设置为 0,表示初始状态下没有多头仓位。
# 获取空仓仓位
ContextInfo.position_short = 0
# 这行代码初始化了空头仓位,ContextInfo.position_short
# 被设置为 0,表示初始状态下没有空头仓位。
#剩余资金
ContextInfo.surpluscapital = ContextInfo.capital
# 这行代码将 ContextInfo.capital 的值赋给
# ContextInfo.surpluscapital,表示当前剩余的可用资金与初始资金相同。这通常是在交易开始前设置的。
#保证金比率
comdict = ContextInfo.get_commission()
ContextInfo.marginratio = comdict['margin_ratio']
# 这两行代码用于获取保证金比率。
# 首先,调用 ContextInfo.get_commission() 方法,
# 返回一个包含佣金和保证金比率等信息的字典 comdict,
# 然后从字典中提取 margin_ratio 键对应的值,
# 并将其赋值给 ContextInfo.marginratio。
#合约乘数
ContextInfo.multiplier = ContextInfo.get_contract_multiplier(ContextInfo.tradefuture)
# 这行代码获取了合约乘数。
# 调用 ContextInfo.get_contract_multiplier() 方法,
# 并传入 ContextInfo.tradefuture 作为参数,返回该合约的乘数值,
# 并将其赋值给 ContextInfo.multiplier。
# 合约乘数通常用于计算期货合约的实际交易价值。
#账号
ContextInfo.accountid='testF'
# 这行代码为 ContextInfo 设置了一个账户 ID,赋值为 'testF'。
# 按照实际情况是可以修改成自己的。
ContextInfo.now_timestamp = time.time()
# 这行代码获取当前的时间戳,并将其赋值给 ContextInfo.now_timestamp。
# time.time() 返回的是当前时间的秒数,
# 从1970年1月1日 00:00:00 UTC以来的秒数,
# 用于记录操作的时间或用于时间相关的计算。
def handlebar(ContextInfo):
index = ContextInfo.barpos
# 将 ContextInfo.barpos 的值赋给 index。barpos 通常表示当前K线的位置索引。
realtimetag = ContextInfo.get_bar_timetag(index)
# 调用 ContextInfo.get_bar_timetag(index) 获取当前K线的时间戳,并将其赋值给 realtimetag。这个时间戳表示当前K线的时间。
lasttimetag = ContextInfo.get_bar_timetag(index - 1)
# 获取上一根K线的时间戳,并将其赋值给 lasttimetag。通过索引 index - 1 来访问上一根K线。
print(timetag_to_datetime(realtimetag, '%Y-%m-%d %H:%M:%S'))
# 将当前K线的时间戳转换为我们习惯的日期时间格式,并打印出来。timetag_to_datetime 函数将时间戳转换为指定格式的字符串,这里使用了格式 '%Y-%m-%d %H:%M:%S'。
if ContextInfo.period in ['1m','3m','5m','15m','30m'] and not ContextInfo.do_back_test:
# 判断当前的交易周期 ContextInfo.period 是否在给定的列表 ['1m','3m','5m','15m','30m'] 中,并且 ContextInfo.do_back_test 不为真(即不在进行回测)。如果两个条件都满足,则进入下一步的判断。
if (datetime.datetime.fromtimestamp(ContextInfo.now_timestamp) - datetime.datetime.fromtimestamp(realtimetag / 1000)).days > 7:
return
# 进一步检查当前时间与K线时间的差异。如果差异超过7天,则函数返回 None,即不执行后续操作。realtimetag / 1000 是将毫秒级时间戳转换为秒级时间戳,以便与 now_timestamp 进行比较。
starttime = timetag_to_datetime(realtimetag-86400000 * 10, '%Y%m%d%H%M%S')
# 计算开始时间。这里从当前时间戳 realtimetag 减去 86400000 * 10 毫秒(即10天),然后将结果转换为指定格式的日期时间字符串,并赋值给 starttime。
endtime = timetag_to_datetime(realtimetag-86400000, '%Y%m%d%H%M%S')
# 计算结束时间。这里从当前时间戳 realtimetag 减去 86400000 毫秒(即1天),然后将结果转换为指定格式的日期时间字符串,并赋值给 endtime。
Result=ContextInfo.get_market_data(['close'],stock_code=[ContextInfo.tradefuture],start_time=starttime,end_time=endtime,skip_paused=False,period=ContextInfo.period,dividend_type='front')
# 调用 ContextInfo.get_market_data() 获取市场数据。
# 请求的参数包括数据类型(这里是 ['close'] 表示收盘价)、
# 标的代码(ContextInfo.tradefuture)、开始时间 starttime、
# 结束时间 endtime、是否跳过停牌数据(skip_paused=False)、
# 周期 ContextInfo.period 和分红类型(dividend_type='front')。
# 返回的数据保存在 Result 变量中。
close_sort = Result['close'].sort_index(axis = 0,ascending = True)
# 对获取的收盘价数据 Result['close'] 按照索引进行升序排序,
# 并将排序后的结果赋值给 close_sort。sort_index(axis=0, ascending=True)
# 表示按照行(即时间顺序)进行排序,升序排列。
#过去300个价格数据的均值和标准差
Result_mean = close_sort.tail(300).mean()
# 计算 close_sort 中最后300个数据(即最近300个时间点的收盘价数据)的
# 均值,并将其赋值给 Result_mean。tail(300) 方法用于获取最后300个数据点,
# 而 mean() 方法则用于计算这些数据点的平均值。
Result_std = close_sort.tail(300).std()
# 计算 close_sort 中最后300个数据的标准差,
# 并将其赋值给 Result_std。std() 方法用于计算标准差,
# 它表示这些价格数据的波动程度。
ContextInfo.band = Result_mean + np.array([-40, -3, -2, 2, 3, 40]) * Result_std
# 这行代码用于计算并设置交易策略中的价格区间(或称为“网格”)。
# 首先,创建一个包含 [-40, -3, -2, 2, 3, 40] 的数组,
# 然后将这个数组与 Result_std 相乘,得到一个新的数组,
# 其中每个元素都表示一个标准差的倍数。
# 接着,将这些标准差的倍数加到 Result_mean 上,
# 形成一组带有不同偏差的价格带。
# 最终结果赋值给 ContextInfo.band,
# 用于定义不同价格区间的上下限,以便在网格交易策略中使用。
#print 'ContextInfo.band',ContextInfo.band
if np.isnan(ContextInfo.band).any() or Result_std==0:
return
if index > 0:
lasttimetag = ContextInfo.get_bar_timetag(index - 1)
#前一根bar收盘价
close_lastbar = ContextInfo.get_market_data (['close'],stock_code=[ContextInfo.tradefuture],period=ContextInfo.period,dividend_type='front')
#当前开盘价
open_currentbar = ContextInfo.get_market_data (['open'],stock_code=[ContextInfo.tradefuture],period=ContextInfo.period,dividend_type='front')
#划分网格
#print close_lastbar,ContextInfo.band
grid = pd.cut([close_lastbar], ContextInfo.band, labels=[0, 1, 2, 3, 4])[0]
#print 'grid ',grid
if not ContextInfo.do_back_test:
ContextInfo.paint('grid',float(grid),-1,0)
# 若无仓位且价格突破则按照设置好的区间开仓
if ContextInfo.position_long == 0 and ContextInfo.position_short == 0 and grid != 2:
# 大于3为在中间网格的上方,做多
if grid >= 3 and ContextInfo.surpluscapital > 0 :
long_num = int(ContextInfo.weight[grid]*ContextInfo.surpluscapital/(ContextInfo.marginratio*close_lastbar*ContextInfo.multiplier))
ContextInfo.position_long = long_num
buy_open(ContextInfo.tradefuture,long_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital -= long_num * ContextInfo.marginratio * close_lastbar * ContextInfo.multiplier
#print '开多'
elif grid <= 1 and ContextInfo.surpluscapital > 0 :
short_num = int(ContextInfo.weight[grid]*ContextInfo.surpluscapital/(ContextInfo.marginratio*close_lastbar*ContextInfo.multiplier))
ContextInfo.position_short = short_num
sell_open(ContextInfo.tradefuture,short_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital -= short_num * ContextInfo.marginratio * close_lastbar * ContextInfo.multiplier
#print '开空'
# 持有多仓的处理
elif ContextInfo.position_long > 0 :
if grid >= 3 and ContextInfo.surpluscapital > 0 :
targetlong_num = int(ContextInfo.weight[grid] * (ContextInfo.surpluscapital + ContextInfo.multiplier * close_lastbar * ContextInfo.position_long*ContextInfo.marginratio)/ (ContextInfo.marginratio*close_lastbar * ContextInfo.multiplier))
if targetlong_num > ContextInfo.position_long :
trade_num = targetlong_num - ContextInfo.position_long
ContextInfo.position_long = targetlong_num
buy_open(ContextInfo.tradefuture,trade_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital -= trade_num * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
elif targetlong_num < ContextInfo.position_long:
trade_num = ContextInfo.position_long - targetlong_num
ContextInfo.position_long = targetlong_num
sell_close_tdayfirst(ContextInfo.tradefuture,trade_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital += trade_num * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
#print '调多仓到仓位'
# 等于2为在中间网格,平仓
elif grid == 2:
sell_close_tdayfirst(ContextInfo.tradefuture,ContextInfo.position_long,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital += ContextInfo.position_long * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
ContextInfo.position_long = 0
#print '平多'
# 小于1为在中间网格的下方,做空
elif grid <= 1:
sell_close_tdayfirst(ContextInfo.tradefuture,ContextInfo.position_long,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital += ContextInfo.position_long * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
ContextInfo.position_long = 0
#print '全平多仓'
if ContextInfo.surpluscapital > 0 :
short_num = int(ContextInfo.weight[grid]*ContextInfo.surpluscapital/(ContextInfo.multiplier * ContextInfo.marginratio * close_lastbar))
ContextInfo.position_short = short_num
sell_open(ContextInfo.tradefuture,short_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital -= short_num * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
#print '开空仓到仓位'
# 持有空仓的处理
elif ContextInfo.position_short> 0 :
# 小于1为在中间网格的下方,做空
if grid <= 1:
targetlshort_num = int(ContextInfo.weight[grid]*(ContextInfo.surpluscapital + ContextInfo.multiplier*close_lastbar*ContextInfo.position_short*ContextInfo.marginratio)/(ContextInfo.multiplier * ContextInfo.marginratio * close_lastbar))
if targetlshort_num > ContextInfo.position_short:
trade_num = targetlshort_num - ContextInfo.position_short
ContextInfo.position_short = targetlshort_num
sell_open(ContextInfo.tradefuture,trade_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital -= trade_num * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
#print '开空仓到仓位' ,targetlshort_num
elif targetlshort_num < ContextInfo.position_short:
trade_num = ContextInfo.position_short - targetlshort_num
ContextInfo.position_short = targetlshort_num
buy_close_tdayfirst(ContextInfo.tradefuture,trade_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital += trade_num * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
#print '平空仓到仓位' ,targetlshort_num
# 等于2为在中间网格,平仓
elif grid == 2:
buy_close_tdayfirst(ContextInfo.tradefuture,ContextInfo.position_short,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital += ContextInfo.position_short * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
ContextInfo.position_short = 0
#print '全平空仓'
# 大于3为在中间网格的上方,做多
elif grid >= 3:
buy_close_tdayfirst(ContextInfo.tradefuture,ContextInfo.position_short,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital += ContextInfo.position_short * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
ContextInfo.position_short = 0
#print '全平空仓'
if ContextInfo.surpluscapital > 0 :
trade_num = int(ContextInfo.weight[grid]*ContextInfo.surpluscapital / (ContextInfo.marginratio * close_lastbar * ContextInfo.multiplier))
ContextInfo.position_long = trade_num
buy_open(ContextInfo.tradefuture,trade_num,'fix',close_lastbar,ContextInfo,ContextInfo.accountid)
ContextInfo.surpluscapital -= trade_num * close_lastbar * ContextInfo.marginratio * ContextInfo.multiplier
#print ' 开多仓到仓位'
# 获取多仓仓位
#print 'ContextInfo.position_long',ContextInfo.position_long
# 获取空仓仓位
#print 'ContextInfo.position_short',ContextInfo.position_short
# 获取剩余资金
#print 'ContextInfo.surpluscapital',ContextInfo.surpluscapital
这一篇就先解释这些代码,后面再给大家做剩余部分解释。