from datetime import datetime,timedelta
STRATEGY_NAME = "STRATEGY_tick"
# 根据tick数据计算分钟k线的OCHL
def calc_OCHL(ticks):
#1分钟k线的开盘价
tmp_open = 0.5 * (ticks[0].ask_price1 + ticks[0].bid_price1)
#1分钟k线的收盘盘价
tmp_close = 0.5 * (ticks[-1].ask_price1 + ticks[-1].bid_price1)
tmp_high = tmp_open
tmp_low = tmp_open
tmp_volume = ticks[-1].volume - ticks[0].volume
#获取1分钟k线的最高价和最低价
for tick in ticks[1:]:
mid_price = 0.5 * (tick.ask_price1 + tick.bid_price1)
if tmp_high < mid_price:
tmp_high = mid_price
if tmp_low > mid_price:
tmp_low = mid_price
return {'open' : tmp_open, 'close' : tmp_close, 'high' : tmp_high,\
'low' : tmp_low, 'volume' : tmp_volume, 'time_int' : ticks[-1].time_int}
def initialize(context):
"""初始化函数"""
context.symbol = context.get_conf_param("instruments") #处理的
context.order_num = context.get_conf_param("order_num") # 下单手数
context.ma_length = 20 #20分钟均线
context.closetime_day = context.get_conf_param("closetime_day")# 日内策略白盘平仓时间,一般14:58
context.closetime_night = context.get_conf_param("closetime_night")#日内策略夜盘平仓时间,一般22:58,注意有些商品夜盘收盘时间不一样
def before_trading(context, tick):
"""盘前处理函数"""
context.tick_num_count = 0 # tick数量记录
context.trade_count = 0 # 记录每日交易次数
context.subscribe(context.symbol) #注册合约
context.tick_num = 60 * 2 # 期货1分钟 60秒 每妙2tick
context.bar_num = context.ma_length # 均线长度
context.tick_series = NumPyDeque(context.tick_num, dtype='object') # 最近120个tick组成的队列
context.bar_series = NumPyDeque(context.bar_num, dtype='object') # 20个分钟 K线 (包括OHLC)
context.bar_open_series = NumPyDeque(context.bar_num, dtype='int') # 20个分钟 K线 开盘价
context.bar_high_series = NumPyDeque(context.bar_num, dtype='int') # 20个分钟 K线 最高价
context.bar_low_series = NumPyDeque(context.bar_num, dtype='int') # 20个分钟 K线 最低价
context.bar_close_series = NumPyDeque(context.bar_num, dtype='int') # 20个分钟 K线 收盘价
def handle_tick(context, tick):
"""主函数"""
context.tick_series.append(tick)
context.tick_num_count += 1 # 每遍历一个tick数据,计数+1
if context.tick_num_count % context.tick_num == 0: # 每过一分钟 就加一根bar
OCHL = calc_OCHL(context.tick_series) # 分钟K线
context.bar_series.append(OCHL) # 分钟K线的队列
context.bar_open_series.append(OCHL['open'])
context.bar_low_series.append(OCHL['low'])
context.bar_high_series.append(OCHL['high'])
context.bar_close_series.append(OCHL['close'])
# 不足20根k线时,直接返回,不用往下运行
if len(context.bar_series) < context.bar_num:
return
elif len(context.bar_series) == context.bar_num:
mean_close = context.bar_close_series.data().mean() # 分钟K线均值
position_long = context.get_position(tick.symbol, Direction.LONG) # 多头持仓
position_short = context.get_position(tick.symbol, Direction.SHORT) # 空头持仓
price = tick.last_price # 最新价
cur_hm = tick.datetime.strftime('%H:%M') # 当前时间
# 部分品种夜盘收盘时间不一样,此时间表示指定的尾盘平仓时间往后偏移30分钟,这段时间内不能开新仓,只能平仓。给30分钟是为了足够的冗余
closetime_nightshift = (datetime.strptime(context.closetime_night,'%H:%M') + timedelta(minutes = 30)).strftime('%H:%M')
# 尾盘平仓
if((cur_hm>=context.closetime_day and cur_hm<="15:00") or (cur_hm>=context.closetime_night and cur_hm<=closetime_nightshift)):
if (position_long.current_qty != 0):
rv = context.sell_close(tick.symbol, position_long.avail_qty, price, order_type=OrderType.MARKET)
msg = "{} 尾盘平多 for {} 最新价={} 下单函数返回{}".format(tick.time_int,tick.symbol,str(price),context.get_error_msg(rv))
context.write_log(msg, stdout=0) #输出关键日志
if (position_short.current_qty != 0):
rv = context.buy_close(tick.symbol, position_short.avail_qty, price, order_type=OrderType.MARKET)
msg = "{} 尾盘平空 for {} 最新价={} 下单函数返回{}".format(tick.time_int,tick.symbol,str(price),context.get_error_msg(rv))
context.write_log(msg, stdout=0) #输出关键日志
# 建仓逻辑
if price > mean_close:
#有空单先平
if position_short.current_qty != 0:
rv = context.buy_close(tick.symbol, position_short.avail_qty, price, order_type=OrderType.MARKET)
msg = "{} 平空 for {} 最新价={} 下单函数返回{}".format(tick.time_int,tick.symbol,str(price),context.get_error_msg(rv))
context.write_log(msg, stdout=0) #输出关键日志
#满足条件开多单
if position_long.current_qty == 0 and context.trade_count <=1 and cur_hm<="14:30":
rv = context.buy_open(tick.symbol, context.order_num, price, order_type=OrderType.MARKET)
msg = "{} 开多 for {} 最新价={} 下单函数返回{}".format(tick.time_int,tick.symbol,str(price),context.get_error_msg(rv))
context.trade_count += 1 # 开仓次数判断需要
context.write_log(msg, stdout=0) #输出关键日志
elif price < mean_close:
#有多单先平
if position_long.current_qty != 0:
rv = context.sell_close(tick.symbol, position_long.avail_qty, price, order_type=OrderType.MARKET)
msg = "{} 平多 for {} 最新价={} 下单函数返回{}".format(tick.time_int,tick.symbol,str(price),context.get_error_msg(rv))
context.write_log(msg, stdout=0) #输出关键日志
#满足条件开空单
if position_short.current_qty == 0 and context.trade_count <=1 and cur_hm<="14:30":
rv = context.sell_open(tick.symbol, context.order_num, price, order_type=OrderType.MARKET)
msg = "{} 开空 for {} 最新价={} 下单函数返回{}".format(tick.time_int,tick.symbol,str(price),context.get_error_msg(rv))
context.trade_count += 1 # 开仓次数判断需要
context.write_log(msg, stdout=0) #输出关键日志
def handle_order(context, order):
"""委托回报推送"""
msg = "handle_order data:{}".format(order.log_str())
context.write_log(msg, stdout=0)
def handle_trade(context, trade):
"""成交回报推送"""
msg = "handle_trade data:{}".format(trade.log_str())
context.write_log(msg, stdout=0)
# 分别获取最新的多头持仓和空头持仓
position_long = context.get_position(trade.symbol, Direction.LONG)
position_short = context.get_position(trade.symbol, Direction.SHORT)
msg = "当前多头持仓:{} 当前空头持仓:{}".format(str(position_long),str(position_short))
context.write_log(msg, stdout=0)
instruments = ['JD2201.DCE']
strategy_setting = [
{
"instruments": instruments,
"order_num": 1,
"closetime_day": "14:58",
"closetime_night": "22:58"
}]
start_date = "2021-10-08"
end_date = "2021-10-28"
md = M.hfbacktest.v1(start_date=start_date, # 开始时间
end_date=end_date, # 结束时间
instruments=instruments, # 回测标的
capital_base=10000, # 初始资金
product_type=Product.FUTURE, # 回测标的所属市场
frequency = Frequency.TICK, # 回测数据级别
initialize=initialize, # 初始化函数
before_trading_start=before_trading, # 盘前处理函数
handle_tick=handle_tick, # 主函数
handle_order=handle_order,
handle_trade=handle_trade,
plot_charts=True, # 是否绘制回测图
volume_limit=1.0, # 成交比率限制
disable_cache=0,
show_debug_info=1, # 是否显示完善日志
strategy_setting=strategy_setting, # 策略外部参数配置字典
slippage_type=SlippageType.FIXED,# 滑点固定模式
slippage_value=1.0, # 买卖双向各1个滑点
m_deps=np.random.rand()) # 模块依赖,值相同的话会命中缓存
[2021-10-29 13:47:29.324111] INFO: moduleinvoker: hfbacktest.v1 开始运行..
[2021-10-29 13:47:29.329490] INFO: hfbacktest: passed-in daily_data_ds:None
[2021-10-29 13:47:29.330661] INFO: hfbacktest: passed-in minute_data_ds:None
[2021-10-29 13:47:29.331684] INFO: hfbacktest: passed-in tick_data_ds:None
[2021-10-29 13:47:29.332695] INFO: hfbacktest: passed-in each_data_ds:None
[2021-10-29 13:47:29.333763] INFO: hfbacktest: passed-in dominant_data_ds:None
[2021-10-29 13:47:29.334762] INFO: hfbacktest: passed-in benchmark_data_ds:None
[2021-10-29 13:47:29.335748] INFO: hfbacktest: passed-in trading_calendar_ds:None
[2021-10-29 13:47:29.336689] INFO: hfbacktest: biglearning V1.2.5
[2021-10-29 13:47:29.337722] INFO: hfbacktest: bigtrader v1.7.8_sp1
[2021-10-29 13:47:29.360135] INFO: hfbacktest: strategy callbacks:{'on_init': , 'on_start': , 'handle_tick': , 'handle_trade': , 'handle_order': }
[2021-10-29 13:47:29.382328] INFO: hfbacktest: begin reading history data, 2021-10-08 00:00:00~2021-10-28, disable_cache:0
[2021-10-29 13:47:29.385115] INFO: hfbacktest: reading benchmark data 2021-09-01 00:00:00~2021-10-28...
[2021-10-29 13:47:29.398094] INFO: moduleinvoker: cached.v2 开始运行..
[2021-10-29 13:47:29.549856] INFO: moduleinvoker: cached.v2 运行完成[0.151781s].
[2021-10-29 13:47:29.598571] INFO: hfbacktest: reading daily data 20200403~2021-10-28...
[2021-10-29 13:47:29.615156] INFO: moduleinvoker: cached.v2 开始运行..
[2021-10-29 13:47:29.847104] INFO: moduleinvoker: cached.v2 运行完成[0.231954s].
[2021-10-29 13:47:29.895124] INFO: hfbacktest: reading tick data 2021-09-30 20:55:00~2021-10-28...
[2021-10-29 13:47:29.914976] INFO: moduleinvoker: cached.v2 开始运行..
[2021-10-29 13:47:31.251787] INFO: moduleinvoker: cached.v2 运行完成[1.336821s].
[2021-10-29 13:47:31.578914] INFO: hfbacktest: cached_benchmark_ds:DataSource(78262d7ec5504711bd74f077809515a8T)
[2021-10-29 13:47:31.581866] INFO: hfbacktest: cached_daily_ds:DataSource(0471ddbe11614fd6817fcf4bf7961c5fT)
[2021-10-29 13:47:31.595903] INFO: hfbacktest: cached_minute_ds:None
[2021-10-29 13:47:31.598040] INFO: hfbacktest: cached_tick_ds:DataSource(396b00718eaa4089870b3e689cc346b7T)
[2021-10-29 13:47:31.599828] INFO: hfbacktest: cached_each_ds:None
[2021-10-29 13:47:31.600807] INFO: hfbacktest: dominant_data_ds:None
[2021-10-29 13:47:31.601769] INFO: hfbacktest: read history data done, call run_backtest()
[2021-10-29 13:48:34.800275] INFO: hfbacktest: backtest done, raw_perf_ds:DataSource(28258fdce7ac4b80afb99d689a2cd05cT)
[2021-10-29 13:48:35.345423] INFO: moduleinvoker: hfbacktest.v1 运行完成[66.021316s].