第 8 章 · 01 订单类型与提交 本节摘要:本节钻取 Lean 的第 8 层入口——订单类型与订单提交链路。 目录是 Lean 最大的领域模型集中地之一(约 1.5 万行、80+ 个 .cs 文件),覆盖订单类型(Market 市价/Limit 限价/StopMarket 止损市价/StopLimit 止损限价/Combo 组合/OptionExercise 期权行权/MarketOnOpen 开盘市价/MarketOnClose 收盘市价)、订单属性(IOrderProperties,内含 TimeInForce/Exchange)、订单票据(OrderTicket,跟踪 Status/FilledQuantity/AverageFillPrice
本节摘要:本节钻取 Lean 的第 8 层入口——订单类型与订单提交链路。
Common/Orders目录是 Lean 最大的领域模型集中地之一(约 1.5 万行、80+ 个 .cs 文件),覆盖订单类型(Market 市价/Limit 限价/StopMarket 止损市价/StopLimit 止损限价/Combo 组合/OptionExercise 期权行权/MarketOnOpen 开盘市价/MarketOnClose 收盘市价)、订单属性(IOrderProperties,内含 TimeInForce/Exchange)、订单票据(OrderTicket,跟踪 Status/FilledQuantity/AverageFillPrice 并支持查询/修改/取消)、订单序列化(OrderJsonConverter + Serialization/)。订单从用户代码到 TransactionHandler 的链路为:QCAlgorithm.Trading的便捷方法(MarketOrder/LimitOrder/SetHoldings)→ 构造SubmitOrderRequest→PreOrderChecks预检(资金/市场/数量/手数)→Transactions.AddOrder进入事务管理器 → 由 TransactionHandler 落地。读完本节,你掌握了 Lean 订单体系的骨架与下单链路。
内容来源:原项目源码
Common/Orders/Order.cs、MarketOrder.cs、LimitOrder.cs、StopMarketOrder.cs、StopLimitOrder.cs、ComboOrder.cs、OptionExerciseOrder.cs、MarketOnOpenOrder.cs、MarketOnCloseOrder.cs、OrderProperties.cs、OrderTicket.cs、Algorithm/QCAlgorithm.Trading.cs,精读并套用体系化模板。
⚠️ 注意:本节聚焦"订单是什么 + 怎么提交",成交怎么算(ImmediateFillModel/PartialFillModel)在下一节,手续费/滑点/TimeInForce 在第 03 节。三者共同构成"订单生命周期"。
阅读完本节,你应当能够:
Order 抽象基类的统一字段与 CreateOrder 工厂。SubmitOrderRequest → PreOrderChecks → AddOrder。IOrderProperties 与 OrderTicket 的作用。SetHoldings(按百分比)与 MarketOrder(按绝对数量)。Common/Orders/ 下 80+ 个 .cs 文件,按职责分四组:
| 分组 | 代表文件 |
|---|---|
| 订单类型 | Order.cs(基类)、MarketOrder.cs、LimitOrder.cs、StopMarketOrder.cs、StopLimitOrder.cs、TrailingStopOrder.cs、LimitIfTouchedOrder.cs、MarketOnOpenOrder.cs、MarketOnCloseOrder.cs、OptionExerciseOrder.cs、ComboOrder.cs/ComboMarketOrder.cs/ComboLimitOrder.cs/ComboLegLimitOrder.cs |
| 订单请求/响应 | SubmitOrderRequest.cs、UpdateOrderRequest.cs、CancelOrderRequest.cs、OrderResponse.cs、OrderError.cs |
| 订单票据/事件 | OrderTicket.cs、OrderEvent.cs、OrderUpdateEvent.cs |
| 属性/序列化 | OrderProperties.cs、AlpacaOrderProperties.cs 等 20+ 券商专属属性、OrderJsonConverter.cs、Serialization/ |
另有三个子目录:Fills/(成交模型,下节讲)、Fees/(手续费,第 03 节)、Slippage/(滑点,第 03 节)、TimeInForces/(有效期,第 03 节)、OptionExercise/(行权模型,第 03 节)。命名规律清晰——文件名即类型名。
Common/Orders/Order.cs:31(所有订单类型的基类):
31 public abstract class Order 33 private volatile int _incrementalId; 41 public int Id { get; internal set; } // 订单ID 62 public int ContingentId { get; internal set; } // 依赖订单(先处理后处理本单) 68 public List<string> BrokerId { get; internal set; } // 券商侧ID(可能多个) 74 public Symbol Symbol { get; internal set; } // 标的 80 public decimal Price { get; internal set; } // 订单价格(成交后回填均价) 96 public DateTime Time { get; internal set; } // 创建时间(UTC) 108 public DateTime? LastFillTime { get; internal set; } // 最近成交时间 114 public DateTime? LastUpdateTime { get; internal set; } 120 public DateTime? CanceledTime { get; internal set; } 126 public virtual decimal Quantity { get; internal set; }// 数量(正买负卖) 136 public abstract OrderType Type { get; } // 订单类型(子类实现) 142 public OrderStatus Status { get; set; } // 状态 148 public TimeInForce TimeInForce => Properties.TimeInForce; 160 public IOrderProperties Properties { get; private set; } // 订单属性 172 public OrderDirection Direction // 方向(由Quantity符号决定) 176 if (Quantity > 0) return OrderDirection.Buy; 180 if (Quantity < 0) return OrderDirection.Sell; 184 return OrderDirection.Hold;
两个抽象成员决定了"子类必须实现什么":Type(返回 OrderType 枚举)和 Clone()(深拷贝,用于回测快照)。GetValueImpl(security) 也是抽象的——每种订单算"订单价值"的方式不同(市价单按当前价,限价单按限价)。
Order.cs:413 的静态工厂 CreateOrder(SubmitOrderRequest) 是订单构造的总入口,内部 switch 按 OrderType 实例化对应子类:
413 public static Order CreateOrder(SubmitOrderRequest request) 420 private static Order CreateOrder(int orderId, OrderType type, ...) 425 switch (type) 427 case OrderType.Market: order = new MarketOrder(...); break; 431 case OrderType.Limit: order = new LimitOrder(...); break; 435 case OrderType.StopMarket:order = new StopMarketOrder(...);break; 439 case OrderType.StopLimit: order = new StopLimitOrder(...);break; 443 case OrderType.TrailingStop: order = new TrailingStopOrder(...);break; 451 case OrderType.MarketOnOpen: order = new MarketOnOpenOrder(...);break; 455 case OrderType.MarketOnClose: order = new MarketOnCloseOrder(...);break; 459 case OrderType.OptionExercise:order = new OptionExerciseOrder(...);break; 463 case OrderType.ComboLimit: order = new ComboLimitOrder(...);break; 471 case OrderType.ComboMarket: order = new ComboMarketOrder(...);break; 478 order.Status = OrderStatus.New; 479 order.Id = orderId;
每种订单类型都是 Order 的子类,只多出"自己专属的字段":
| 类型 | 类名 | 专属字段 | 成交逻辑(下节详述) |
|---|---|---|---|
| 市价 | MarketOrder |
无 | 立即按当前价全部成交 |
| 限价 | LimitOrder |
LimitPrice |
价格触及限价或更好时成交 |
| 止损市价 | StopMarketOrder |
StopPrice |
价格触及止损价后按市价成交 |
| 止损限价 | StopLimitOrder |
StopPrice+LimitPrice+StopTriggered |
触发后转成限价单 |
| 追踪止损 | TrailingStopOrder |
TrailingAmount+TrailingAsPercentage |
止损价随市场价自动跟踪 |
| 触价限价 | LimitIfTouchedOrder |
TriggerPrice+LimitPrice |
触及触发价后挂限价单 |
| 开盘市价 | MarketOnOpenOrder |
无 | 下一交易日开盘成交 |
| 收盘市价 | MarketOnCloseOrder |
SubmissionTimeBuffer |
当日收盘成交(需提前 15.5 分钟) |
| 期权行权 | OptionExerciseOrder |
无(数量为正) | 行权产生标的买卖 |
| 组合市价 | ComboMarketOrder |
GroupOrderManager |
多腿组合按市价 |
| 组合限价 | ComboLimitOrder |
GroupOrderManager+LimitPrice |
整组合按一个限价 |
看 LimitOrder.cs:26:
26 public class LimitOrder : Order 32 public decimal LimitPrice { get; internal set; } // 限价 37 public override OrderType Type => OrderType.Limit; 58 public LimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, DateTime time, ...) 61 LimitPrice = limitPrice;
StopMarketOrder.cs:26(止损市价,只多一个 StopPrice):
26 public class StopMarketOrder : Order 32 public decimal StopPrice { get; internal set; } // 止损触发价 37 public override OrderType Type => OrderType.StopMarket; 58 public StopMarketOrder(Symbol symbol, decimal quantity, decimal stopPrice, DateTime time, ...) 61 StopPrice = stopPrice;
StopLimitOrder.cs:26(止损限价,既有 StopPrice 又有 LimitPrice,还多一个 StopTriggered 状态位记录"是否已触发"):
32 public decimal StopPrice { get; internal set; } 38 public bool StopTriggered { get; internal set; } // 是否已转为限价单 44 public decimal LimitPrice { get; internal set; } 49 public override OrderType Type => OrderType.StopLimit;
OptionExerciseOrder.cs:26(期权行权,字段最少——只靠 Symbol 携带期权合约信息,Quantity 表示行权合约数):
26 public class OptionExerciseOrder : Order 44 public OptionExerciseOrder(Symbol symbol, decimal quantity, DateTime time, ...) 52 public override OrderType Type => OrderType.OptionExercise;
ComboOrder.cs:24(组合单基类,引入 GroupOrderManager 管理多腿,Quantity 不是绝对数量而是"腿比例 × 组合总量"):
24 public abstract class ComboOrder : Order 34 public override decimal Quantity 36 get => _ratio.GetOrderLegGroupQuantity(GroupOrderManager).Normalize(); 40 internal set => _ratio = value.GetOrderLegRatio(GroupOrderManager);
💡 钻取要点:
Order.IsMarketable(Order.cs:210)是个聪明属性——市价单与组合市价单永远可成交,限价单则要看"限价是否已穿越当前买卖盘"(买单LimitPrice >= AskPrice、卖单LimitPrice <= BidPrice)。这个判断用OrderSubmissionData记录的提交时刻报价,而不是实时报价,避免后续行情变化导致判断漂移。
用户不直接 new Order,而是调 Algorithm/QCAlgorithm.Trading.cs 的便捷方法。最常用的是 MarketOrder(QCAlgorithm.Trading.cs:241):
241 public OrderTicket MarketOrder(Symbol symbol, decimal quantity, bool asynchronous = false, string tag = "", IOrderProperties orderProperties = null) 243 var security = GetSecurityForOrder(symbol); 246 if (security.Type != SecurityType.Future && security.Type != SecurityType.FutureOption) 250 if (!security.Exchange.ExchangeOpen) // 市场关闭 252 var mooTicket = MarketOnOpenOrder(security.Symbol, quantity, ...); // 自动转 MOO 258 return mooTicket; 268 if (!LiveMode && !security.Exchange.Hours.IsMarketAlwaysOpen && IsDailyResolutionOnly(security.Symbol)) // 仅日线订阅 270 var convertedTicket = IsWithinMarketOnCloseSubmissionBuffer(security) 271 ? MarketOnOpenOrder(...) // 接近收盘转 MOO 272 : MarketOnCloseOrder(...); // 否则转 MOC 283 var request = CreateSubmitOrderRequest(OrderType.Market, security, quantity, tag, orderProperties ?? DefaultOrderProperties?.Clone(), asynchronous); 286 var ticket = SubmitOrderRequest(request);
注意 250-258 行的智能转换:市场关闭时,市价单自动转成 MarketOnOpenOrder(避免成交在陈旧价);268-280 行:仅日线订阅时,市价单转成 MarketOnCloseOrder(避免成交在昨日收盘价)。这些"自动转换"是 Lean 体贴用户的细节。
Order(symbol, quantity)(QCAlgorithm.Trading.cs:180)是 MarketOrder 的别名:
180 public OrderTicket Order(Symbol symbol, decimal quantity) 182 return MarketOrder(symbol, quantity);
LimitOrder(QCAlgorithm.Trading.cs:473)与 StopMarketOrder(QCAlgorithm.Trading.cs:525)模式一致:取 security → CreateSubmitOrderRequest → SubmitOrderRequest:
473 public OrderTicket LimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, ...) 476 var request = CreateSubmitOrderRequest(OrderType.Limit, security, quantity, tag, 477 orderProperties ?? DefaultOrderProperties?.Clone(), asynchronous, limitPrice: limitPrice); 479 return SubmitOrderRequest(request); 525 public OrderTicket StopMarketOrder(Symbol symbol, decimal quantity, decimal stopPrice, ...) 528 var request = CreateSubmitOrderRequest(OrderType.StopMarket, security, quantity, tag, 529 orderProperties ?? DefaultOrderProperties?.Clone(), asynchronous, stopPrice: stopPrice); 531 return SubmitOrderRequest(request);
SetHoldings(QCAlgorithm.Trading.cs:1547)按"占总资产百分比"下单,内部先调 CalculateOrderQuantity 把百分比换算成绝对数量,再走市价链路:
1547 public List<OrderTicket> SetHoldings(Symbol symbol, decimal percentage, bool liquidateExistingHoldings = false, ...) 1549 return SetHoldingsImpl(symbol, CalculateOrderQuantity(symbol, percentage), liquidateExistingHoldings, asynchronous, tag, orderProperties);
💡 钻取要点:
SetHoldings("AAPL", 0.1)表示"把 AAPL 调仓到占总资产 10%";SetHoldings("AAPL", 2)表示 2 倍杠杆满仓。它内部用CalculateOrderQuantity反算"还需要买卖多少股"(目标持仓 - 当前持仓),如果liquidateExistingHoldings: true还会先清空其他持仓。这是"目标仓位"语义,与MarketOrder的"增量数量"语义截然不同。
所有便捷方法都汇聚到 CreateSubmitOrderRequest(QCAlgorithm.Trading.cs:1708)与 SubmitOrderRequest(QCAlgorithm.Trading.cs:1018):
1708 private SubmitOrderRequest CreateSubmitOrderRequest(OrderType orderType, Security security, decimal quantity, string tag, IOrderProperties properties, bool asynchronous, decimal stopPrice = 0m, decimal limitPrice = 0m, ...) 1712 return new SubmitOrderRequest(orderType, security.Type, security.Symbol, quantity, stopPrice, limitPrice, triggerPrice, trailingAmount, trailingAsPercentage, UtcTime, tag, properties, groupOrderManager, asynchronous); 1018 public OrderTicket SubmitOrderRequest(SubmitOrderRequest request) 1020 var response = PreOrderChecks(request); // 预检 1021 if (response.IsError) return OrderTicket.InvalidSubmitRequest(Transactions, request, response); 1027 return Transactions.AddOrder(request); // 进入事务管理器
PreOrderChecksImpl(QCAlgorithm.Trading.cs:1050)是下单的"安全阀":
1052 if (IsWarmingUp) return OrderResponse.WarmingUp(request); // 预热期禁下单 1060 if (!Securities.TryGetValue(request.Symbol, out security)) 1062 return OrderResponse.MissingSecurity(request); // 标的未订阅 1066 if (request.Quantity == 0) return OrderResponse.ZeroQuantity(request); // 数量为0 1071 if (Math.Abs(request.Quantity) < security.SymbolProperties.LotSize) 1073 return OrderResponse.Error(request, OrderResponseErrorCode.OrderQuantityLessThanLotSize, ...);
四道关卡:① 预热期(IsWarmingUp)② 标的已订阅 ③ 数量非零 ④ 数量不少于最小手数(LotSize)。任一不过直接返回错误票据,不进入事务管理器。通过后调 Transactions.AddOrder(request),由 SecurityTransactionManager 把请求交给第 3 章的 ITransactionHandler(回测走 BacktestingTransactionHandler,实盘走 BrokerageTransactionHandler)落地。
OrderProperties.cs:24(订单属性,默认实现):
24 public class OrderProperties : IOrderProperties 29 public TimeInForce TimeInForce { get; set; } // 有效期(GTC/Day/GTD) 35 public Exchange Exchange { get; set; } // 交易所 42 TimeInForce = TimeInForce.GoodTilCanceled; // 默认 GTC
除了通用字段,每个券商都有专属属性类(InteractiveBrokersOrderProperties、BinanceOrderProperties、IndiaOrderProperties...),用来塞券商特殊参数(如 IB 的 OutsideRegularTradingHours、币安的 IsPostOnly)。订单通过 Order.Properties 持有它,TimeInForce 也挂在 Properties 上。
OrderTicket(OrderTicket.cs:29)是用户手里的"订单回执",实时跟踪订单状态:
29 public sealed class OrderTicket 53 public int OrderId { get => _submitRequest.OrderId; } 61 public OrderStatus Status { get => _order == null ? OrderStatus.New : _order.Status; } 98 public decimal AverageFillPrice { get => _fillState.AverageFillPrice; } // 平均成交价 110 public decimal QuantityFilled { get => _fillState.QuantityFilled; } // 已成交数量
Ticket 不是订单本身的拷贝,而是一个实时视图——它持有 _order 引用和 _fillState,订单在引擎内部状态变化时通过 Update(update) 同步刷新 Ticket。用户代码可以:ticket.Status 查状态、ticket.AverageFillPrice 查成交均价、ticket.Update(new UpdateOrderFields{ LimitPrice = ... }) 改单、ticket.Cancel() 撤单。QuantityFilled - SubmitRequest.Quantity 即剩余数量。
订单要持久化(结果文件)和跨进程传输(实盘 API),靠 OrderJsonConverter 把 Order 多态序列化成 JSON——type 字段标记子类,反序列化时按 type 走 CreateOrder 工厂。Serialization/SerializedOrderEvent.cs 是 OrderEvent 的可序列化投影(下节讲 OrderEvent)。
Type 与 Clone,静态工厂 CreateOrder 按 OrderType 实例化子类。CreateSubmitOrderRequest → PreOrderChecksImpl(预热/订阅/数量/手数四关)→ Transactions.AddOrder → TransactionHandler。CalculateOrderQuantity 反算增量,与 MarketOrder 的"绝对数量"语义不同。下一节,我们钻进 Fills 子目录——
ImmediateFillModel/FillModel/PartialFillModel如何用历史 bar 算出成交价,以及OrderEvent成交事件与TradeBuilder交易构建器。