尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Nautilus Trader 网格做市策略(Grid Market Maker)深度指南:库存感知定价、几何网格与仓位风控

Nautilus Trader 网格做市策略(Grid Market Maker)深度指南:库存感知定价、几何网格与仓位风控 Nautilus Trader 网格做市策略Grid Market Maker深度指南库存感知定价、几何网格与仓位风控【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader网格做市Grid Market Making是加密货币与衍生品市场中最经典的流动性提供策略之一围绕最新中间价对称挂出一组限价买单与卖单通过价格波动反复成交赚取买卖价差。nautilus_trader 仓库在crates/trading中内置了生产级参考实现GridMarketMaker它在传统网格基础上加入了库存感知偏斜Avellaneda-Stoikov 启发、最坏情况仓位投影与异步撤单保护等机制。读完本文你将掌握该策略的完整下单流程、全部配置参数与调优方法并能同时在 Rust 与 Pythonv2两条路径中将其接入LiveNode或BacktestEngine运行。本文以 crates/trading/src/examples/strategies/grid_mm/README.md 为核心骨架并结合同目录下的 strategy.rs、config.rs、tests.rs 源码与测试逐一展开印证。策略概览GridMarketMaker是一套库存感知inventory-aware的网格做市策略核心设计目标有三围绕中间价对称挂单在每个 requote 周期内按几何间隔在中间价两侧各生成num_levels档限价单买、卖各num_levels档。降低撤换单频率已挂出的订单不会逐 tick 重建只有当中间价相对上次挂单位置移动超过requote_threshold_bps阈值时才整体撤换从而减少无谓的 cancel/replace 流量。抑制库存堆积整张网格会按当前净持仓方向施加一个与仓位成比例的偏斜skew鼓励市场消化已有库存而非单向越积越多。策略通过DataActor接口订阅单个合约的QuoteTick行情流所有状态instrument、price_precision、last_quoted_mid、pending_self_cancels都保存在策略实例内部采用纯事件驱动方式响应行情与订单事件。下单流程一次 requote 的完整生命周期几何网格定价公式GridMarketMaker每次 requote 的订单生成逻辑集中在 strategy.rs 的grid_orders方法中。网格采用**几何间隔geometric spacing**而非等差间隔即价格按指数递减/递增买入第 N 档mid × (1 - grid_step_bps / 10000) ^ N - skew卖出第 N 档mid × (1 grid_step_bps / 10000) ^ N - skew其中grid_step_bps为网格间距以中间价的基点计10 bps 0.1%。几何网格在高价位区间相邻档位间距更大、在低价位区间更密天然契合价格百分比波动近似的市场特征是网格做市的常见做法。源码中对应实现为let pct self.config.grid_step_bps as f64 / 10_000.0; for level in 1..self.config.num_levels { let buy_f64 mid_f64 * (1.0 - pct).powi(level as i32) - skew_f64; let sell_f64 mid_f64 * (1.0 pct).powi(level as i32) - skew_f64; let buy_price instrument.next_bid_price(buy_f64, 0); let sell_price instrument.next_ask_price(sell_f64, 0); // ... }值得注意的是计算出的裸价格并不会直接下单而是经过instrument.next_bid_price/next_ask_price做tick 取整买入价向下取整到最近的有效买盘 tick保证buy_price ≤ 理论价对买方更有利、更易成交卖出价向上取整到最近的有效卖盘 tick保证sell_price ≥ 理论价。源码注释明确指出这一步是为了防止在 tick 间隔较粗的合约上出现自成交self-cross——若两侧都向下或向上取整买价与卖价可能在窄网格下互相穿越。测试 tests.rs 中给出精确例证mid1000、grid_step_bps1001% 网格时三档买价为 990.00 / 980.10 / 970.29三档卖价为 1010.00 / 1020.10 / 1030.31970.299→970.29 向下取整、1030.301→1030.31 向上取整。requote 触发条件阈值 空仓兜底每个QuoteTick到达后on_quote 首先从 bid/ask 计算中间价let mid_f64 f64::midpoint(quote.bid_price.as_f64(), quote.ask_price.as_f64());随后调用should_requotestrategy.rs判断是否满足撤换条件——中间价相对last_quoted_mid的相对位移是否达到requote_threshold_bpslet threshold self.config.requote_threshold_bps as f64 / 10_000.0; (mid.as_f64() - last_f64).abs() / last_f64 threshold另外还有一层空网格兜底即使中间价仍在阈值内只要当前既没有 restingopen订单也没有 inflight在途订单策略也会强制 requotestrategy.rs避免出现零挂单干等价格漂移的僵局。测试 tests.rs 验证了阈值判定边界5 bps 阈值下 mid 从 1000.00 移到 1000.300.03%不 requote移到 1000.500.05%或 999.40-0.06%则 requote。requote 的完整执行顺序README 与源码一致计算最新 quote 的中间价判断中间价自上次挂网以来是否移动超过requote_threshold_bps撤销全部既有订单cancel_all_orders按几何间距公式计算各档买/卖价并做 tick 取整逐档校验max_position仓位上限计入在途订单的最坏情况敞口后才挂出。库存偏斜Inventory SkewAvellaneda-Stoikov 启发网格不是永远对称的。为了抑制库存堆积策略会根据当前净持仓对整张网格施加平移偏斜公式为skew skew_factor * net_position当net_position 0持有净多头时skew 0此时卖出档价格 mid × (1pct)^N - skew整体下移→ 卖价更贴近市场更激进地出掉多头库存买入档价格 mid × (1-pct)^N - skew同样下移→ 买价离市场更远不再继续追买。净空头时则完全对称地反向作用。这一偏斜方向与持仓相反、幅度与仓位成正比的思路正是 Avellaneda-Stoikov 做市模型中库存风险控制的核心思想README 与 mod.rs 的文档注释均明确标注 Avellaneda-Stoikov inspired。测试 tests.rs 给出精确数值例证skew_factor1.0、net_position2.0时skew2.0mid1000、5% 网格下单档买价 1000×0.95 − 2.0 948.00单档卖价 1000×1.05 − 2.0 1048.00整张网格整体下移 2 个价格单位。仓位上限基于最坏情况敞口的逐档校验为什么必须用最坏情况撤单是异步的策略发起cancel_all_orders后被撤订单在交易所确认之前仍可能成交。因此max_position的校验不能只看当前持仓而必须把在途pending买单/卖单一并计入投影敞口。on_quote中通过缓存cache分别聚合net_position所有 open 仓位的带符号数量signed_qty之和用于计算 skewworst_long 当前仓位 所有在途买单剩余数量leaves_qtyworst_short 当前仓位 − 所有在途卖单剩余数量。源码在聚合 open/inflight 订单时还对ClientOrderId做了去重strategy.rs因为订单在状态迁移过程中可能同时出现在 open 与 inflight 集合里避免重复计仓。逐档投影校验grid_orders内逐档累加投影敞口strategy.rsif let Some(buy_price) buy_price projected_long trade_size max_pos { orders.push((OrderSide::Buy, buy_price)); projected_long trade_size; } if let Some(sell_price) sell_price projected_short - trade_size -max_pos { orders.push((OrderSide::Sell, sell_price)); projected_short - trade_size; }即只有当前投影多头 本档买单 ≤max_position时才挂买档只有当前投影空头 − 本档卖单 ≥ −max_position时才挂卖档越界的档位直接跳过skip。max_position是双向硬顶long 与 short 对称。tests.rs 中用一组参数化测试完整覆盖了这些分支持仓 9.9、trade_size0.1、max10.0时买档只放得下 1 档卖档 3 档全放tests.rs持仓恰好等于max10.0时买档全部被拦截tests.rsmax_position0.15、trade_size0.1时两侧各只能放 1 档投影敞口跨档累加校验tests.rsmax_position0.05小于单档trade_size0.1时网格为空tests.rs。还有一个容易被忽略的细节当网格计算出来为空时last_quoted_mid不会更新strategy.rs这样下一个 quote 仍会触发 requote 重试策略不会因仓位限制而永久停滞。订单生命周期与事件处理策略不仅处理行情还通过nautilus_strategy!宏注册了四个关键订单事件处理器strategy.rs核心逻辑围绕pending_self_cancels集合与last_quoted_mid锚点展开事件行为目的on_order_rejected从pending_self_cancels移除该单last_quoted_mid None拒单后网格已不完整下一个 quote 自动重挂整张网格on_order_expired同上GTD 到期意味着网格消失需解除 requote 抑制on_order_filled仅当订单完全关闭is_closed()时才从pending_self_cancels移除部分成交必须保留 ID避免后续自撤单被误判为外部撤单on_order_canceled若命中pending_self_cancels则忽略这是自己发起的撤单否则当on_cancel_resubmittrue时重置last_quoted_mid区分自撤与协议/外部撤单外部撤单后允许重挂网格pending_self_cancels会在每次 requote 撤单前由on_quote填充当on_cancel_resubmittrue时见 strategy.rs。on_cancel_resubmit的典型应用场景是 dYdX 这类协议会主动撤销短时订单的交易所外部撤单事件到达后策略能感知到网格残缺并在下一根 quote 上自动重挂。tests.rs 针对这一区分逻辑设计了多组边界测试tests.rs例如自撤单不重置 mid → 后续协议撤单正常重置、订单在撤单确认前先成交不影响后续协议撤单触发重挂、on_cancel_resubmitfalse时任何撤单都不重置等。启动、停止与重置DataActor生命周期回调strategy.rson_start从缓存解析合约缓存price_precision与min_quantity若配置未显式给出trade_size则回退为合约min_quantity再退化为Quantity::new(1.0, size_precision)随后subscribe_quotes订阅行情。on_stopcancel_all_orders撤掉全部订单、close_all_positions平掉全部仓位、unsubscribe_quotes退订行情干净退出。on_reset清空instrument、price_precision、last_quoted_mid、pending_self_cancels并把trade_size还原为配置值tests.rs 验证了重置后状态完整性。配置参数全解README 中的参数表是配置的核心依据下表在保留全部字段的基础上补充了 config.rs 中 builder 默认值、python/examples.rs Python 绑定签名及init.pyi 的类型信息参数类型默认值说明instrument_idInstrumentId必填交易合约如BTC-USDT-SWAP.OKXmax_positionQuantity必填净敞口硬顶多头/空头对称trade_sizeOptionQtyNone每档下单数量None时在on_start从合约min_quantity解析num_levelsusize3每侧买/卖档位数grid_step_bpsu3210网格间距中间价的基点10 bps 0.1%skew_factorf640.0库存偏斜乘数越大越激进地逆持仓方向偏移网格requote_threshold_bpsu325触发重挂的最小中间价位移bps降低撤换单频率expire_time_secsOptionu64None订单到期秒数设置后订单使用 GTDGood-Till-Date时间生效on_cancel_resubmitboolfalse收到外部撤单事件后在下一根 quote 重挂整张网格此外Rust builder 默认注入基础策略配置config.rs默认strategy_id GRID_MM-001、order_id_tag 001tests.rs 的test_config_defaults逐一断言了全部默认值。Python 构造器还额外暴露两个基础配置项examples.rsstrategy_id: StrategyId | None None自定义策略 IDorder_id_tag: str | None None订单 ID 标签use_uuid_client_order_ids: bool False/use_hyphens_in_client_order_ids: bool True客户端订单 ID 生成方式部分交易所如 OKX 要求纯字母数字 clOrdId需关闭连字符见 examples/live/okx/exec_tester.py 的实际用法。下单时所有限价单均以post_onlytrue提交strategy.rs即只挂单不吃单确保策略纯粹作为流动性提供者运行同时避免与 taker 单竞争。参数调优指南README 给出了三组典型场景的调参建议可归纳为以下决策框架窄价差、高成交量市场grid_step_bps5、num_levels5、requote_threshold_bps2。网格更密、撤换更灵敏能捕捉更多 tick但会产生更高的订单流量对成交与撤换速度要求高。宽价差、低成交量市场grid_step_bps20、num_levels3、requote_threshold_bps10。放宽阈值减少对流动性差合约的无谓 requote避免频繁撤换单带来的滑点与手续费损耗。库存控制从skew_factor0.5起步若策略持续累积方向性库存再逐步提高同时把max_position设为可承受的最大敞口配合投影校验杜绝超限。调参时需理解三组参数的耦合关系requote_threshold_bps决定多久撤换一次影响订单流量grid_step_bps × num_levels决定网格覆盖的价格范围影响成交概率与库存累积速度skew_factor × max_position决定库存如何被抑制与封顶影响风险敞口。expire_time_secs适合设置后自动清掉残单的场景配合on_cancel_resubmittrue实现过期自动重挂闭环。Rust 用法策略暴露在nautilus_trading::examples::strategies模块下见 strategies/mod.rs 的 re-export通过 builder 构造配置、GridMarketMaker::new实例化后注册到节点use nautilus_trading::examples::strategies::{GridMarketMaker, GridMarketMakerConfig}; let config GridMarketMakerConfig::builder() .instrument_id(InstrumentId::from(BTC-USDT-SWAP.OKX)) .max_position(Quantity::from(10.0)) .trade_size(Quantity::from(0.1)) .num_levels(5) .grid_step_bps(15) .skew_factor(0.5) .requote_threshold_bps(5) .build(); let strategy GridMarketMaker::new(config); node.add_strategy(strategy)?;纯 Rust 路径不需要 Python 运行时编写策略与main函数后用cargo build构建独立二进制node.add_strategy(strategy)?注册后node.run().await?即可运行参考 docs/concepts/rust.md 中的 Pure Rust 示例。Python 用法v2Python 侧通过add_builtin_strategy把配置传给LiveNode或BacktestEnginecrates/live/src/python/node.rs 与 crates/backtest/src/python/engine.rs 均实现了同名方法。Python 只负责提供配置策略本体完全在 Rust 中运行——这是single-source设计同一份 Rust 策略代码同时服务 Rust/Python 文档、示例与测试见 docs/concepts/rust.md 的说明与内置策略注册表。from nautilus_trader.trading import GridMarketMakerConfig config GridMarketMakerConfig( instrument_idInstrumentId.from_str(BTC-USDT-SWAP.OKX), max_positionQuantity.from_str(10.0), trade_sizeQuantity.from_str(0.1), num_levels5, grid_step_bps15, skew_factor0.5, requote_threshold_bps5, ) node.add_builtin_strategy(GridMarketMaker, config)add_builtin_strategy(GridMarketMaker, ...)的字符串类型名与GridMarketMakerConfig的对应关系记录在 docs/concepts/rust.md 的内置策略表中。需要注意的是add_builtin_strategy是内置示例策略的注册通道并非自定义原生策略的一等扩展路径——如需编写自己的原生策略请走纯 Rust 路径add_strategy。Python 调用方若使用BacktestEngine同样调用engine.add_builtin_strategy(GridMarketMaker, config)crates/backtest/src/python/engine.rs。源码验证与测试覆盖策略的正确性由一组完备的单元测试背书tests.rs使用rstest参数化框架测试合约为crypto_perpetual_ethusdt桩默认值test_config_defaults断言 strategy_id、order_id_tag、trade_sizeNone、num_levels3、grid_step_bps10、skew_factor0.0、requote_threshold_bps5、expire_time_secsNone、on_cancel_resubmitfalserequote 阈值无上次报价必 requote阈值内不 requote恰达阈值/越过阈值正负方向均 requote几何网格平坦仓位下 1% 网格两侧各 3 档价格精确到 tick买档向下取整、卖档向上取整库存偏斜skew2.0 时买/卖单档价格精确平移 2.0仓位限制多头/空头越界拦截、全拦截、投影敞口跨档累加、全约束时空网格错误路径instrument 未解析、price_precision 未解析时的错误信息断言撤单语义自撤/协议撤/成交后撤/关重挂开关 四种组合下last_quoted_mid与pending_self_cancels的状态转移重置on_reset后全部状态清空、trade_size 还原。这些测试既验证了 README 描述的每一项行为也明确了异常边界如max_position trade_size时返回空网格而非报错可作为二次开发时理解语义的第一手材料。小结GridMarketMaker把网格做市从简单对称挂单推进到了库存感知 最坏情况风控 异步撤单感知的生产级形态几何网格定价配合 tick 取整保证可成交性与无自成交requote_threshold_bps抑制无谓撤换单流量skew skew_factor × net_position实现 Avellaneda-Stoikov 式库存管理max_position投影校验覆盖在途订单。它同时是理解 nautilus_trader 事件驱动策略框架DataActor、nautilus_strategy!宏、cache 查询的优秀参考实现。上手路径清晰Rust 侧 builder 构造 add_strategyPython 侧GridMarketMakerConfigadd_builtin_strategy(GridMarketMaker, config)配置与调优完全对齐本文参数表。【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表