历史文档

BigTrader使用文档 (副本)

由yvan0617创建,最终由yvan0617 被浏览 251 用户

交易引擎介绍

BigTrader是宽邦科技推出的分析为用户提供了简单、功能性的测测和创业交易策略、回分析、模拟实盘交易的工具。

支持的同学

股票、基金、期货,可转债,未来会支持债券、债券、两融

交易频率

日线、分钟、tick、逐笔

策略写

策略程序架构

姓名 说明
初始化 策略初始化函数,只触发一次。可以在该函数中初始化一些变量,如读取配置等
交易前 策略盘前交易函数,每日盘前触发一次。可以在该函数中一些启动前的准备,如订阅行情等
车把 当根bar行情通知函数,每根bar时间周期分钟会触发,包括日线和。每次注册某个时间,魔法调用一次handlebar。handlebar和handle_data不能同时使用。
句柄数据 行通知函数,支持和分钟。注册一些合同时,handle_data 会等待所有合同数据到触发后的时间。例如合同套,同时需要处理合同handle_data。handle_data 和handlebar 同时使用。
handle_tick 滴答拍行通知函数,标的情况有变化时的触发。
handle_l2trade level2逐笔处理业务行情更新时的
handle_order 每次委托回复通知,订单状态有变化时会触发。
handle_trade 成交反馈通知,有事时会触发。

主要函数说明

有两个使用频率最高的函数:初始化和handle_data(handle_bar),理解两个函数开发策略就不会难了,结合下面K线图来理解两个函数。

既可以对应产生一个,实际上一个共有26个事件,也就是26个根K线,的第一个,又一个,土,只有一个。,从第一根K线到最终可以理解K线初始化。运行handle_data一次,因此很多策略逻辑部分就可以选择handle_data里。

一些通用说明

  • 框架结构与我们线上现有的滑索策略框架类似(我们可能会说明该框架与滑索框架的一些主要差异)
  • 该框架对标的,均是代码。后缀的形式,如00001.SZA, 600000.SHA, RB2110.SHF,注意这里期货代码时,统一为大写产品代码+4位月+后缀,而现在后缀主要有: DCE,CZC,SHF,INE,CFX。其中,郑商所的实际交易代码为SR109,但在该平台中为SR1109或SR2109,即补全为4位月的数字。
  • 回时,平台内部会自动读取历史行情进行回测,当然也支持外部自定义历史数据进行回测。

策略典型代码结构

下图给出了一个典型策略需要的代码结构

#给每个策略取一个名字
STRATEGY_NAME = "STRATEGY_1"

def initialize(context):
    """策略初始化函数,只触发一次。可以在该函数中初始化一些变量,如读取配置和全局使用数据"""
    #输出关键日志
    msg = "initialize:" 
    context.write_log(msg, stdout=1)
        
def before_trading(context, data):
    """盘前处理,策略盘前交易函数,每日盘前触发一次。可以在该函数中一些启动前的准备,如订阅行情等"""
    #输出关键日志
    msg = "before_trading dt:{}".format(data.current_dt)
    context.write_log(msg, stdout=1)
    
    #如果需要处理tick数据,需要添加tick处理函数:handle_tick(context, tick):
    #如果需要使用handle_data,需要添加处理函数:handle_data(context, data):
def handle_bar(context, bar):
    """Bars行情通知函数,每个bar时间周期会触发,包括日线和分钟"""
    #输出关键日志
    msg = "handle_bar dt:{}".format(bar.datetime)
    context.write_log(msg, stdout=1)

def handle_order(context, order):
    """委托回报通知函数,每个订单状态有变化时会触发"""
    #输出关键日志
    msg = "handle_order data:{}".format(order.log_str())
    context.write_log(msg, stdout=1) 

def handle_trade(context, trade):
    """成交回报通知函数,有成交时会触发"""
    #输出关键日志
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 

策略回测

回测代码的写和运行

策略逻辑写完成后通过函数M.hfbacktest.v1(也是一个可视化模块的入口)来测,如下是此函数的详细说明

M.hfbacktest.v1(
    start_date,    #回测开始日期
    end_date,  #回测结束日期
    instruments=None,  #回测股票/基金/期货列表
    initialize=None,   #初始化函数初始化函数,initialize(context)
    on_stop=None,  #策略运行结束处理函数,on_stop(context)
    before_trading_start=None,     #在每个交易日开始前的处理函数,before_trading_start(context, data)
    handle_bar=None,  #每个bar更新时的处理函数, handle_bar(context, bar),不能和handle_data同时注册
    handle_data=None,  #数据更新时的处理函数, handle_data(context, data),不能和handle_bar同时注册
    handle_tick=None,  #在每个Tick快照行情更新时的处理函数,handle_tick(context, tick)
    handle_l2trade=None, #在每个逐笔成交行情更新时的处理函数,handle_l2trade(context, l2trade)
    handle_trade=None, #在成交回报更新时的处理函数,handle_trade(context, trade)
    handle_order=None, #在委托回报更新时的处理函数,handle_order(context, order)
    capital_base=1000000,   #初始资金,默认为 1000000
    slippage_type=SlippageType.FIXED,   #指定滑点模式
    slippage_value=0, #指定买卖双向的滑点
    volume_limit=0.25, #执行下单时控制成交量参数,若设置为0时,不进行成交量检查;默认值是0.25,下单量如果超过该K线成交量的2.5%,多余的订单量会自动取消
    product_type=None, #回测产品类型,如 stock/future/option等,一般不用指定,系统自动根据合约代码判断产品类型
    price_type=None,   #回测复权类型,如 真实价格[real],后复权[post]
    frequency='1m',    #回测频率,如 1d/1m/tick/
    benchmark='000300.HIX',    #benchmark:回测基准数据, 可以是DataSource,DataFrame,或者股票/指数代码如000300.
    plot_charts=True,  #是否画回测评估图
    options=None,    #其他参数从这里传入,可以在 handle_data 等函数里使用
    disable_cache=0,  #默认0,表示不启用数据缓存。设置为1,如果本次回测的数据范围之前已经读取过则不会重复读取,这样能加快回测速度
    show_debug_info=False  #默认False,表示是否打印回测框架的debug信息
)

在BigQuant平台上,新建一个空白的代码策略模板,把写好的策略逻辑代码和回代码拷贝进去(也可以建好模板后,直接在模板的笔记本里面写代码)。 点击“运行全部”按钮就可以进行回测了。

订单撮合处理

本节介绍主要基于历史行情的模拟撮合介绍,主要包括酒吧行情、撮行情、逐笔行情合。上交深交所基金、同类上市和A股、期货期货、期货等动物不支持新、购配售股票申购更多等0.03%,计算则压缩0.13%(默认包含0.1%的印花税),期货对应费率计算,实际费率也可在回测模拟中设置。

一些通用规则

  • 对于没有成交的委托,部分成交的委托,可以或者单。当天的委托如果没有成交,收市以后自动作废,不参加下一次交易日撮合。
  • 对于市价委托,未成交部分会自动撤销(即退出退出)。

一、基于酒吧行情数据撮合规则

  • 参考价格可指定为开/关
  • 成交量最大当次吧的成交量,日线是一般案例加为一个成交率比例
  • 委托书过大时,可能出现模拟撮合结果与真实情况真实量的真实情况。

二、基于行情数据的撮合规则

主要原则是基于行情中的撮合,而不是交易口的价格撮合,交付订单最新价后,使用下一笔勾画行情撮合。

买入:

  • 如果最新成交委托价,则按照委托价成交
  • 如果最新成交价委托价,则按照最新成交价成交
  • 注意,若成交价在买一或停涨时,不能即时成交,请会现场撮合,即时记录买一量,如果成交量现场量大买一量,可成交量是成交量和当时的情况的买量一的差,以一种方式模拟量真实交易的事件。但未及时考虑购买一上的单量。
  • 如果涨停板被打开,广告价格委托价,则按照现价成交。

展览:

  • 如果最新成交委托价,则按照委托价成交

  • 如果最新的协商成交价,按照最新的成交价,如果成交价在卖一价或停工,联系,委托会协商结合,记​​录当时的成交量,如果现场成交量大卖一量,可模拟成交量是一期成交量和当时的量,但未估计量一上的成交量。

  • 停盘被,优惠价打开,则立即出现成交量。当次成交量两个图片的真实成交量计算,如果成交量有0个,不成交。如果成交量则删除委托未成交量,因此部分撮合,数量,只有合委托的存货在撮合,等待新的成交明细,早上集合竞标期间的报会在09:25:00进行撮合,或09:30:00开始进行连续竞价撮合。

    例如:600804(鹏博士)上午开市后涨,用户在10:10以涨停价委托买入100手,此时的成交量是51000手,停涨板买一单子5000手,如果停不下来打开,只有阶段统计量大于5000手时,用户的委托才可以联系到客户。如果成交量达到56010手,则用户投诉10手(5601 0-51000-5000),量的部分可以查询到更多。

三、基于笔逐笔行情撮合的基本原则是逐笔行情中的最新成交价撮合,交付订单后,利用下一笔逐笔行情撮合,需要配合实时报道口信息(在会委托)时,获取当前盘口)。

买入委托:

  • 市委托价:
  1. 当前在涨停版上,不成交,自动撤销
  2. 说,成供给,量也可以从卖一到卖五档
  3. 不支持FOK
  • 委托价格 > 最新价:

成交价,成交价=最新价,成交量从十档卖到一到,生成多笔记录信息,FME:如果卖掉十档的涨价,价格触发到停板的。

  • 委托价格==最新价:
  1. 个人主动提出,当前记录的成交量,判断数量<=0,则成交,成交当次追笔成交量,FIXME:更模拟模式是取追笔交易-数量数量
  2. 个人主动买,不成交
  • 撤单成交(仅适用于深交所)
  1. 撤单价格==对在线购买中买的购买委托的广告数量做

  2. 其他撤单,不做处理

  3. 其它不成交

    \

委托委托

  • 市委托价:
  1. 当前在跌停版上,不成交,自动撤销
  2. 聊天,从成口,喂量也可以买一到买五档
  3. 不支持FOK
  • 委托价格 < 最新价:

成交,成交价=最新价,成交量从买一到买十档,生成多笔成交信息,修正:如果超出买十档的,价格触发到跌停停板的。

  • 委托价格==最新价:
  1. 个人主动购买,判断当前记录的活动,数量<=0,则调查,举报当次追笔成交量,FIXME:更模拟模式是取追笔交易-数量数量
  2. 个人主动卖,不成交
  • 撤单成交(仅适用于深交所)
  1. 撤单价==卖一价,对在线服务中撤买的广告数量做
  2. 其他撤单,不做处理

回测结果分析

当我们完成一个策略回测时,我们会得到如下的一个图形

上图为策略回测结果图,红色椭圆形部分包含了策略的信息,包括成果概况、交易详情、每日持仓及收益日志、输出日志。主要的故事,我们详细介绍这几个部分。

深收益概况概况以折线图的方式显示了策略在时间序列上的目标,蓝色为沪策略目标。同时也了30个近似值作为比较基准,蓝色曲线为基准。 ,最绿色的土地为持仓权益,持仓即仓位,10%的仓位只占账面价值的10%。率(如下图所示),就可以将其绘制出来。

如此,衡量一个策略好坏的关键指标在结果概览页面也得到展示。

  • 一切策略:整个探索时间回。如果段上为30%,创业的时间是1万本金,结束时间本金就回到1.3万了,一共赚了3000元。
  • 年化距离:该路线每一年的探索时间为2,总距离为30年,如果每年的年化就在15年左右(大约)。
  • 深基准策略需要有一个比较基准,基准基准为300。若基准基准为15%,保证自己回归测算时间,大盘目标就提高了15%,如果策略接近基准基准,说明策略表现并不好,连大盘都没有跑赢。
  • 阿尔法:衡量策略的指标,该值攻击一个重要的。
  • 贝塔:衡量策略的一个指标,该值越小那么重要。
  • 夏普评估指标:判断策略最重要的计算方法,该考虑的考虑因素,还考虑了考虑,因此,具有比较参考价值,可以根据调整后的收益来理解。
  • 胜率:胜率投资策略一指标,胜率搏击出现。例如10次成功8次盈利,胜率就是80%。
  • 盈亏大小:衡量策略盈亏能力比较,盈亏比扑倒。例如投资信心时,平均能力4元,法力时,平均每2元,那么此时的盈亏比为2。
  • 标准增长率:目标的差,是一个指标。
  • 最大回撤:策略在整个时间段上最严重的时候,净值最高点下降了。如果最大回撤到20%,策略在某个时间点上,比之前的净值最高点下降了20%。最大回撤是策略评估时非常关键的一个指标,通常与风险解决能力相关。
  • 信息教学:信息错误评价指标一个常用的策略

交易详情详情主要显示了策略在整个回测过程中每个交易日的交易信息。包括交易时间、股票代码、交易方向、交易数量、成交价格、交易价格。具体见下图:

每日持仓及收益日常持仓及收益当主要表现日常持有股票代码、日证券价、持仓股票数量、持仓金额、收益等指标。具体见下图

输出日志输出日志主要是为了策略运行过程中的一些日志。包括涨停股票不能交易、停牌估计不能交易等。该日志可以引导我们检查回测结果的正确性。

关于回测结果和指标更详细的分析可参照如下链接

https://bigquant.com/community/t/topic/131431

\

模拟交易

回测完成,可以在simnow的官网(http://www.simnow.com.cn/)注册申请模拟交易账号进行模拟交易。如何需要开通本地模拟交易,请联系宽邦客服,我们会提供技术支持帮助客户搭建好本地模拟交易环境。

\

策略实盘

如果需要在本地进行实盘交易,需要联系开户的期货公司进行穿透式监管认证。请联系宽邦客服,我们会提供技术支持帮助客户完成穿透式监管认证,然后搭建好本地实盘交易环境。

\

API介绍

策略回调接口

initialize(context):

策略初始化函数,只触发一次。可以在该函数中初始化一些变量,如读取配置等

  • context: 策略上下文对象

before_trading(context, data):

策略盘前交易函数,每日盘前触发一次。可以在该函数中一些启动前的准备,如订阅行情等

  • context: 策略上下文对象
  • data: BarDatas对象

on_timer(context, t):

策略定时触发函数,每秒前触发一次。可以在该函数中一些秒级的定时处理工作。

  • context: 策略上下文对象
  • t: datetime对象,生成该事件的时间点

handle_tick(context, tick):

Tick快照行情通知函数,每个标的的行情有变化时则会触发。

  • context: 策略上下文对象
  • tick: TickData对象

handle_bar(context, bar):

Bar行情通知函数,每个bar时间周期会触发,包括日线和分钟。

  • context: 策略上下文对象
  • bar: BarData对象

handle_data(context, data):

行情通知函数,每个时间周期会触发,包括日线和分钟。

  • context: 策略上下文对象
  • data: BarDatas对象

handle_l2trade(context, l2trade_data):

逐笔成交行情通知函数,每笔逐笔成交都会触发,包括上海和深圳。

  • context: 策略上下文对象
  • l2trade_data: L2TradeData对象

handle_l2order(context, l2order_data):

逐笔委托行情通知函数,每笔逐笔委托都会触发,只深圳有。

  • context: 策略上下文对象
  • l2order_data: L2OrderData对象

handle_order(context, order):

委托回报通知函数,每个订单状态有变化时会触发。

  • context: 策略上下文对象
  • order: OrderData对象

handle_trade(context, trade):

成交回报通知函数,有成交时会触发。

  • context: 策略上下文对象
  • trade: TradeData对象

\

策略请求接口

order(symbol, volume, price, order_type=OrderType.LIMIT, offset=Offset.NONE)

  • 下单[股票、期货],必须指定标的、下单数量、下单价格
  • param:
  • symbol: str 下单标的 000001.SZA/600000.SHA/510050.HOF/RB2110.SHF
  • volume: int 下单数量 大于0为买,小于0为卖
  • price: float 下单价格
  • order_type: OrderType 委托类型
  • MARKET: 市价指令
  • LIMIT: 限价指令(默认)
  • offset: 开平方向,股票不需要指定
  • Offset.NONE: 自动决定开平方向(上期所会区分平今,内部会自动处理)
  • Offser.OPEN: 开仓
  • Offset.CLOSE: 平仓
  • Offset.CLOSETODAY: 平今
  • return:int 下单状态码 0:成功,否则失败

order_percent(symbol, percent, price, order_type=OrderType.LIMIT)

  • 按比例下单[股票],必须指定标的、资金仓位比例、下单价格, 只针对买单
  • param:
  • symbol: str 下单标的 000001.SZA/600000.SHA
  • percent: float 资金仓位比例
  • price: float 下单价格
  • order_type: 同上
  • return:int 下单状态码 0:成功否则失败

order_value(symbol, value, price, order_type=OrderType.LIMIT)

  • 按价值下单[股票],必须指定标的、资金、下单价格, 只针对买单
  • param:
  • symbol: str 下单标的 000001.SZA/600000.SHA
  • value: float 资金量
  • price: float 下单价格
  • order_type: 同上
  • return:int 下单状态码 0:成功,否则失败

order_target(symbol, target, price, order_type=OrderType.LIMIT)

  • 下单[股票],必须指定标的、目标数量、下单价格, 多用于清空持仓
  • param:
  • symbol: str 下单标的 000001.SZA/600000.SHA
  • target: float 目标数量
  • price: float 下单价格
  • order_type: 同上
  • return:int 下单状态码 0:成功,否则失败

buy_open(symbol, volume, price, order_type=OrderType.LIMIT)

  • 期货下单,买开
  • param:
  • symbol: str 下单标的 RB2105.SHF, AP2101.CZC, JD2013.DCE, IF2103.CFX
  • volume: int 下单数量
  • price: float 下单价格
  • order_type: 同上
  • return:int 下单状态码 0:成功,否则失败

buy_close(symbol, volume, price, order_type=OrderType.LIMIT)

  • 期货下单,买平
  • param:
  • symbol: str 下单标的
  • volume: int 下单数量
  • price: float 下单价格
  • order_type: 同上
  • return:int 下单状态码 0:成功,否则失败

sell_open(symbol, volume, price, order_type=OrderType.LIMIT)

  • 期货下单,卖开
  • param:
  • symbol: str 下单标的
  • volume: int 下单数量
  • price: float 下单价格
  • order_type: 同上
  • return:int 下单状态码 0:成功,否则失败

sell_close(symbol, volume, price, order_type=OrderType.LIMIT)

  • 期货下单,卖平
  • param:
  • symbol: str 下单标的
  • volume: int 下单数量
  • price: float 下单价格
  • order_type: 同上
  • return:int 下单状态码 0:成功,否则失败

cancel_order(order_param)

  • 取消订单
  • order_param: order_key or OrderData or OrderCancelReq
  • return: int 撤单状态码 0:成功,否则失败

cancel_all()

  • 取消当前账户所有未成交订单
  • return:None

subscribe(symbol)

  • 订阅标的的(实时)行情
  • param:
  • symbol: str 订阅标的,如 000001.SZA/510050.HOF/RB2105.SHF

set_universe(symbols)

  • 设置股票池
  • param:
  • symbols:Interable(Set or List),如 [‘000001.SZA’, ‘510050.HOF’]

\

数据获取相关接口

get_trading_account()

  • 获取资金账户信息,具体参考下面的介绍
  • return: TradingAccount(StockTradingAccount/FutureTradingAccount)

get_account_position(symbol, direction=Direction.NONE, create_if_none=True)

  • 获取指定标的的持仓
  • param:
  • symbol: str 代码,如600000.SHA/000001.SZA/RB2010.SHF/IF2012.CFX/AP2103.CZC
  • direction: Direction,持仓方向,如 Direction.LONG/SHORT
  • create_if_none: bool 仓位不存在时是否创建新的对象
  • return:StockPosition/FuturePosition

get_position(symbol, direction=Direction.NONE, create_if_none=True)

  • 获取指定标的的持仓
  • 同 get_account_position() 具有相同功能

get_account_positions()

  • 获取所有持仓
  • return:Dict[symbol, Position]

get_open_orders(symbol='')

  • 获取当前挂单,即未完全成交订单
  • param:

* symbol: str ,指定了标的时,只获取该标的所有挂单

  • return: List[OrderData]

get_orders(symbol='')

  • 获取当日所有订单
  • param:

* symbol: str

  • return: List[OrderData]

get_trades(symbol='')

  • 获取当日所有成交
  • param:

* symbol: str

  • return: List[TradeData]

get_dominant(product_code)

  • 获取主力合约,只针对期货
  • param:
  • product_code:str 期货品种代码 or 期货代码,比如 A,RB,SR
  • return: str IF2103.CFX, RB2105.SHF

get_universe(symbols)

  • 获取股票池
  • return: List[symbols]

\

其它接口

get_trading_day()

  • 获取当前交易日
  • param:无
  • return:str: YYYYmmdd

get_conf_param(name, d=None)

  • 获取策略设置,获取策略变量,如未获取到, 返回d
  • param:
  • name: 需要获取的设置
  • d: 默认值
  • return:对象的值

get_error_msg(error_id)

  • 获取错误信息
  • param:
  • error_id: 错误代码
  • return:str 错误信息

write_log(content, level="info", stdout=0)

  • 记录日志
  • param:
  • content: str 需要记录的内容
  • level: str 日志级别 debug/info/warn/error
  • stdout: 是否打印到标准输出, 小于0不记录日志,等于0不打印日志到前端,等于1时还会打印日志到前端

calc_buy_volume(symbol, price, value)

  • 计算指标标的可买入数量
  • param:
  • symbol: str 证券代码
  • price: float 指定价格
  • value: float 指定金额 return: int 可买入数量

\

回测专有接口

set_commission(equities_commission=None, futures_commission=None)

  • 设置回测费率,一般股票和期货有不同的费率模式
  • param:
  • equities_commission: PerOrder or dict like {"buy_cost": 0.0002, "sell_cost": 0.0012}
  • futures_commission: PerContract or dict like {"RB": (2, 2, 2)}
  • return:None

set_margin(symbol, margin)

  • 设置回测保证金率,期货专用
  • param:
  • symbol: str 期货品种代码,比如 RB,A,SR
  • margin: float 保证金率,比如 0.05
  • return:None

set_slippage(slippage)

  • 设置回测撮合模型
  • param:
  • slippage: sub-class of SlippageModel object
  • return:None

\

常见对象说明

TradingAccount(StockTradingAccount/FutureTradingAccount)

交易账户资金相关,可访问如下属性

  • trading_day: 交易日 YYYYmmdd
  • portfolio_value: 总资产,主要是资金+持仓市值
  • positions_value: 总持仓市值
  • available: 可用资金,主要是账户资金-冻结资金
  • pre_balance: 昨日账户结算净值
  • balance: 账户资金
  • frozen_cash: 冻结资金
  • realized_pnl: 平仓盈亏
  • total_used_cash: margin + commission + frozen_cash
  • commission: 今日手续费
  • margin: 保证金占用
  • total_margin: 冻结保证金+保证金占用
  • total_frozen_margin: 冻结保证金
Position(StockPosition/FuturePosition)

合约持仓数据, 可访问以下属性

  • trading_day: 交易日 YYYYmmdd
  • direction: 持仓方向 Direction.LONG/SHORT
  • last_price: 最新价
  • cost_price: 持仓均价
  • current_qty: 当前数量
  • avail_qty: 可用数量
  • today_qty: 今持仓
  • yd_qty: 昨持仓
  • frozen_qty: 冻结数量
  • margin: 保证金占用
  • market_value: 持仓市值
  • realized_pnl: 平仓盈亏
  • long: 多头持仓,期货专用
  • short: 空头持仓,期货专用
Portfolio

投资组合对象,主要为兼容zipline框架

  • positions_value: 持仓市值
  • portfolio_value: 总资产
  • cash: 可用资金(即TradingAccount中的available)
  • actual_cash: 实时资金(即TradingAccount中的balance)
  • positions: Dict 获取持仓字典,可通过标的获取各持仓对象
OrderData

委托数据, 可访问以下属性

  • trading_day: 交易日 YYYYmmdd
  • instrument: 合约代码,如 000001/RB2105
  • exchange: 交易所代码,如 SSE/SZSE/SHFE/CFFEX/SHFE/INE/CZCE/DCE
  • symbol: 内部合约标识,如 000001.SZA, RB2105.SHF
  • order_id: 本地委托编号(主要为本地生成)
  • order_sysid: 柜台/交易所报单编号(服务端生成)
  • bt_order_sysid: 本地唯一标识的柜台/交易所报单编号
  • direction: 买卖方向 Direction.LONG/SHORT
  • offset: 开平标志 Offset.OPEN/CLOSE/CLOSETODAY
  • order_qty: 委托数量
  • order_price: 委托限价
  • filled_qty: 成交数量
  • order_type: 委托类型 OrderType.LIMIT/MARKET
  • order_status: 委托状态 OrderStatus.NOTTRADED/ALLTRADED/CANCELLED
  • order_key: 本地订单唯一标识
  • order_time: 委托时间
  • insert_date: 委托日期
  • status_msg: 委托状态描述
TradeData

成交数据, 可访问以下属性

  • trading_day: 交易日 YYYYmmdd
  • instrument: 合约代码,如 000001/RB2105
  • exchange: 交易所代码,如 SSE/SZSE/SHFE/CFFEX/SHFE/INE/CZCE/DCE
  • symbol: 内部合约标识,如 000001.SZA, RB2105.SHF
  • order_id: 本地委托编号(主要为本地生成)
  • order_sysid: 柜台/交易所报单编号(服务端生成)
  • bt_order_sysid: 本地唯一标识的柜台/交易所报单编号
  • trade_id: 成交编号
  • bt_trade_id: 本地唯一标识的成交编号
  • direction: 买卖方向 Direction.LONG/SHORT
  • offset: 开平标志 Offset.OPEN/CLOSE/CLOSETODAY
  • filled_price: 成交价格
  • filled_qty: 成交数量
  • filled_money: 成交金额
  • trade_time: 成交时间
  • trade_date: 成交日期
  • order_key: 本地订单唯一标识
TickData

行情快照数据, 可访问以下属性

  • trading_day: 交易日 YYYYmmdd
  • instrument: 合约代码,如 000001/RB2105
  • exchange: 交易所代码,如 SSE/SZSE/SHFE/CFFEX/SHFE/INE/CZCE/DCE
  • symbol: 内部合约标识,如 000001.SZA, RB2105.SHF
  • last_price: 最新成交价
  • volume: 当日累计成交量
  • turnover: 当日累计成交金额
  • open_price: 当日开盘价
  • high_price: 当日最高价
  • low_price: 当日最低价
  • open_interest: 最新持仓量
  • bid_priceX: 买盘价格
  • bid_volumeX: 买盘数量
  • ask_priceX: 卖盘价格
  • ask_volumeX: 卖盘数量
  • pre_close: 昨收盘(股票里为调整后的价格)
  • upper_limit: 涨停价
  • lower_limit: 跌停价
  • datetime: datetime 当前日期时间
  • time: 当前更新时间 HH:MM:SS.fff
  • time_int: 当前整数时间 93520500,毫秒精度
BarData

Bar行情数据, 可访问以下属性

  • datetime: datetime 当前日期时间
  • symbol: 内部合约标识,如 000001.SZA, RB2105.SHF
  • turnover: 当根bar成交金额
  • close: 当根bar收盘价
  • high: 当根bar最高价
  • low: 当根bar最低价
  • open: 当根bar开盘价
  • open_interest: 最新持仓量
  • trading_day: 交易日期
  • product_code: 品种代码
BarDatas

Bar行情访问(注意不是单个bar数据), 可访问以下属性

  • current_dt: datetime 当前日期时间
  • trading_day_dt: datetime 交易日
  • current_dominant(code): 获取主力合约代码
  • get_daily_value(symbol, field): 获取最新日线数据
  • current(symbol, field): 获取此刻标的指定字段值
  • history(symbol, fields, count, frequency): 获取此刻以前的历史数据
    • symbol: 标的代码
    • fields: 单个或多个字段,如 ‘close’ 或 [‘volume’, ‘open_interest‘]
    • count: 要获取的bar的数量
    • frequency: 频率,如 ‘1d’, ‘1m’

\

策略对象属性

  • instruments:List 运行前时指定的代码列表
  • account_id: str 账户号
  • account: AccountEngine 策略的账户对象
  • portfolio: Portfolio 账户资产组合对象,包含有当前资金和持仓
  • run_mode: RunMode 当前运行模式
  • trading_calendar: TradingCalendar 交易日历对象
  • options: Dict 运行前时指定的'options'参数

\

常量定义说明

Direction:买卖方向/持仓方向

  • LONG: 买(多)
  • SHORT: 卖(空)

Offset:开平标志

  • OPEN: 开仓
  • CLOSE: 平仓
  • CLOSETODAY: 平今

OrderType:委托类型

  • LIMIT: 限价
  • MARKET: 市价

OrderStatus:委托状态

  • UNKNOWN: 未知
  • NOTTRADED: 未成交
  • PARTTRADED: 部分成交
  • ALLTRADED: 全部成交
  • CANCELLED: 全部撤单
  • PARTCANCELLED: 部分撤单
  • REJECTED: 拒单

Frequency:频率

  • DAILY: 日级别
  • MINUTE: 分钟级别
  • TICK: Tick级别
  • TICK2: Level2级别

AdjustType:复权类型

  • NONE: 不复权
  • PRE: 前复权
  • POST: 后复权

Product:产品类别

  • NONE: 未知
  • EQUITY: 股票
  • FUND: 基金
  • FUTURE: 期货
  • OPTION: 期权
  • INDEX: 指数

\

策略示例(按交易频率分类)

例1 均线突破策略(Tick级别)

策略思想:

本策略是基于tick数据的高频日内交易策略。策略每tick触发一次,根据tick数据合并成分钟K线数据,然后计算分钟K线的20均线值,若当前tick价格上穿均线,则买开;反之,则卖开。每日交易次数小于2次,14:30分后不再建仓,尾盘阶段平掉所有仓位。

策略代码如下:

from datetime import datetime,timedelta
from bigtrader.constant import Direction, OrderType

STRATEGY_NAME = "STRATEGY_tick"

# 根据tick数据计算分钟k线的OCHL
def calc_OCHL(ticks):
    tmp_open = 0.5 * (ticks[0].ask_price1 + ticks[0].bid_price1)
    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
    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.set_universe(context.symbol ) #设置需要处理的合约
    context.ma_length = 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):
                context.sell_close(tick.symbol, position_long.avail_qty, price, order_type=OrderType.MARKET)
                msg = cur_hm +  " 尾盘平多 for " + tick.symbol + " 最新价=" + str(price) 
                msg = "{} 尾盘平多 for {}  最新价={}".format(cur_hm,tick.symbol,str(price))
                context.write_log(msg, stdout=0) #输出关键日志,stdout设置为0,不展示到页面,stdout设置为1,展示到页面
        if (position_short.current_qty != 0):
                context.buy_close(tick.symbol, position_short.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 尾盘平空 for {}  最新价={}".format(cur_hm,tick.symbol,str(price))
                context.write_log(msg, stdout=0) #输出关键日志
                
    # 建仓逻辑
    if price > mean_close and  position_long.current_qty  == 0 and  context.trade_count <=2 and cur_hm<="14:30":
        context.buy_open(tick.symbol, context.order_num, price, order_type=OrderType.MARKET)
        msg = "{} 开多 for {}  最新价={}".format(cur_hm,tick.symbol,str(price))
        context.trade_count += 1 # 开仓次数判断需要
        context.write_log(msg, stdout=0) #输出关键日志
    elif price < mean_close and  position_short.current_qty  == 0 and context.trade_count <=2 and cur_hm<="14:30":
        context.sell_open(tick.symbol, context.order_num, price, order_type=OrderType.MARKET)
        msg = "{} 开空 for {}  最新价={}".format(cur_hm,tick.symbol,str(price))
        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=1)
    
        
def handle_trade(context, trade):
    """成交回报推送"""
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 
    
    # 分别获取最新的多头持仓和空头持仓
    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=1) 
    
instruments = ['AG2006.SHF'] # 白银2106合约
strategy_setting = [
    {
        "instruments": instruments,
        "order_num": 1,
        "closetime_day": "14:58",
        "closetime_night": "22:58"
    }]

start_date = "2020-01-01"
end_date = "2020-04-01"
md = M.hfbacktest.v1(
                     start_date=start_date, # 开始时间
                     end_date=end_date, # 结束时间
                     instruments=instruments, # 回测标的
                     capital_base=100000, # 初始资金
                     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()) # 模块依赖,值相同的话会命中缓存

例2 菲阿里四价策略(分钟级别)

策略思想: 菲阿里四价指的是:昨日高点、昨日低点、昨天收盘、今天开盘四个价格。 菲阿里四价上下轨的计算非常简单。昨日高点为上轨,昨日低点为下轨。当价格突破上轨时,买入开仓;当价格突破下轨时,卖出开仓。

策略代码如下:

from datetime import datetime,timedelta
from bigtrader.constant import Direction, OrderType

STRATEGY_NAME = "STRATEGY_1"
    
def initialize(context):
    """初始化"""
    context.ins = context.get_conf_param("instruments") # 从传入参数中获取需要交易的合约
    context.order_num = context.get_conf_param("order_num") # 下单手数
    context.set_universe(context.ins) # 设置需要处理的合约
    
    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, data):
    """盘前处理"""
    context.subscribe(context.ins) # 注册合约
    context.flag = 1 # 用于获取今开
    context.count = 0 # 交易次数记录
    
    bar1d_df = data.history(context.ins, ["high","low",'close'], 1, "1d")
    context.high = bar1d_df.iloc[0]['high'] # 昨高
    context.low = bar1d_df.iloc[0]['low']  # 昨低
    
def handle_data(context, data):
    """Bar行情推送"""

    # 获取当天开盘价
    if context.flag == 1:
        open_price = data.history(context.ins, ["open"], 1, "1m")
        context.flag = context.flag + 1
        context.today_open = open_price.iloc[-1]['open'] 
        
    cur_date =  data.current_dt
    cur_hm = cur_date.strftime('%H:%M')
    
    # 部分品种夜盘收盘时间不一样,此时间表示指定的尾盘平仓时间往后偏移30分钟,这段时间内不能开新仓,只能平仓。给30分钟是为了足够的冗余
    closetime_nightshift = (datetime.strptime(context.closetime_night,'%H:%M') + timedelta(minutes = 30)).strftime('%H:%M')
    # 分别获取多头持和空头持仓
    position_long = context.get_position(context.ins, Direction.LONG)
    position_short = context.get_position(context.ins, Direction.SHORT)
    # 获取当前价格
    price = data.current(context.ins, "close")
    
    # 尾盘平仓
    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(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
        if(position_short.current_qty != 0):
            rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
        #尾盘不开新仓,直接返回
        return
    
    
    # 建仓逻辑
    # 多头持仓,最新价小于开盘价 平多
    if position_long.current_qty != 0 and price < context.today_open:
        rv = context.sell_close(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
        msg = "{} 平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
        context.write_log(msg, stdout=1) 
        
    # 空头持仓,最新价大于开盘价 平空
    elif position_short.current_qty != 0 and price > context.today_open:
        rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
        msg = "{} 平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
        context.write_log(msg, stdout=1) 
       
    # 当前的最新价大于前一天的最高价且没有持仓 开多
    if (position_long.current_qty == 0) and (price > context.high) and (context.count<=1):
        rv = context.buy_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
        msg = "{} 开多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
        context.count += 1 #只开仓一次的判断
        context.write_log(msg, stdout=1) 
    # 当前最新价小于前一天的最低价且没有持仓 开空
    elif (position_short.current_qty == 0) and (price < context.low) and (context.count<=1):
        rv = context.sell_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
        msg = "{} 开空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
        context.count += 1
        context.write_log(msg, stdout=1) 
        
def handle_order(context, order):
    """委托回报推送"""
    msg = "handle_order data:{}".format(order.log_str())
    context.write_log(msg, stdout=1)
         
def handle_trade(context, trade):
    """成交回报推送"""
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 
    # 分别获取最新的多头持仓和空头持仓
    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=1) 

instruments = "RB2110.SHF" 
#需要交易者传入的参数
strategy_setting = [{
        "instruments": instruments,
        "order_num": 2,
        "closetime_day": "14:58",
        "closetime_night": "22:58"
    }]

start_date = "2021-04-06"
end_date = "2021-05-12"
md = M.hfbacktest.v1(start_date=start_date,
                     end_date=end_date,
                     instruments=[instruments],  
                     capital_base=100000,
                     product_type=Product.FUTURE,
                     frequency=Frequency.MINUTE,
                     initialize=initialize,
                     before_trading_start=before_trading,
                     handle_data=handle_data,
                     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())

经典策略示例

例1 均线金叉开仓策略(股票)

策略思想: 1分钟频率回测,如果5均线上穿20均线平空开多,5均线下穿20均线平多开空,为正反手策略。尾盘需要清仓。

策略代码如下:


from bigtrader.constant import Direction, OrderType

#给每个策略取一个名字
STRATEGY_NAME = "double_ma"

def initialize(context):
    """策略初始化函数,只触发一次。可以在该函数中初始化一些变量,如读取配置和全局使用数据"""
    #输出关键日志
    msg = "initialize:" 
    context.write_log(msg, stdout=1)
    
    context.ins = context.get_conf_param("instruments") #从传入参数中获取需要交易的合约
    context.short_ma = 5 #均线短周期参数
    context.long_ma = 20 #均线长周期参数
    context.order_pct =0.1 #每只股票占用总资金的百分比
    context.closetime = "14:55"
        
def before_trading(context, data):
    """盘前处理,策略盘前交易函数,每日盘前触发一次。可以在该函数中一些启动前的准备,如订阅行情等"""
    # 输出关键日志
    msg = "before_trading"
    context.write_log(msg, stdout=1)
    
    # 订阅要交易的合约的行情
    context.subscribe(context.ins)

def handle_data(context, data):
    """行情通知函数"""
    #获取当前时间
    cur_date =  data.current_dt
    cur_hm = cur_date.strftime('%H:%M')
    
    # 获取账户资金
    trading_account = context.get_trading_account()
    total_portfolio = trading_account.portfolio_value
        
    for instr in context.ins:
        #获取持仓情况
        position = context.get_position(instr)

        #最新价格
        price = data.current(instr, "close")
        
        #尾盘平仓
        if(cur_hm>=context.closetime and cur_hm<="15:00"):
            #有持仓则卖出    
            if (position.current_qty != 0):
                rv = context.order(instr, -position.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 尾盘卖出{}  最新价={:.2f} 下单函数返回={}".format(cur_date,context.ins,price,rv)
                context.write_log(msg, stdout=1)   
            #尾盘不开新仓,直接返回
            continue
            
        #获取1m历史数据数据
        hist = context.history_data(instr, ["open","high","low","close"], context.long_ma + 5, "1m")

        #计算短周期和长周期均线
        hist['short_ma'] = hist['close'].rolling(context.short_ma).mean()
        hist['long_ma'] = hist['close'].rolling(context.long_ma).mean()

        #短周期均线上穿越长周期均线买入
        if(hist.iloc[-2]['short_ma']<=hist.iloc[-2]['long_ma'] and hist.iloc[-1]['short_ma']>hist.iloc[-1]['long_ma']):
            #当前没有持仓则市价买入
            if (position.current_qty == 0):
                #计算买入此股票的数量,不要超过总资金的某个比例
                order_num = int(total_portfolio*context.order_pct/price/100)*100
                rv = context.order(instr, order_num, price, order_type=OrderType.MARKET)
                msg = "{} 买入{}  最新价={:.2f} 下单函数返回={}".format(cur_date,context.ins,price,rv)
                context.write_log(msg, stdout=1) 
       
        #短周期均线下穿越长周期均线卖出
        elif(hist.iloc[-2]['short_ma']>=hist.iloc[-2]['long_ma'] and hist.iloc[-1]['short_ma']<hist.iloc[-1]['long_ma']):
            #有持仓则卖出    
            if (position.current_qty != 0):
                rv = context.order(instr, -position.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 卖出{}  最新价={:.2f} 下单函数返回={}".format(cur_date,context.ins,price,rv)
                context.write_log(msg, stdout=1)    
            
def handle_order(context, order):
    """委托回报通知函数,每个订单状态有变化时会触发"""
    #输出关键日志
    msg = "handle_order data:{}".format(order.log_str())
    context.write_log(msg, stdout=1) 
    
def handle_trade(context, trade):
    """成交回报通知函数,有成交时会触发"""
    #输出关键日志
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 


instruments = ["000001.SZA","000002.SZA"]
# 需要交易者传入的参数
strategy_setting = [
    {
        "instruments": instruments,
    }    
]

start_date = "2021-06-01"
end_date = "2021-06-05"
md = M.hfbacktest.v1(start_date=start_date, #回测开始时间
                     end_date=end_date, #回测结束时间
                     instruments=instruments, #股票列表
                     capital_base=1000000, #初始资金
                     product_type=Product.EQUITY, #交易标的为股票
                     frequency=Frequency.MINUTE,  #回测频率
                     initialize=initialize,
                     before_trading_start=before_trading,
                     handle_data=handle_data,
                     handle_order=handle_order,
                     handle_trade=handle_trade,
                     plot_charts=True, #画出回测结果分析图
                     disable_cache=0, #启用缓存加速
                     show_debug_info=1, #打印debug日志
                     strategy_setting=strategy_setting, #配置参数
                     slippage_type=SlippageType.FIXED,#滑点固定模式
                     slippage_value=0.02,#买卖双向各0.02个滑点
                     stock_t1=0 #0表示回测支持当天买和卖。1表示不支持t0
                     )

例2 价格穿越均线开仓策略(期货)

策略思想: 1分钟k线如果上穿20均线则平空开多,如果跌破20均线则平多开空, 为正反手策略。

策略代码如下:

from bigtrader.constant import Direction, OrderType

#给每个策略取一个名字
STRATEGY_NAME = "STRATEGY_1"

def initialize(context):
    """策略初始化函数,只触发一次。可以在该函数中初始化一些变量,如读取配置和全局使用数据"""
    #输出关键日志
    msg = "initialize:" 
    context.write_log(msg, stdout=1)
    
    #从传入参数中获取需要交易的合约
    context.ins = context.get_conf_param("instrument")

    #设置需要处理的股票池或者合约集合
    context.set_universe(context.ins)
        
def before_trading(context, data):
    """盘前处理,策略盘前交易函数,每日盘前触发一次。可以在该函数中一些启动前的准备,如订阅行情等"""
    # 输出关键日志
    msg = "before_trading"
    context.write_log(msg, stdout=1)
    
    # 订阅要交易的合约的行情
    context.subscribe(context.ins)
    
    # 如果需要处理tick数据,需要添加tick处理函数:handle_tick(context, tick):

def handle_bar(context, bar):
    """Bars行情通知函数,每个bar时间周期会触发,包括日线和分钟"""
   
    # 分别获取多头持仓和空头持仓,主要使用 current_qty 和 avail_qty 两个属性
    position_long = context.get_position(context.ins, Direction.LONG)
    position_short = context.get_position(context.ins, Direction.SHORT)

    # 获取账户资金,主要使用 balance 和 available 两个属性,本策略没有使用到账户信息,在此仅作展示获取方法
    trading_account = context.get_trading_account()

    # 获取当前k线最新收盘价格
    price = bar.close

    #获取30根1m历史数据数据
    hist = context.history_data(context.ins, ["open","high","low","close"], 30, "1m")

    #计算20均线
    hist['ma'] = hist['close'].rolling(20).mean()

    #价格向上穿越20均线
    if(hist.iloc[-2]['close']<hist.iloc[-2]['ma'] and price>=hist.iloc[-1]['ma']):
        #有空单的先平掉
        if (position_short.avail_qty != 0):
            rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
            context.write_log("buy_close error={}".format(context.get_error_msg(rv)), stdout=1)
            msg = "{} 平空 for {}  最新价={} 下单函数返回={}".format(bar.datetime,context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1)
        #没有多单则开多1手
        if (position_long.current_qty == 0):
            rv = context.buy_open(context.ins, 1, price, order_type=OrderType.MARKET)
            msg = "{} 开多 for {}  最新价={} 下单函数返回={}".format(bar.datetime,context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) 
    #价格向下穿越20均线
    if (hist.iloc[-2]['close']>hist.iloc[-2]['ma'] and price<hist.iloc[-1]['ma']):
        #有多单的先平掉
        if (position_long.avail_qty != 0):
            rv = context.sell_close(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 平多 for {}  最新价={} 下单函数返回={}".format(bar.datetime,context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1)
        #没有空单则开空1手    
        if (position_short.current_qty == 0):
            rv = context.sell_open(context.ins, 1, price, order_type=OrderType.MARKET)
            msg = "{} 开空 for {}  最新价={} 下单函数返回={}".format(bar.datetime,context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1)    
            
def handle_order(context, order):
    """委托回报通知函数,每个订单状态有变化时会触发"""
    #输出关键日志
    msg = "handle_order data:{}".format(order.log_str())
    context.write_log(msg, stdout=1) 
    
        
def handle_trade(context, trade):
    """成交回报通知函数,有成交时会触发"""
    #输出关键日志
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 

    

instrument = "RB2110.SHF"
# 需要交易者传入的参数
strategy_setting = [
    {
        "instrument": instrument,
    }    
]
start_date = "2021-05-14"
end_date = "2021-05-15"
md = M.hfbacktest.v1(start_date=start_date, #回测开始时间
                     end_date=end_date, #回测结束时间
                     instruments=[instrument], #合约列表
                     capital_base=100000, #初始资金
                     product_type=Product.FUTURE,
                     frequency=Frequency.MINUTE,  #回测频率
                     initialize=initialize,
                     before_trading_start=before_trading,
                     handle_bar=handle_bar,
                     handle_order=handle_order,
                     handle_trade=handle_trade,
                     plot_charts=True, #画出回测结果分析图
                     disable_cache=0, #启用缓存加速
                     show_debug_info=1, #打印debug日志
                     strategy_setting=strategy_setting, #配置参数
                     slippage_type=SlippageType.FIXED,#滑点固定模式
                     slippage_value=1.0,#买卖双向各1个滑点
                     )

例3 Dual Thrust日内策略

策略思想: 计算前N天的最高价-收盘价的最小值和收盘价的最大值-最低价。然后取其中的较大值,乘以k值。把结果称为触发值。 当价格超过上轨(开盘+触发值)时买入,或者价格低于下轨(开盘-触发值)时卖空。此策略没有止损,为正反手策略。

策略代码如下:

from datetime import datetime,timedelta
from bigtrader.constant import Direction, OrderType

STRATEGY_NAME = "STRATEGY_1m"
        
def initialize(context):
    """初始化"""
    print("initialize")  
    context.ins = context.get_conf_param("instruments")#从传入参数中获取需要交易的合约
    context.order_num = context.get_conf_param("order_num")#下单手数
    context.set_universe(context.ins)#设置需要处理的合约
    context.N = 5 #基于N天的历史数据
    context.k1 = 0.2 #上轨参数
    context.k2 = 0.2 #下轨参数
    context.today_open = 0 #当天开盘价
    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, data):
    """盘前处理"""
    context.subscribe(context.ins) #注册合约
    context.index = 0
    context.flag = 1 #用于获取今开
    # 取历史数据
    hist = data.history(context.ins, ["open","high","low","close"], context.N, "1d")
    # 计算Dual Thrust 的上下轨
    HH = hist['high'].max()
    HC = hist['close'].max()
    LC = hist['close'].min()
    LL = hist['low'].min()
    context.range = max(HH - LC, HC - LL)

def handle_data(context, data):
    """Bar行情推送"""
    #获取当天的开盘价
    if context.flag == 1 :
        history_data = data.history(context.ins, ["open"], 1, "1m")
        context.today_open = history_data.iloc[-1]['open'] 
        context.flag += 1
    
    #获取当前时间
    cur_date = data.current_dt
    cur_hm = cur_date.strftime('%H:%M') 
    #计算上下轨
    buy_line = context.today_open + context.range * context.k1  
    sell_line = context.today_open - context.range * context.k2 
    # 分别获取多头持仓和空头持仓
    position_long = context.get_position(context.ins, Direction.LONG)
    position_short = context.get_position(context.ins, Direction.SHORT)
    # 获取当前价格
    price = data.current(context.ins, "close")    
    
    #尾盘平仓
    #部分品种夜盘收盘时间不一样,此时间表示指定的尾盘平仓时间往后偏移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(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) 
        if(position_short.current_qty != 0):
            rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) 
        #尾盘不开新仓,直接返回
        return
        
    #交易逻辑
    if price > buy_line:
        if position_long.current_qty != 0:
            return
        else:
            if position_short.current_qty != 0:
                rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1)
            rv = context.buy_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
            msg = "{} 开多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) 
   
    elif price < sell_line:
        if position_short.current_qty != 0:
            return
        else:
            if position_long.current_qty != 0:
                rv = context.sell_close(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1) 
            rv = context.sell_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
            msg = "{} 开空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1)    

            
def handle_order(context, order):
    """委托回报推送"""
    msg = "handle_order data:{}".format(order.log_str())
    context.write_log(msg, stdout=1)
         
def handle_trade(context, trade):
    """成交回报推送"""
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 
    # 分别获取最新的多头持仓和空头持仓
    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=1) 
    

instruments = "RB2110.SHF" 

#需要交易者传入的参数
strategy_setting = [
    {
        "instruments": instruments,
        "order_num": 2,
        "closetime_day": "14:58",
        "closetime_night": "22:58"
    }    
    
]
start_date = "2021-04-21"
end_date = "2021-04-27"
md = M.hfbacktest.v1(start_date=start_date,
                     end_date=end_date,
                     instruments=[instruments], 
                     capital_base=100000,
                     product_type=Product.FUTURE,
                     frequency=Frequency.MINUTE,
                     initialize=initialize,
                     before_trading_start=before_trading,
                     handle_data=handle_data,
                     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())

例4 R-Breaker日内策略

策略思想: R-Breaker是一种短线日内交易策略。根据前一个交易日的收盘价(C)、最高价(H)和最低价(L)数据通过一定方式计算出六个价位,从大到小依次为: 突破买入价、观察卖出价、反转卖出价、反转买入、观察买入价、突破卖出价。 中心价位P = (H + C + L)/3 突破买入价 = H + 2P -2L 观察卖出价 = P + H - L 反转卖出价 = 2P - L 反转买入价 = 2P - H 观察买入价 = P - (H - L) 突破卖出价 = L - 2(H - P)

空仓时:突破策略 空仓时,当盘中价格>突破买入价,则认为上涨的趋势还会继续,开仓做多; 空仓时,当盘中价格<突破卖出价,则认为下跌的趋势还会继续,开仓做空。

持仓时:反转策略 持多单时:当日内最高价>观察卖出价后,盘中价格回落,跌破反转卖出价构成的支撑线时,采取反转策略,即做空; 持空单时:当日内最低价<观察买入价后,盘中价格反弹,超过反转买入价构成的阻力线时,采取反转策略,即做多。

策略代码如下:

from datetime import datetime,timedelta
from bigtrader.constant import Direction, OrderType

STRATEGY_NAME = "STRATEGY_1m"
        
def initialize(context):
    """初始化"""
    print("initialize")  
    context.symbol = instruments
    context.ins = context.get_conf_param("instruments")#从传入参数中获取需要交易的合约
    context.order_num = context.get_conf_param("order_num")#下单手数
    context.set_universe(context.ins)#设置需要处理的合约
    context.stopLossPrice = 50    # 设置止损点数
    
    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, data):
    """盘前处理"""
    #print("before_trading")
    context.subscribe(context.ins) #注册合约
    context.max_high = 0 #当日最高价
    context.max_low = 0 #当日最低价
    hist = data.history(context.ins, ["high","low","open","close"], 1, "1d")
    high = hist['high'].iloc[0]  # 前一日的最高价
    low = hist['low'].iloc[0]  # 前一日的最低价
    close = hist['close'].iloc[0]  # 前一日的收盘价
    pivot = (high + low + close) / 3  # 枢轴点
    context.bBreak = high + 2 * (pivot - low)  # 突破买入价 
    context.sSetup = pivot + (high - low)  # 观察卖出价
    context.sEnter = 2 * pivot - low  # 反转卖出价
    context.bEnter = 2 * pivot - high  # 反转买入价
    context.bSetup = pivot - (high - low)  # 观察买入价
    context.sBreak = low - 2 * (high - pivot)  # 突破卖出价

    
def handle_data(context, data):
    """Bar行情推送"""
    cur_date =  data.current_dt
    cur_hm = cur_date.strftime('%H:%M') 
    
    #获取当日最高价和最低价
    today_high = data.current(context.ins,'high')
    today_low = data.current(context.ins,'low')
    context.max_high = max(context.max_high,today_high)
    context.max_low = max(context.max_high,today_low) 
    
    # 分别获取多头持仓,和空头持仓
    position_long = context.get_position(context.ins, Direction.LONG)
    position_short = context.get_position(context.ins, Direction.SHORT)
    
    # 获取当前价格
    price = data.current(context.ins, "close")
    
    #部分品种夜盘收盘时间不一样,此时间表示指定的尾盘平仓时间往后偏移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(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
        if(position_short.current_qty != 0):
            rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
        #尾盘不开新仓,直接返回
        return
        
    # 突破策略:
    if position_long.current_qty == 0 and position_short.current_qty == 0:  # 空仓条件下
        if price > context.bBreak:
            # 在空仓的情况下,如果盘中价格超过突破买入价,则采取趋势策略,即在该点位开仓做多
            rv = context.buy_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
            msg = "{} 开多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
            context.open_position_price = price
        elif price < context.sBreak:
            # 在空仓的情况下,如果盘中价格跌破突破卖出价,则采取趋势策略,即在该点位开仓做空
            rv = context.sell_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
            msg = "{} 开空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
            context.open_position_price = price
    # 设置止损条件
    else:  # 有持仓时
        # 开仓价与当前行情价之差大于止损点则止损
        if (position_long.current_qty != 0 and context.open_position_price - price >= context.stopLossPrice) or \
                (position_short.current_qty != 0 and price - context.open_position_price >= context.stopLossPrice):
            context.write_log('达到止损点,全部平仓', stdout=1) #输出关键日志
            if(position_long.current_qty != 0):
                rv = context.sell_close(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 止损平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1) #输出关键日志
            if(position_short.current_qty != 0):
                rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 止损平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1) #输出关键日志
        # 反转策略:
        if position_long.current_qty != 0:  # 多仓条件下
            if context.max_high > context.sSetup and price < context.sEnter:
                # 多头持仓,当日内最高价超过观察卖出价后
                # 盘中价格出现回落,且进一步跌破反转卖出价构成的支撑线时
                # 采取反转策略,即在该点位反手做空
                context.write_log('多头持仓,当日内最高价超过观察卖出价后跌破反转卖出价: 反手做空', stdout=1) #输出关键日志
                rv = context.sell_close(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1) #输出关键日志
                
                rv = context.sell_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
                context.open_position_price = price
                msg = "{} 开空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1) #输出关键日志  
                
        elif position_short.current_qty != 0:  # 空头持仓
            if context.max_low < context.bSetup and price > context.bEnter:
                # 空头持仓,当日内最低价低于观察买入价后,
                # 盘中价格出现反弹,且进一步超过反转买入价构成的阻力线时,
                # 采取反转策略,即在该点位反手做多
                context.write_log('空头持仓,当日最低价低于观察买入价后超过反转买入价: 反手做多', stdout=1) #输出关键日志
                rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
                msg = "{} 平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1) #输出关键日志
                
                rv = context.buy_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
                context.open_position_price = price
                msg = "{} 开多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                context.write_log(msg, stdout=1) #输出关键日志   
                

def handle_order(context, order):
    """委托回报推送"""
    msg = "handle_order data:{}".format(order.log_str())
    context.write_log(msg, stdout=1)
         
def handle_trade(context, trade):
    """成交回报推送"""
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 
    # 分别获取最新的多头持仓和空头持仓
    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=1) 
    
instruments = "RB2005.SHF"

#需要交易者传入的参数
strategy_setting = [
    {
        "instruments": instruments,
        "order_num": 2,
        "closetime_day": "14:58",
        "closetime_night": "22:58"
    }    
]
start_date = "2020-01-21"
end_date = "2020-04-01"
md = M.hfbacktest.v1(start_date=start_date,
                     end_date=end_date,
                     instruments=[instruments], #只传入一个合约便于策略逻辑展示
                     capital_base=100000,
                     product_type=Product.FUTURE,
                     frequency=Frequency.MINUTE,
                     initialize=initialize,
                     before_trading_start=before_trading,
                     handle_data=handle_data,
                     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())

例5 网格交易策略

策略思想: 第一步:确定价格中枢、压力位和阻力位 第二步:确定网格的数量和间隔 第三步:当价格触碰到网格线时,若高于买入价,则每上升一格卖出m手;若低于买入价,则每下跌一格买入m手。

策略说明: 1、采用区域判断方式。根据网格线划分网格区域为1、2、3、4、5、6。利用pandas库提供的cut函数,将当前价格所处的网格区域表示出来。当网格区域发生变化,说明价格突破了一个网格线。 2、为了避免出现4区-5区开仓一次,5区-4区又平仓一次这种假突破,我们认定4-5开仓一次和5-4平仓一次实际上突破的是一根线,此时的形态是价格沿着这根线上下波动。只有第一次穿过这条线时才是真正的交易信号,其他的并没有形成突破。因此我们需要一个变量储存每一次交易时网格区域的变化形态(按照从大到小的顺序),比如5-4可以记为[4,5],4-5记为[4,5]。当新的记录=旧的记录时,信号失效

策略代码如下:

from datetime import datetime,timedelta
from bigtrader.constant import Direction, OrderType

STRATEGY_NAME = "STRATEGY_1m"
        
def initialize(context):
    """初始化"""
    print("initialize")  
    context.ins = context.get_conf_param("instruments")#从传入参数中获取需要交易的合约
    context.order_num = context.get_conf_param("order_num")#下单手数
    context.set_universe(context.ins)#设置需要处理的合约
    context.last_grid = 0 # 储存前一个网格所处区间,用来和最新网格所处区间作比较
    
    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, data):
    """盘前处理"""
    context.subscribe(context.ins) #注册合约

    # 记录上一次交易时网格范围的变化情况(例如从4区到5区,记为4,5)
    context.grid_change_last = [0,0]
    # 以前一日的收盘价为中枢价格
    context.center = data.history(context.ins,["close"],1,"1d").iat[0,0]

    
def handle_data(context, data):
    """Bar行情推送"""
    cur_date =  data.current_dt
    cur_hm = cur_date.strftime('%H:%M') #time           
    # 分别获取多头持仓和空头持仓
    position_long = context.get_position(context.ins, Direction.LONG)
    position_short = context.get_position(context.ins, Direction.SHORT)
    # 获取当前价格
    price = data.current(context.ins, "close")
    
    #部分品种夜盘收盘时间不一样,此时间表示指定的尾盘平仓时间往后偏移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(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
        if(position_short.current_qty != 0):
            rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 尾盘平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
        #尾盘不开新仓,直接返回
        return
        
    # 设置网格和当前价格所处的网格区域
    band = np.array([0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09]) * context.center
    grid = pd.cut([price], band, labels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18])[0]
    # 如果价格超出网格设置范围,则提示调节网格宽度和数量
    if np.isnan(grid):
        context.write_log("价格波动超过网格范围,可适当调节网格宽度和数量", stdout=1) #输出关键日志
    # 如果新的价格所处网格区间和前一个价格所处的网格区间不同,说明触碰到了网格线,需要进行交易
    # 如果新网格大于前一天的网格,做空或平多
    if context.last_grid < grid:
        # 记录新旧格子范围(按照大小排序)
        grid_change_new = [context.last_grid,grid]
        # 几种例外:
        # 当last_grid = 0 时是初始阶段,不构成信号
        # 如果此时grid = 3,说明当前价格仅在开盘价之下的3区域中,没有突破网格线
        # 如果此时grid = 4,说明当前价格仅在开盘价之上的4区域中,没有突破网格线
        if context.last_grid == 0:
            context.last_grid = grid
            return
        if context.last_grid != 0:
            # 如果前一次开仓是4-5,这一次是5-4,算是没有突破,不成交
            if grid_change_new != context.grid_change_last:
                # 更新前一次的数据
                context.last_grid = grid
                context.grid_change_last = grid_change_new
                # 如果有多仓,平多
                if position_long.current_qty != 0:
                    rv = context.sell_close(context.ins,context.order_num, price, order_type=OrderType.MARKET)
                    msg = "{} 平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                    context.write_log(msg, stdout=1) #输出关键日志
                # 否则,做空
                if not position_long.current_qty != 0:
                    rv = context.sell_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
                    msg = "{} 开空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                    context.write_log(msg, stdout=1) #输出关键日志
                    
    # 如果新网格小于前一天的网格,做多或平空
    if context.last_grid > grid:
        # 记录新旧格子范围(按照大小排序)
        grid_change_new = [grid,context.last_grid]
        # 几种例外:
        # 当last_grid = 0 时是初始阶段,不构成信号
        # 如果此时grid = 3,说明当前价格仅在开盘价之下的3区域中,没有突破网格线
        # 如果此时grid = 4,说明当前价格仅在开盘价之上的4区域中,没有突破网格线
        if context.last_grid == 0:
            context.last_grid = grid
            return
        if context.last_grid != 0:
            # 如果前一次开仓是4-5,这一次是5-4,算是没有突破,不成交
            if grid_change_new != context.grid_change_last:
                # 更新前一次的数据
                context.last_grid = grid
                context.grid_change_last = grid_change_new
                # 如果有空仓,平空
                if position_short.current_qty != 0:
                    rv = context.buy_close(context.ins, context.order_num, price, order_type=OrderType.MARKET)
                    msg = "{} 平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                    context.write_log(msg, stdout=1) #输出关键日志
                # 否则,做多
                if not position_short:
                    rv = context.buy_open(context.ins, context.order_num, price, order_type=OrderType.MARKET)
                    msg = "{} 开多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
                    context.write_log(msg, stdout=1) #输出关键日志
                        
    # 设计一个止损条件:当持仓量达到10手,全部平仓
    if position_long.current_qty == 10 or position_long.current_qty == 10:
        context.write_log('触发止损,全部平仓', stdout=1) #输出关键日志
        if(position_long.current_qty != 0):
            rv = context.sell_close(context.ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 止损平多 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志
        if(position_short.current_qty != 0):
            rv = context.buy_close(context.ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
            msg = "{} 止损平空 for {}  最新价={} 下单函数返回={}".format(str(data.current_dt),context.ins,str(price),str(rv))
            context.write_log(msg, stdout=1) #输出关键日志

            
def handle_order(context, order):
    """委托回报推送"""
    msg = "handle_order data:{}".format(order.log_str())
    context.write_log(msg, stdout=1)
         
def handle_trade(context, trade):
    """成交回报推送"""
    msg = "handle_trade data:{}".format(trade.log_str())
    context.write_log(msg, stdout=1) 
    # 分别获取最新的多头持仓和空头持仓
    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=1) 

instruments = "RB2110.SHF" 

#需要交易者传入的参数
strategy_setting = [
    {
        "instruments": instruments,
        "order_num": 2,
        "closetime_day": "14:58",
        "closetime_night": "22:58"
    }    
]
start_date = "2021-01-01"
end_date = "2021-05-12"
md = M.hfbacktest.v1(start_date=start_date,
                     end_date=end_date,
                     instruments=[instruments], 
                     capital_base=100000,
                     product_type=Product.FUTURE,
                     frequency=Frequency.MINUTE,
                     initialize=initialize,
                     before_trading_start=before_trading,
                     handle_data=handle_data,
                     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())

常见问题

回测如何设置手续费和保证金率

可以在Initial函数中通过context的set_commission设置

def initialize(context):
    """初始化"""
    print("initialize")    

    # 股票设置费率的示例
    context.set_commission(equities_commission=PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5.0))    

    # 期货设置费率的示例
    comm_dict = {
        # 开仓,平仓,平今手续费
        'I':(0.00012, 0.00006, 0.00012),
        'RB':(0.000045, 0.000045, 0.000045)
    }
    context.set_commission(futures_commission=PerContract(comm_dict))

    # 期货设置保证金率
    context.set_margin('RB', 0.05)


\

评论
  • 文档不能稍微好好搞一下吗,至少代码能保证运行吧。。。。
{link}