NautilusTrader 回测成交模型Fill Models完全指南从 L1 概率滑点到合成订单簿【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader导读NautilusTrader 的历史数据无法展示一笔模拟订单会如何与其他市场参与者交互。成交模型Fill Model正是用来控制引擎在回测中对限价单成交资格limit-order eligibility、单跳滑点one-tick slippage以及可选合成流动性synthetic liquidity所做假设的机制。读完本文你将掌握 11 种内置成交模型的适用场景与参数语义、如何通过BacktestVenueConfig配置它们、概率参数prob_fill_on_limit与prob_slippage的精确含义以及如何编写自定义成交模型接入低层回测引擎。一、为什么需要成交模型历史数据的盲区在真实市场中一笔限价单的成交取决于当时盘口上是否存在对家流动性、队列位置、对手方行为等大量微观因素。而历史盘口数据只记录了市场当时的快照无法回答如果你的订单当时在场会发生什么。NautilusTrader 的回测匹配引擎matching engine把这种反事实问题交给成交模型来处理。成交模型控制三类核心假设限价单成交资格当市场触及touch但未穿越cross限价时该限价单是否成交单跳滑点L1 盘口下每次成交是否附加一个 tick 的不利价格移动合成流动性是否在真实记录的盘口之外额外提供一层假设的挂单深度。这些逻辑的实现位于 crates/execution/src/models/fill.rs所有模型都实现同一个 Rust traitFillModel它定义了四个方法is_limit_filled()、is_slipped()、fill_limit_inside_spread()以及get_orderbook_for_fill_simulation()。Python 侧的基类nautilus_trader.execution.FillModel提供了这些方法的默认实现便于用户以子类化方式编写自定义模型。二、按盘口类型的差异化行为成交模型的行为因盘口类型book type而异这是理解所有参数的前提。L2 / L3 深度盘口当回测使用 L2 或 L3 数据时记录的盘口本身就包含逐档价格与数量。匹配引擎直接沿这些档位执行prob_fill_on_limit用来建模触及限价是否成交的概率prob_slippage不适用——因为盘口深度本身已经决定了价格冲击price impact不需要再叠加概率滑点。L1 盘口含由 quotes、trades、bars 更新的盘口L1 盘口只暴露最优买卖价best bid/ask此时prob_fill_on_limit决定限价单价格被触及时的成交概率prob_slippage对每一次成交都独立判定与订单类型和流动性方向maker/taker无关一次成功的滑点抽样会使成交价朝订单不利方向移动一个 tick模型可以返回一张合成 L2 盘口以表达最优价之外的流动性。滑点方向示例假设prob_slippage0.5则每笔 BUY 成交都有 50% 的概率在成交价上多付出一个 tick即买得更贵。复现性要求当一次回测运行需要精确复现模型的随机抽样时必须设置random_seed。注意这个种子只控制成交模型内部的随机性不配置引擎其他部分的随机性或执行顺序——这一点在 fill-prices-and-matching.md 中有明确说明。默认行为DefaultFillModel如果一个 venue 没有指定成交模型引擎自动使用DefaultFillModel其默认参数为prob_fill_on_limit1.0、prob_slippage0.0对应源码 crates/execution/src/models/fill.rs 中Default for DefaultFillModel的实现。含义是触及的限价单默认具备成交资格L1 成交默认不附加概率性单跳滑点但这不会禁用匹配引擎对符合条件的市价类订单market-style orders单独执行的剩余量残差成交residual-fill规则——该规则是确定性的与成交模型的概率滑点是两套独立机制详见 fill-prices-and-matching.md。三、内置成交模型全景下表列出 NautilusTrader 当前提供的全部 11 种内置模型及其流动性行为模型定义与构造均可在 crates/execution/src/models/fill.rs 中找到Python 导出见 python/nautilus_trader/execution/init.pyi模型流动性行为DefaultFillModel直接使用匹配引擎记录的盘口不提供合成盘口get_orderbook_for_fill_simulation返回NoneBestPriceFillModel在最优买价与卖价处提供无限量流动性内部以 10,000,000,000 单位的哨兵值表示无限OneTickSlippageFillModel在最优价外一个 tick 处提供无限量流动性ProbabilisticFillModel以 50% 概率选择最优价或差一个 tick 的价格TwoTierFillModel最优价处放 10 个单位其余在一个 tick 之外无限量ThreeTierFillModel在三个档位分别放置 50、30、20 个单位LimitOrderPartialFillModel最优价处放 5 个单位其余在一个 tick 之外无限量SizeAwareFillModel以 10 个单位为阈值改变盘口形态CompetitionAwareFillModel在最优价处暴露 1,000 个单位的可配置比例VolumeSensitiveFillModel在最优价处放置其内部近期成交量的 25%MarketHoursFillModel使用正常或加宽一个 tick 的合成价差逐模型源码解读下面结合源码逐一看每个模型的合成盘口构造逻辑均以instrument.price_increment()作为 tick 单位以instrument.size_precision()作为数量精度DefaultFillModelget_orderbook_for_fill_simulation恒返回None引擎因此回退到记录的原始盘口执行成交。BestPriceFillModel在best_bid与best_ask各放一档无限量挂单同时fill_limit_inside_spread返回true意味着限价单只要处于自身方向的当前最优报价或更优位置BUY ≥ bid、SELL ≤ ask即可视为可成交而不必穿越价差。OneTickSlippageFillModel买盘放在best_bid - tick、卖盘放在best_ask tick强制所有成交都比最优价差一个 tick其fill_limit_inside_spread返回false。ProbabilisticFillModel通过内部 RNG 的random_bool(0.5)掷一次硬币正面则在最优价放无限量、反面则在最优价外一个 tick 放无限量——实现最优价或差一档的 50/50 分布。TwoTierFillModel最优价各放Quantity::new(10.0, size_prec)下一档±1 tick放无限量。前 10 个单位以最优价成交其余部分承受一个 tick 的价格冲击。ThreeTierFillModel三档分别放置 50最优价、30±1 tick、20±2 tick个单位模拟更平滑的深度衰减。LimitOrderPartialFillModel最优价只放 5 个单位剩余需求在 ±1 tick 处无限量成交——适合模拟限价单部分成交后再以更差价格补足剩余量的场景。SizeAwareFillModel以 10 个单位为阈值——小单数量 ≤ 10在最优价获得 50 个单位的流动性大单则先在最优价成交 10 个单位剩余数量在 ±1 tick 处成交模拟大单的价格冲击。CompetitionAwareFillModel最优价处的可用量为max(1000 * liquidity_factor, 1)其中liquidity_factor取值范围[0.0, 1.0]、默认0.3并向下钳制到至少 1 个数量单位源码见 fill.rs。例如liquidity_factor0.5时暴露 500 个单位1.0时暴露完整 1,000 个单位。VolumeSensitiveFillModel内部维护recent_volume近期成交量初始值 1,000最优价处的可用量为max(recent_volume * 0.25, 1)±1 tick 处为无限量。MarketHoursFillModel内部维护is_low_liquidity标志初始false。正常时段在最优价各放 500 个单位低流动性时段把两个档位外移一个 tickbest_bid - tick/best_ask tick模拟价差扩大。使用注意档位数量是模型常量以合约数量单位instrument quantity units表示。例如ThreeTierFillModel的 50/30/20、TwoTierFillModel的 10、CompetitionAwareFillModel的 1,000都不是按标的金额换算的。在使用分层tiered模型之前务必确认这些常量符合标的合约的数量级——对一个每手仅几单位的小品种50 个单位的最优价流动性可能是天文数字。另外当前 Python 绑定不暴露VolumeSensitiveFillModel与MarketHoursFillModel的状态设置方法即 Rust 侧的set_recent_volume与set_low_liquidity_period。从 Python 使用这两个模型时它们保持初始值1,000 个近期成交量单位、正常流动性模式。四、配置方法BacktestVenueConfig高层 API直接传入内置模型对象最直接的配置方式是把内置模型实例直接传给BacktestVenueConfig的fill_model参数from nautilus_trader.config import BacktestVenueConfig from nautilus_trader.execution import DefaultFillModel from nautilus_trader.model import AccountType from nautilus_trader.model import BookType from nautilus_trader.model import OmsType venue BacktestVenueConfig( nameSIM, oms_typeOmsType.NETTING, account_typeAccountType.CASH, book_typeBookType.L1_MBP, starting_balances[100_000 USD], fill_modelDefaultFillModel( prob_fill_on_limit0.2, prob_slippage0.5, random_seed42, ), )合成盘口类模型使用相同的构造参数。例如把DefaultFillModel换成ThreeTierFillModelfrom nautilus_trader.execution import ThreeTierFillModel venue BacktestVenueConfig( nameSIM, oms_typeOmsType.NETTING, account_typeAccountType.CASH, book_typeBookType.L1_MBP, starting_balances[100_000 USD], fill_modelThreeTierFillModel( prob_fill_on_limit1.0, prob_slippage0.0, random_seed42, ), )关于配置解析源码层面的支撑如下Python 侧的fill_model参数会通过 crates/backtest/src/python/config.rs 中的pyobject_to_fill_model_any转换为 Rust 侧的FillModelAny枚举再包装为运行时句柄FillModelHandle见 crates/execution/src/models/fill.rs最终注入匹配引擎。转换过程crates/execution/src/python/fill.rs按类型逐一尝试提取 11 种内置模型如果对象不是任何受支持的内置绑定会抛出类型错误Cannot convert {type_name} to FillModel。注意当前高层 venue 配置BacktestVenueConfig只接受内置成交模型对象不会从 import-path 配置对象加载成交模型。低层引擎接受任意自定义 Python 对象低层BacktestEngine.add_venue()方法同样接受fill_model参数但它可以是一个自定义 Python 对象。该对象必须实现两个方法is_limit_filled() - boolis_slipped() - bool还可以可选实现fill_limit_inside_spread() - boolget_orderbook_for_fill_simulation(instrument, order, best_bid, best_ask) - OrderBook | None这个自定义对象协议仅适用于低层引擎。子类化nautilus_trader.execution.FillModel可以为这些方法提供默认实现Python 绑定PyFillModel的默认实现见 crates/execution/src/python/fill.rsis_limit_filled默认True、is_slipped默认False、fill_limit_inside_spread默认False、get_orderbook_for_fill_simulation默认返回None。底层机制上crates/execution/src/python/fill.rs 中的pyobject_to_fill_model_handle会先尝试内置模型转换失败后检查对象是否具备is_limit_filled与is_slipped两个必需方法然后包装成PythonFillModel桥接到 Rust 的FillModeltraitfill_limit_inside_spread与get_orderbook_for_fill_simulation若缺失则使用默认行为前者返回false后者返回None。五、概率参数详解所有内置模型共享同一组概率参数DefaultFillModel之外其余模型的概率语义相同它们在 Rust 侧由ProbabilisticFillState统一实现见 crates/execution/src/models/fill.rs构造时通过check_in_range_inclusive_f64严格校验两个概率参数必须落在[0.0, 1.0]越界直接返回OutOfRange错误——例如DefaultFillModel(prob_fill_on_limit1.1, ...)会报invalid f64 for prob_fill_on_limit not in range [0, 1], was 1.1该行为有单元测试覆盖设置random_seed时使用StdRng::seed_from_u64(seed)构造确定性随机源否则使用系统随机源概率判定通过event_success实现0.0恒为否、1.0恒为是、中间值按概率抽样。prob_fill_on_limit默认1.0该参数控制限价单价格被市场触及touch但未穿越cross时是否成交0.0触及永不成交0.5平均一半的有效触及成交1.0触及必成交。需要明确的是穿越限价是匹配引擎的另一套独立成交条件订单作为 taker 直接吃掉盘口。prob_fill_on_limit只负责触及但不穿越的场景。若需要显式的队列量跟踪而非概率近似参见 trade-execution.md 中的队列位置跟踪queue_positionTrue。在匹配引擎中限价单的成交判定位于 crates/execution/src/matching_engine/mod.rs 附近引擎先调用self.fill_model.is_limit_filled()返回false时不成交。源码注释还特别指出引擎会避免对同一订单重复调用is_limit_filled()以防概率被二次放大。prob_slippage默认0.0仅对 L1 盘口生效该参数控制每次成交附加一个 tick 不利移动的概率0.0从不附加模型滑点0.5平均一半的成交附加一个 tick1.0每次成交都附加一个 tick。抽样对 maker 和 taker 成交一视同仁不适用于 L2/L3 盘口深度盘口的价格影响由盘口本身决定。匹配引擎中的调用点在 crates/execution/src/matching_engine/mod.rsif self.book_type BookType::L1_MBP self.fill_model.is_slipped()?——即只有 L1 盘口才触发该判定滑点命中后成交价朝订单方向外移一个 tick。需要区分的是这个概率滑点是成交模型的行为与匹配引擎对市价类订单的确定性残差成交规则L1 盘口耗尽后剩余量以更差一个 tick 成交是两套机制后者的行为与price_protection_points价格保护边界共同决定详见 fill-prices-and-matching.md。六、合成订单簿引擎如何与模型协作在判定成交之前匹配引擎会先向成交模型请求一张可选的合成订单簿synthetic order book引擎同时持有best_bid与best_ask时调用fill_model.get_orderbook_for_fill_simulation(instrument, order, best_bid, best_ask)若模型返回一张OrderBook引擎把订单作为BookOrder对这张合成盘口执行simulate_fills以合成盘的档位为准决定成交价与成交量市价单与限价单分别走determine_market_fill_model_price_and_volume与determine_limit_fill_model_price_and_volume见 crates/execution/src/matching_engine/mod.rs若模型返回None引擎回退到记录的原始盘口执行标准成交逻辑。三个关键细节合成盘的流动性语义只有内置模型显式构造的档位是可成交的。例如DefaultFillModel返回None因此完全依赖真实记录盘口BestPriceFillModel返回一张只有最优价一档无限量的 L2 盘口。合成盘不做逐档流动性消耗跟踪liquidity_consumptionTrue只作用于真实记录的盘口档位。对合成模型盘口任何想要的消耗行为都必须由模型自己在返回的盘口中表达例如SizeAwareFillModel根据订单大小改变档位形态本质上就是一种消耗感知的建模。fill_limit_inside_spread影响盘内限价单当模型返回true时如BestPriceFillModel匹配内核把位于自身方向最优报价或更优位置的限价单视为可成交而不仅仅是穿越价差时。该标志在引擎初始化与set_fill_model时同步给匹配内核见 crates/execution/src/matching_engine/mod.rs。七、与流动性消耗及盘口不可变性的配合历史盘口数据在成交后保持不可变。默认情况下liquidity_consumptionFalse同一档位上显示的挂单量可以同时支撑多笔模拟订单成交——这在高频、大单回测中会高估可用流动性。要逐档跟踪已消耗数量设置liquidity_consumptionTrue引擎会记录每个价格档位的原始量与已消耗量可用量 原始量 − 已消耗量直到有新的盘口更新重置该档位的记录。from nautilus_trader.config import BacktestVenueConfig from nautilus_trader.model import AccountType from nautilus_trader.model import BookType from nautilus_trader.model import OmsType venue BacktestVenueConfig( nameSIM, oms_typeOmsType.NETTING, account_typeAccountType.CASH, book_typeBookType.L1_MBP, starting_balances[100_000 USD], liquidity_consumptionTrue, )两点提醒该消耗跟踪是估计值而非队列优先级——它不模拟排队顺序。需要展示队列位置时用queue_positionTrue配合 book 与 trade 数据或者用prob_fill_on_limit做概率近似。正如前文所述合成盘口不参与逐档消耗跟踪如果你在成交模型中构造了合成盘必须在模型内部自行表达流动性消耗假设。更完整的盘口不可变性与消耗语义含 L1 被动单部分成交示例、trade 驱动的消耗共享规则参见 fill-prices-and-matching.md。八、实战选型建议基准对照最乐观BestPriceFillModel——所有订单都在最优价无限量成交适合作为策略逻辑正确性的快速验证不应视为现实结果。标准回测中性DefaultFillModel——完全依赖记录盘口prob_fill_on_limit用来调节限价单成交的乐观程度如0.2表示只有 20% 的触及会成交模拟弱势的队列位置prob_slippage给 L1 成交附加成本。悲观压力测试OneTickSlippageFillModel强制每笔成交都差一个 tickTwoTierFillModel/LimitOrderPartialFillModel可模拟最优价流动性有限、剩余量需要吃一档的情形。大单价格冲击SizeAwareFillModel按订单规模切换盘口形态ThreeTierFillModel提供三段式深度衰减CompetitionAwareFillModel用liquidity_factor控制最优价可用深度适合研究如果最优价只有 300 个单位会怎样。成交量/时段敏感VolumeSensitiveFillModel以近期成交量的 25% 作为最优价深度MarketHoursFillModel模拟低流动性时段的价差扩大注意 Python 侧当前无法切换这两个模型的状态它们保持初始值。结果复现任何使用概率判定的模型含DefaultFillModel的非 0/1 参数、ProbabilisticFillModel都应设置random_seed否则每次运行抽样不同。最后提醒所有模型的价格与数量都必须符合标的物配置的price_precision与size_precision合成盘口构造时使用instrument.price_increment()与instrument.size_precision()这也是精度一致性在源码层的保证。有关精度校验、价格保护与残差成交的更多细节请继续阅读 fill-prices-and-matching.md 与 trade-execution.md。【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
