克隆策略
In [5]:
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:31.617946 run trading v1.7.8_sp1 
2021-10-29 13:47:31.618585 init history datas... 
2021-10-29 13:47:31.624112 init trading env... 
2021-10-29 13:47:31.624304 run_backtest() capital:10000, frequency:tick, product_type:future, date:2021-10-08 00:00:00 ~ 2021-10-28 00:00:00 
2021-10-29 13:47:31.624475 run_backtest() running... 
2021-10-29 13:48:33.719521 run_backtest() finished! time cost 62.095s! 
  • 收益率-5.83%
  • 年化收益率-63.53%
  • 基准收益率-1.33%
  • 阿尔法-0.64
  • 贝塔0.07
  • 夏普比率-16.79
  • 胜率0.04
  • 盈亏比0.21
  • 收益波动率6.16%
  • 最大回撤5.83%
bigcharts-data-start/{"__type":"tabs","__id":"bigchart-f1b2de5a5fce449ea95f6cffd0decf65"}/bigcharts-data-end