BigTrader 量化交易引擎(回测)
简介
BigTrader 是 BigQuant 推出的专业级量化交易引擎,主要用于策略在历史数据中回测撮合。BigTrader采用 C++ 核心实现,并提供 Python API 接口和回调函数。它为量化投资者提供了一个全面的交易解决方案,无论您是初学者还是专业投资者,都能轻松上手使用。
核心特性
全市场覆盖
多品种支持:覆盖股票、基金、期货、可转债、指数、期权、两融等主流投资品种
全面的数据支持:支持从日线到逐笔成交的各类数据
多场景适配
回测环境:用于策略开发和历史表现分析
仿真模拟:在仿真环境下进行模拟交易
实盘交易:直接接入真实市场进行交易
绩效分析:全方位评估策略表现
多级别交易支持
日频交易(日线级别)
分钟级交易
Tick 级交易
逐笔交易(Tick2)
技术优势
高度还原真实交易
回测环境最大程度模拟真实市场条件
考虑各种交易限制和市场规则
确保回测结果的可靠性和参考价值
无缝代码迁移
同一套代码可用于回测、模拟和实盘
无需在不同环境间修改代码
大幅降低策略部署成本
专业工程框架
结构化的策略开发环境
完善的工程化支持
提高策略开发效率
便于团队协作和策略管理
适用人群
初学者
友好的 Python API 接口
详细的使用文档和示例
完整的策略开发流程指导
专业投资者
高性能 C++ 核心引擎
灵活的策略定制能力
专业的风控和监控体系
交易引擎介绍
BigQuant回测引擎为时间驱动回测引擎,当设定好每一根K线后,会根据K线频率读取数据。如当在回测引擎中设置为daily时,回测引擎会按照天从传入数据读取。
回测引擎交易时间图{w:100}{w:100}{w:100}{w:100}{w:100}{w:100}{w:100}
回测引擎运行逻辑{w:100}{w:100}{w:100}{w:100}{w:100}{w:100}{w:100}
回测引擎的运行逻辑为每一根K线运行一次,在当前K线进行下单操作时会在下一根K线开始撮合成交。
交易引擎的调取
搜索bigtrader, 拖入画布即可
可视化回测引擎
交易引擎详细介绍
业务流程框架图:
交易所后缀
平台的各交易所代码后缀说明,均为真实交易代码+交易所后缀:
交易所 | 代码后缀 | 示例 | 说明 |
|---|---|---|---|
上交所 SSE | SH | 600000.SH、510050.SH、000001.SH | 含股票、基金、可转债、指数 |
深交所 SZSE | SZ | 000001.SZ、159919.SZ、399001.SZ | 含股票、基金、可转债、指数 |
北交所 BSE | BJ | 920099.BJ | 含股票、指数 |
中金所 CFFEX | CFE | IF2501.CFE, T2503.CFE, IO2501-C-4300.CFE | 含股指期货、国债期货、股指期权 |
上期所 SHFE | SHF | rb2505.SHF, cu2503.SHF | 含期货和期货期权,合约代码为小写+2位年份+2位月份 |
上能所 INE | INE | sc2505.SHF | 含期货和期货期权,合约代码为小写+2位年份+2位月份 |
大商所 DCE | DCE | a2505.DCE | 含期货和期货期权,合约代码为小写+2位年份+2位月份 |
郑商所 CZCE | CZC | SR505.CZC | 含期货和期货期权,合约代码为大写+1位年份+2位月份 |
广期所 GFEX | GFE | si2505.GFE | 含期货和期货期权,合约代码为小写+2位年份+2位月份 |
上交所期权 SSE | SHO | 10000001.SHO | |
深交所期权 SZSE | SZO | 90000001.SZO |
策略回调函数介绍
每个策略需要实现以下一个或多个回调函数,并传给交易引擎,交易引擎会在不同的事件触发时调用对应的策略回调函数,以通知策略发生事件变化(如行情更新、委托回报通知、成交回报通知),策略的逻辑主要在 handle_data 回调函数内实现。用户可通过可视化或代码方式对函数内容进行更改,最终实现通过回测引擎达到回测的目的。以下是各类函数的运行逻辑。
函数名称 | 函数英文名称 | 说明 |
|---|---|---|
初始化函数 | initialize(context) | 策略初始化函数,只触发一次。可以在该函数中初始化一些变量,如读取配置、设置交易费率、设置交易滑点等 |
盘前处理函数 | before_trading(context, data) | 策略盘前交易函数,每日盘前触发一次。可以在该函数中处理一些当日交易前的准备,如高频回测交易时要订阅行情等 |
行情处理函数 | handle_data(context, data) | K线行情通知函数,频率支持日线和分钟。注册多个合约时,handle_data会等待所有合约数据到齐后统一触发一次 |
tick处理函数 | handle_tick(context, tick) | Tick快照行情通知函数,每个标的的行情有变化时则会触发,此函数的触发依赖于交易所实时行情推送。 |
逐笔成交处理函数 | handle_l2trade(context, l2trade) | 逐笔成交行情更新时的处理函数 |
逐笔委托处理函数 | handle_l2order(context, l2order) | 逐笔委托行情更新时的处理函数 |
委托回报通知函数 | handle_order(context, order) | 委托回报通知函数,每个订单状态有变化时会触发。 |
成交回报通知函数 | handle_trade(context, trade) | 成交回报通知函数,有成交时会触发。 |
盘后处理函数 | after_trading(context, data) | 盘后处理函数,每日盘后运行一次 |
有两个使用频率很高的函数:initialize函数和handle_data函数,理解了这两个函数开发策略就再也不是什么难事了,结合下面K线图来理解这两个函数。
初始化函数、行情处理函数原理
什么是策略的 context
context是策略的上下文对象,每个策略的函数的第一个参数都是 context,必须通过此对象才能获取到策略交易相关的资产,如资金、持仓、委托等,也必须通过此对象才发完成发单的操作。context 拥有的属性和接口几以下介绍。context 有哪些属性
portfolio:投资组合对象,其中包含 资金cash: float, 持仓字典positions: dict 等属性
user_store: 用户持久化变量的数据对象,字典类型,主要用于模拟交易,用户可以将策略需要持久化的变量写入该字典。
data: 用户通过回测入口传入的自定义数据(如预测/因子数据),用户传入什么数据,在策略里引用 context.data 就是什么数据。
context 有哪些接口
下单接口调用示例:context.order(instrument, 100) 或 context.order_target_percent(instrument, 0.2)
下单接口公有说明:
instrument: str, 平台内的标的代码,如 000001.SZ 或 rb2501.SHF 等
limit_price: float,默认为 None,表示使用市价单,实盘中上交所市价单时,此字段为保护价,并指定 order_type=OrderType.MARKET(市价五档即成剩撤)
order_type: OrderType,默认为None,表示使用市价单,当指定了 order_type 参数时,订单类型优先为指定类型,可从 bigtrader.constant 中 import
下单接口返回值为 int 类型,小于0表示失败,失败时可使用 context.get_error_msg(ret_code) 获取失败的错误信息
下单成功后,可以通过 context.get_last_order_key() 获取刚下单的委托的本地唯一编号,后续的委托回报均可通过 order_key 找到对应关系
接口分类 | 接口名称 | 接口说明 | |
|---|---|---|---|
交易类 | |||
普通下单 | order(instrument: str, order_qty: int, limit_price=None, order_type=None) | order_qty:委托数量,>0为买,<0为卖。 | |
按目标量下单 | order_target(instrument: str, target: int, limit_price=None, order_type=None) | target: 为目标持仓量,注意下期货订单时可以为负,表示做空至 target 手 | |
按目标仓位下单 | order_target_percent(instrument: str, percent: float, limit_price=None, order_type=None) | target: 为目标持仓占比(按账户总资产计算占比) | |
按目标金额下单 | order_target_value(instrument: str, value: float, limit_price=None, order_type=None) | ||
按金额下单 | order_value(instrument: str, value: float, limit_price=None, order_type=None) | ||
按仓位下单 | order_percent(instrument: str, percent: float, limit_price=None, order_type=None) | ||
买入开仓 | buy_open(instrument: str, order_qty: int, limit_price=None, order_type=None) | 期货/期权专用 | |
卖出平仓 | sell_close(instrument: str, order_qty: int, limit_price=None, order_type=None) | 期货/期权专用 | |
卖出开仓 | sell_open(instrument: str, order_qty: int, limit_price=None, order_type=None) | 期货/期权专用 | |
买入平仓 | buy_close(instrument: str, order_qty: int, limit_price=None, order_type=None) | 期货/期权专用 | |
担保品买卖 | margin_trade(instrument: str, order_qty: int, limit_price=None, order_type=None) | 两融专用 | |
融资买入 | margincash_open(instrument: str, order_qty: int, limit_price=None, order_type=None) | 两融专用 | |
卖券还款 | margincash_close(instrument: str, order_qty: int, limit_price=None, order_type=None) | 两融专用 | |
融券卖出 | marginsec_open(instrument: str, order_qty: int, limit_price=None, order_type=None) | 两融专用 | |
买券还券 | marginsec_close(instrument: str, order_qty: int, limit_price=None, order_type=None) | 两融专用 | |
直接还款 | margincash_direct_refund(value: float) | 两融专用 | |
直接还券 | marginsec_direct_refund(instrument: str, order_qty: int) | 两融专用 | |
撤单 | context.cancel_order(order_param) | order_param:可以是 order 数据,也可以 order_key,也可以 cancel_req | |
查询类 | |||
获取资金账户 | get_trading_account() -> FundData | 包含总资金,可用资金,保证金,持仓市值等 | |
获取两融负债信息 | get_marginasset_data() -> Dict | 包含总资金,可用保证金,融资负债,融券负债等 | |
获取总资金 | get_balance() -> float | 总资金=可用资金+冻结资金 | |
获取可用资金 | get_available_cash() -> float | ||
获取账户总资产 | get_portfolio_value() -> float | 股票总资产 = 总资金 + 总持仓市值 | |
获取单个持仓 | get_position(instrument: str, direction=None)-> PositionData | 注意,获取期货的持仓时,需要传入 direction 值时,'1'获取多持仓持仓数据,'2'获取空头持仓数据,否则返回的是一个期货多空持仓的组合对象(可以通过 .long 或 .short 获取多头仓数据或空头持仓数据) | |
获取所有持仓 | get_positions() -> Dict[key, PositionData] | 使用该接口获取的持仓字典中,字典的值为期货持仓时,也是一个期货多空持仓的组合对象(可以通过 .long_position() 或 .short_position() 获取多头仓数据或空头持仓数据 PositionData) | |
获取当日委托 | get_orders(instrument="") -> List[OrderData] | instrument 传入值时表示只获取此代码相关的委托 | |
获取未成交委托 | get_open_orders(instrument="") -> List[OrderData] | instrument 传入值时表示只获取此代码相关的挂单 | |
获取当日成交 | get_trades(instrument="") -> List[TradeData] | instrument 传入值时表示只获取此代码相关的成交 | |
获取最新价格 | get_last_price(instrument: str) -> float | ||
获取合约信息 | get_contract(instrument: str) -> ContractData | ContractData 主要有 合约大小multiplier, 最小变动价格price_tick, 合约名称name, 标的代码underlying 等属性 | |
获取当前交易日 | get_trading_day() -> str | 返回当前的交易日 YYYYmmdd,夜盘时交易为第二天(周末或节假日顺延) | |
设置类 | |||
设置费率 | from bigtrader.finance.commission import PerOrder, PerContract | 股票使用 PerOrder 设置费率,参数分别为 买入费率buy_cost,卖出费率sell_cost,最少费用min_cost,印花税率tax_ratio。 | |
设置保证金率 | set_margin_ratio(code: str, margin_ratio: float) | 期货专用,参数 code 可以是品种代码 rb 或完全合约代码 rb2501.SHF | |
设置滑点 | set_slippage_name("fixed") 设置为策略指定价格成交,一般可用于交割单回测set_slippage_value(slippage_type=1, slippage_value=1.0) 或者 set_slippage_value(slippage_type=2, slippage_value=0.005) | 使用 set_slippage_value 时,slippage_type=1 表示固定滑点 slippage_value 的值slippage_type=2 表示滑点 slippage_value 百分比的值 | |
其它 | |||
订阅或取消订阅行情 | 订阅 Tick 数据:subscribe(instruments: List[str] | str) | 主要为订阅高频数据(分钟或Tick)行情,且做高频回测时,每日在盘前函数必须订阅策略当天所需要的代码列表的行情(注意,不需要的代码不要订阅,否则会影响性能) | |
是否是回测模式 | is_backtest_mode() -> bool | ||
什么是 data
如上述,多个策略回调函数的第二个参数就是 data,它是一个 IBarData 对象,封装了一些访问 Bar 数据的访问接口
data 有哪些属性:
属性名称 | 属性说明 | |
|---|---|---|
data.current_dt | datetime 类型,表示回测中当前回测自然日期+时间 | |
data.trading_day_dt | datetime类型,表示回测中当前的交易日,如期货夜盘时,current_dt为夜盘的自然日期,而交易日则是第二天的日期了 | |
data 有哪些接口:
接口名称 | 接口 | 接口说明 |
|---|---|---|
获取 Bar 某字段值 | current(instrument: str, field: str) -> float | int | instrument:平台内的标的代码,如 000001.SZ 或 rb2501.SHF 等 |
获取历史N根 Bar 的值 | history(instrument: str, fields: List[str] | str, count: int, frequency: str, expect_ndarray=True) | frequency: 1d 或 1m |
获取日K线的值 | get_daily_value(instrument, field: str) -> float | int | instrument:平台内的标的代码,如 000001.SZ 或 rb2501.SHF 等 |
BigTrader 其它说明
股票/基金回测/模拟交易时,如何处理除权除息:
现在平台均使用复权因子来处理,即发现当天和前一天的复权因子发生变化,会得到复权因子的变化比率,来调整持仓数量和持仓价格
发生除权除息后,交易引擎会生成一条成交记录,成交记录中有成交数量(转送股数量)或成交金额(分红金额)等值
对于股票退市或期货合约到期的处理
股票会在退市日期,按最新价格自动平仓,会生成一条平仓成交记录,成交金额返还至账户资金,成交时间为00:00:00,也会生成一条 expire 开头的日志
期货会在到期日期,按最新价格自动平仓,会生成一条平仓成交记录,释放的保证金和平仓盈亏返还至账户资金,成交时间为00:00:00,也会生成一条 expire 开头的日志
交易引擎的撮合逻辑:
日频撮合:买入时>最低价,卖出时<最高价 则成交,成交参考价格取决于回测模块指定的 open / close,成交数量=min(委托数量,Bar的成交量*成交比例volume_limit)
分钟撮合:买入时>最低价,卖出时<最高价 则成交,成交参考价格为下一分钟的 open,成交数量会依次撮合至收盘或订单完全成交
Tick撮合:使用盘后撮合,买入时>=卖一价,卖出时<=买一价 则成交,成交价格依次从1档到5档,直至订单全部成交
日频回测时,在 handle_data 中下单,会在下一日撮合成交
因为日频回测时, handle_data 已表示当天已经收盘,策略收到了当天完整的K线数据,若产生了交易信号,当天已不能交易,因为订单请求会暂存至下一天发送出去撮合
回测时,当天未成交的委托会在当天盘后自动清空,不会移到下一日继续撮合。注意:日频回测当日新下的订单,不属于未成交委托,因为单子还未发到模拟撮合引擎里
高频回测交易时,为什么每天都需要在 before_trading_start 回调函数里订阅行情:
在回测时,由于高频数据存储在dai数据平台里,高频数据量特别大,回放行情模块需要知道当天需要回放哪些代码+什么频率的数据,数据可能分布在不同的表中,需要模拟实盘一样按时间先后排序回放行情,以最大仿真实盘中你的策略在交易时间段不停的收到一笔笔行情数据
在实盘时,实时行情服务器在远端,策略需要也将策略关心的代码列表订阅发送给实时行情服务器,然后才能收到实时行情主推,比如当收到一笔Tick行情主推时,交易引擎会立即回调策略的 handle_tick(context, tick) 函数
如何自定义分析回测详细交易结果:
回测模拟运行完成时,会返回一个 raw_perf 是 DataFrame 类型,里面统计了每天的详细交易信号,还包括每日持仓,每日委托,每日成交
如何提升你的策略代码运行效率:
如果是当日静态不变的数据,可以在 before_trading 里提前准备好
尽量不要在回测过程中,在 handle_data / handle_tick 里总是生成 pd.Series 或 pd.DataFrame 来做一些计算,因为它们是非常耗时的操作,建议多使用 numpy 相关接口来实现
关于策略需要变量持久化的注意:
变量初始化一般在 initialize() 函数里进行,但是由于模拟交易每天运行时都会再触发 initialize() 函数,因此策略应该在初始化变量时应该先判断变量名是否在 context.user_store 字典中,不存在时再设置初始值。否则可能出现在回测和模拟交易时策略触发信号的日期不一样。
也可以在 initialize() 里调用 context.user_store.init_once(k1=v1, k2=v2) 方式初始化变量,内部会判断变量是否已存在,不存在会设置初始值,存在则略过。
BigTrader 的数据对象
资金数据 FundData(TradingAccount):
资金账户属性 | 属性含义 | 补充说明 |
|---|---|---|
account_id: str | 资金账号 | |
balance: float | 总资金 | 可用资金 + 冻结资金 |
available: float | 可用资金 | |
frozen_cash: float | 冻结资金 | |
portfolio_value: float | 总资产 | 股票=总资金+总市值期货=总资金+总保证金+持仓盈亏 |
total_market_value: float | 总市值 | 股票/基金/债券/期权 |
total_margin: float | 总保证金 | 期货 / 期权卖方 |
commission: float | 当日总交易费用 | |
positions_pnl: float | 持仓盈亏 |
持仓数据 PositionData:
持仓属性 | 属性含义 | 补充说明 |
|---|---|---|
instrument: str | 持仓代码 | |
posi_direction: str | 持仓方向 | '1'-多头,'2'-空头 |
current_qty: int | 持仓数量 | |
avail_qty: int | 可用数量 | |
cost_price: float | 持仓均价 | |
today_qty: int | 今仓数量 | |
margin: float | 保证金占用 | |
last_price: float | 最新价 | |
open_date: int | 开仓日期 | YYYYmmdd |
open_price: float | 开仓均价 |
委托数据 OrderData:
委托属性 | 属性含义 | 补充说明 |
|---|---|---|
instrument: str | 持仓代码 | |
direction: Direction | 持仓方向 | Direction.BUY / SELL |
offset_flag: OffsetFlag | 开平标志 | OffsetFlag.OPEN/CLOSE/CLOSETODAY |
order_type: OrderType | 委托类型 | OrderType.LIMIT/MARKET/MARKET_FOK |
order_qty: int | 委托数量 | |
filled_qty: int | 成交数量 | |
order_price: float | 委托均价 | |
order_status: OrderStatus | 委托状态 | 见下 OrderStatus 字典 |
order_sysid: str | 系统报单编号 | |
order_key: str | 本地唯一单号 | |
user_id: str | 操作员代码 | |
insert_date: int | 报单日期 | YYYYmmdd |
order_time: int | 报单时间 | HHMMSSmmm |
trading_day: int | 交易日 | YYYYmmdd |
status_msg: str | 报单状态消息 |
成交数据 TradeData:
成交属性 | 属性含义 | 补充说明 |
|---|---|---|
instrument: str | 持仓代码 | |
direction: Direction | 持仓方向 | Direction.BUY / SELL |
offset_flag: OffsetFlag | 开平标志 | OffsetFlag.OPEN/CLOSE/CLOSETODAY |
trade_type: TradeType | 成交类型 | TradeType类型,’0’-普通成交,’1’-期权行权成交,’D’-除权除息,’E’-退市平仓,’F’-强平,‘R’-直接还款还券 |
filled_qty: int | 成交数量 | |
filled_price: float | 成交价格 | |
filled_money: float | 成交金额 | |
trade_id: str | 成交编号 | |
order_sysid: str | 系统报单编号 | |
order_key: str | 本地唯一单号 | |
user_id: str | 操作员代码 | |
trade_date: int | 成交日期 | YYYYmmdd |
trade_time: int | 成交时间 | HHMMSSmmm |
trading_day: int | 交易日 | YYYYmmdd |
Tick 数据,股票Level1行情只有5档(Level2为10档,均3秒更新一次),期货Level1行情只有1档(500毫秒更新一次,Level2为5档,有部分交易所250毫秒更新一次)
Tick属性 | 属性含义 | 补充说明 |
|---|---|---|
instrument: str | 持仓代码 | |
datetime: datetime | tick日期时间 | |
time: int | tick时间 | HHMMSSmmm |
last_price: float | 最新价格 | |
open_price: float | 开盘价格 | |
high_price: float | 最高价格 | |
low_price: float | 最低价格 | |
volume: int | 成交量 | |
amount: float | 成交额 | |
open_interest: int | 持仓量 | |
pre_open_interest: int | 昨持仓量 | |
pre_close: float | 昨收盘 | |
upper_limit: float | 涨停价 | |
lower_limit: float | 跌停价 | |
deal_number: int | 股票成交笔数 | |
iopv: float | 基金估值 | |
ask_volumeX | 叫卖量 | X: 1~10 |
ask_priceX | 叫卖价 | X: 1~10 |
bid_priceX | 叫买价 | X: 1~10 |
bid_volumeX | 叫买量 | X: 1~10 |
BigTrader 的数据字典
买卖方向:Direction.BUY='1',SELL='2',FinancingBuy='3',SellRepay='4',LoanSell='5',BuyRedeliver='6'
开平标志:OffsetFlag.OPEN='0',CLOSE='1',CLOSE_TODAY='2',CLOSE_YESTERDAY='3'
委托类型:OrderType.LIMIT='0',MARKET='U'
委托状态:OrderStatus
委托状态 | 状态值 | 状态说明 |
|---|---|---|
OrderStatus.NOTTRADED | 0 | 未成交 |
PARTTRADED | 1 | 部分成交 |
ALLTRADED | 2 | 全部成交 |
PARTCANCELLED | 3 | 部分撤单 |
CANCELLED | 4 | 全部撤单 |
REJECTED | 5 | 废单 |
UNKNOWN | 6 | 未知 |
NOTPLACE | 10 | 未报 |
PLACING | 11 | 正报 |
PENDINGPLACE | 12 | 待报 |
PARTPENDINGPLACE | 15 | 部分待撤 |
PENDINGPLACE | 16 | 待撤销 |
撮合与策略运行结果说明
订单撮合处理
本章节主要介绍基于历史行情的模拟撮合介绍,主要包括基于Bar行情、快照行情、逐笔行情撮合。支持上交所和深交所两大交易所上市的A股股票、基金、债券、期权、期货等品种。不支持新股申购、市值配售、增发申购、配股等交易。股票交易费用买入按0.03%计算,卖出按0.13%计算(其中默认包含0.1%的印花税),期货则按对应的品种费率计算,实际费率也可在回测模拟中设置。
通用规则
对于没有成交的委托,或者部分成交的委托,可以撒单。当天的委托如果没有成交,收市以后自动作废,不参加下一交易日的撮合。
对于市价委托,未成交部分会自动撤销(即成剩撤)。
基于Bar行情数据撮合规则
成交参考价格可指定为open/close,但是一般分钟回测时,成交价为下单后的第一分钟开盘价或者在之后分钟撮合时为订单限价。
成交数量最大为当次bar的成交量,日线是一般还会加一个成交率比例。
委托量过大时,可能出现模拟撮合结果与真实情况严重失真的情况。
基于快照行情数据的撮合规则
主要原则是基于行情中的最新价撮合,而不是买卖盘口的价格撮合,发出订单后,使用下一笔快照行情撮合。
买入
如果最新成交价等于委托价,按照委托价成交。
如果最新成交价低于委托价,按照最新价成交。
若成交价在买一价或涨停时,不能即时成交,委托会放入撮合等待队列,并且记录当时买一量,如果阶段成交量大于买一量,可成交数量是阶段成交量和当时的买一量的差,以这种方式模拟在真实交易市场排队的情形。但未及时考虑买一上的撤单量。
如果涨停板被打开,价格低于委托价,则按照现价成交。
卖出
如果最新成交价等于委托价,按照委托价成交
如果最新价高于委托价,按照最新价撮合成交 注意,若成交价在卖一价或跌停时,不能即时成交,委托会放入撮合等待队列,并且记录当时卖一量,如果阶段成交量大于卖一量,可成交数量是阶段成交量和当时的卖一量的差,以这种方式模拟在真实交易市场排队的情形。但未及时考虑卖一上的撤单量。
如果跌停板被打开,价格高于委托价,则按照现价成交。 当次成交量按两个快照之间的真实成交量计算,若成交量为0,则不成交。如果真实成交数量小于委托未成交数量,则部分成交,仅撮合真实交易的成交数量,剩余的委托仍保留在撮合队列,等待新的成交明细。因此早上集合竞价期间的报单,会在 09:25:00 进行一次撮合,或 09:30:00开始进行连续竞价撮合。
例如:600804(鹏博士)上午开市后涨停,用户在10:10以涨停价委托买入100手,此时的成交量是51000手,涨停板上买一的单子是5000手,如果涨停板没有被打开,只有阶段成交量大于5000手时,用户的委托才等到可以成交。如果成交量到了56010手,则用户成交10手(56010-51000-5000),剩下的部分等待更多的成量。
基于逐笔成交数据的撮合规则
主要原则是基于逐笔行情中的最新成交价撮合,发出订单后,使用下一笔逐笔行情撮合,需要配合当前快照盘口信息(会在收到委托时,获取当前盘口)。
买入委托
市价委托
当前在涨停版上,不成交,自动撤销
成交,即成剩余,成交量也依次从卖一到卖五档
不支持FOK
委托价格 > 最新价:
成交,成交价=最新价,成交量依次从卖一到卖十档,生成多笔成交信息,FIXME:如果超出卖十档的处理,其中价格触发到涨停板的处理。
委托价格 == 最新价
成交类型主动卖,判断当前记录的排队数量,排队数量 <= 0,则成交,成交量为当次逐笔成交量,FIXME: 更逼真模式是取 逐笔成交-排队数量
成交类型主动买,不成交
撤单成交(仅适用于深交所)
撤单价格 == 买一价,对订单队列中的买入委托的排队数量做减少
其它撤单,不做处理
其它不成交
卖出委托:
市价委托:
当前在跌停版上,不成交,自动撤销
成交,即成剩余,成交量也依次从买一到买五档
不支持FOK
委托价格 < 最新价:
成交,成交价=最新价,成交量依次从买一到买十档,生成多笔成交信息,FIXME:如果超出买十档的处理,其中价格触发到跌停板的处理。
委托价格 == 最新价:
成交类型主动买,判断当前记录的排队数量,排队数量 <= 0,则成交,成交量为当次逐笔成交量,FIXME: 更逼真模式是取 逐笔成交-排队数量
成交类型主动卖,不成交
撤单成交(仅适用于深交所)
撤单价格 == 卖一价,对订单队列中的买入委托的排队数量做减少
其它撤单,不做处理 其它不成交
回测结果分析
当我们完成一个策略回测时,我们会得到如下的一个图形:
上图为策略回测结果图,红色矩形标记部分包含了策略的主要信息,包括 收益概况、交易详情、每日持仓及收益、输出日志 。接下来,我们详细介绍这几个部分。
收益概况
收益概况以折线图的方式显示了策略在时间序列上的收益率,黄色曲线为策略收益率。同时也显示了沪深300收益率曲线作为比较基准,蓝色曲线为基准收益率。同时,最下面的绿色曲线为持仓占比,持仓占比即仓位,10%的持仓占比表示账户里股票价值只占10%。相对收益率的曲线并没有直接绘制在图上,点击图例 相对收益率,就可以将其绘制出来。
不仅如此,衡量一个策略好坏的关键指标在收益概览页面也得到展示。
收益率:策略整个回测时间段上的总收益率。比如,如果收益率为30%,表明起始时间是1万的本金,结束时间本金就变成1.3万了,一共赚了3000元。
年化收益率:该策略每一年的收益率。比如,如果回测时间段为2年,总收益率为30%,那么每年的年化收益率就在15附近(不考虑复利)。
基准收益率:策略需要有一个比较基准,比较基准为沪深300。若基准收益率为15%,表明在整个回测时间段,大盘本身就上涨了15%,如果策略收益率小于基准收益率,说明策略表现并不好,连大盘都没有跑赢。
阿尔法:衡量策略的一个重要指标,该值越大越好。
贝塔:衡量策略的一个重要指标,该值越小越好。
夏普比率:衡量策略最重要的一个指标,该指标的计算不仅考虑收益率,还考虑了风险,因此比较具有参考价值,可以理解为经过风险调整后的收益率。
胜率:衡量策略盈利一指标,胜率越大越好。比如10次投资中有8次获利,胜率就是80%。
盈亏比:衡量策略盈亏能力大小比较,盈亏比越大越好。比如投资盈利时,平均每次盈利4元,亏损时,平均每次亏损2元,那么此时的盈亏比为2。
收益波动率:收益率的标准差,是风险的一个指标。
最大回撤:策略在整个时间段上亏损最严重的时候相比净值最高值下跌的百分比。如果最大回撤为20%,表明策略在某个时间点上,相比之前的净值最高点下降了20%。最大回撤是策略评估时非常关键的一个指标,通常与风险承受能力相关。
信息比率:信息比率也是一个常用的策略评价指标
关于回测结果和指标更详细的分析可参照链接:策略回测结果指标详解
交易详情
交易详情主要显示了策略在整个回测过程中每个交易日的买卖信息。包括买卖时间、股票代码、交易方向、交易数量、成交价格、交易成本。具体见下图:
每日持仓及收益
每日持仓及收益主要呈现每日持有股票代码、当日收盘价、持仓股票数量、持仓金额、收益等指标。具体见下图: