撮合成交

载入CTA策略以及相关历史数据后,策略会根据最新的数据来计算相关指标。若符合条件会生成交易信号,发出具体委托(buy/sell/short/cover),并且在下一根K线成交。

根据委托类型的不同,回测引擎提供2种撮合成交机制来尽量模仿真实交易环节:

  • 限价单撮合成交:(以买入方向为例)先确定是否发生成交,成交标准为委托价>= 下一根K线的最低价;然后确定成交价格,成交价格为委托价与下一根K线开盘价的最小值。

  • 停止单撮合成交:(以买入方向为例)先确定是否发生成交,成交标准为委托价<= 下一根K线的最高价;然后确定成交价格,成交价格为委托价与下一根K线开盘价的最大值。

下面展示在引擎中限价单撮合成交的流程:

  • 确定会撮合成交的价格;

  • 遍历限价单字典中的所有限价单,推送委托进入未成交队列的更新状态;

  • 判断成交状态,若出现成交,推送成交数据和委托数据;

  • 从字典中删除已成交的限价单。

  1. def cross_limit_order(self):
  2. """
  3. Cross limit order with last bar/tick data.
  4. """
  5. if self.mode == BacktestingMode.BAR:
  6. long_cross_price = self.bar.low_price
  7. short_cross_price = self.bar.high_price
  8. long_best_price = self.bar.open_price
  9. short_best_price = self.bar.open_price
  10. else:
  11. long_cross_price = self.tick.ask_price_1
  12. short_cross_price = self.tick.bid_price_1
  13. long_best_price = long_cross_price
  14. short_best_price = short_cross_price
  15.  
  16. for order in list(self.active_limit_orders.values()):
  17. # Push order update with status "not traded" (pending)
  18. if order.status == Status.SUBMITTING:
  19. order.status = Status.NOTTRADED
  20. self.strategy.on_order(order)
  21.  
  22. # Check whether limit orders can be filled.
  23. long_cross = (
  24. order.direction == Direction.LONG
  25. and order.price >= long_cross_price
  26. and long_cross_price > 0
  27. )
  28.  
  29. short_cross = (
  30. order.direction == Direction.SHORT
  31. and order.price <= short_cross_price
  32. and short_cross_price > 0
  33. )
  34.  
  35. if not long_cross and not short_cross:
  36. continue
  37.  
  38. # Push order udpate with status "all traded" (filled).
  39. order.traded = order.volume
  40. order.status = Status.ALLTRADED
  41. self.strategy.on_order(order)
  42.  
  43. self.active_limit_orders.pop(order.vt_orderid)
  44.  
  45. # Push trade update
  46. self.trade_count += 1
  47.  
  48. if long_cross:
  49. trade_price = min(order.price, long_best_price)
  50. pos_change = order.volume
  51. else:
  52. trade_price = max(order.price, short_best_price)
  53. pos_change = -order.volume
  54.  
  55. trade = TradeData(
  56. symbol=order.symbol,
  57. exchange=order.exchange,
  58. orderid=order.orderid,
  59. tradeid=str(self.trade_count),
  60. direction=order.direction,
  61. offset=order.offset,
  62. price=trade_price,
  63. volume=order.volume,
  64. time=self.datetime.strftime("%H:%M:%S"),
  65. gateway_name=self.gateway_name,
  66. )
  67. trade.datetime = self.datetime
  68.  
  69. self.strategy.pos += pos_change
  70. self.strategy.on_trade(trade)
  71.  
  72. self.trades[trade.vt_tradeid] = trade