
第3章第1节 5分钟跑通pip 一条命令 EMA 交叉回测直接出结果别被确定性事件驱动Rust 原生这些词吓住装起来跟装任何普通 Python 库一样简单。这篇我带你从空环境到跑出第一份回测结果全程不超过一首歌。本文导航环境准备我踩过的 Python 版本坑一条命令装好含国内镜像提速完整可运行代码EMA 交叉策略 合成随机游走数据 回测控制台输出长什么样运行流程 Mermaid 图小结下节预告先交代我踩过的坑Python 版本别乱来我最早直接pip install nautilus_trader结果报一串 wheel 错误。查了一下才发现人家有硬性版本要求支持 Python 3.12 ~ 3.14核心引擎是 Rust要求 1.96.1但你是pip安装用户的话官方会直接给你编译好的二进制 wheel你根本不用装 Rust 工具链——这层我帮你省了。我的建议用Python 3.12最稳。建一个干净的虚拟环境别污染全局。用国内镜像源不然下载慢到怀疑人生。一条命令装好先建环境Windows PowerShell 为例macOS/Linux 同理python-mvenv .venv .\.venv\Scripts\activate# Windows# source .venv/bin/activate # macOS / Linux装包走清华镜像提速pipinstall-ihttps://pypi.tuna.tsinghua.edu.cn/simple nautilus_trader装完验证能 import 就成功importnautilus_traderprint(nautilus_trader.__version__)手把手写第一个 EMA 交叉回测先看整体结构。我们做四件事定义策略类 → 造合成行情数据 → 配好回测引擎 → 跑沙箱输出结果。下面这个脚本可以直接存成ema_quickstart.py跑。我尽量贴着 nautilus_trader 的真实 API 写版本以 v1.x 为准老版本函数名若不同顺着报错提示改 import 就行。importrandomfromdatetimeimportdatetime,timedeltafromnautilus_trader.backtest.engineimportBacktestEngine,BacktestEngineConfigfromnautilus_trader.common.enumsimportLogColorfromnautilus_trader.configimportStrategyConfigfromnautilus_trader.indicatorsimportExponentialMovingAveragefromnautilus_trader.model.dataimportBar,BarTypefromnautilus_trader.model.enumsimport(AccountType,BarAggregation,OmsType,OrderSide,TimeInForce,)fromnautilus_trader.model.identifiersimportInstrumentId,Symbol,Venuefromnautilus_trader.model.instrumentsimportCurrencyPairfromnautilus_trader.model.objectsimportCurrency,Money,Price,Quantityfromnautilus_trader.model.ordersimportMarketOrderfromnautilus_trader.test_kit.providersimportTestInstrumentProviderfromnautilus_trader.trading.strategyimportStrategy# ---------- 1. 定义策略 ----------classEmaCrossStrategy(Strategy):双均线交叉快线上穿慢线买入下穿卖出。def__init__(self,config:StrategyConfig):super().__init__(configconfig)self.fast_emaExponentialMovingAverage(self.config.fast_period)# 快线 EMA(fast_period)self.slow_emaExponentialMovingAverage(self.config.slow_period)# 慢线 EMA(slow_period)self.instrumentNoneself.bar_typeself.config.bar_type# 从策略配置读 bar 类型defon_start(self):self.instrumentself.cache.instrument(self.bar_type.instrument_id)self.register_indicator_for_bars(self.bar_type,self.fast_ema)self.register_indicator_for_bars(self.bar_type,self.slow_ema)self.subscribe_bars(self.bar_type)defon_bar(self,bar:Bar):ifself.fast_ema.valueandself.slow_ema.value:ifself.fast_ema.valueself.slow_ema.valueandnotself.portfolio.is_flat(self.instrument.id):self._buy()elifself.fast_ema.valueself.slow_ema.valueandself.portfolio.is_flat(self.instrument.id):self._sell()def_buy(self):orderMarketOrder(instrument_idself.instrument.id,order_sideOrderSide.BUY,quantityQuantity.from_str(1.0),time_in_forceTimeInForce.GTC,)self.submit_order(order)def_sell(self):orderMarketOrder(instrument_idself.instrument.id,order_sideOrderSide.SELL,quantityQuantity.from_str(1.0),time_in_forceTimeInForce.GTC,)self.submit_order(order)defon_stop(self):self.log.info(策略已停止,colorLogColor.GREEN)# ---------- 2. 造合成随机游走行情 ----------defmake_bars(instrument_id:InstrumentId,n:int10000,start_price:float100.0):bar_typeBarType(instrument_id,BarAggregation.TIME,60)# 1 分钟 Barpricestart_price bars[]startdatetime(2023,1,1)foriinrange(n):steprandom.uniform(-1.5,1.5)open_price closemax(open_step,1.0)highmax(open_,close)random.uniform(0,0.5)lowmin(open_,close)-random.uniform(0,0.5)tsint((starttimedelta(minutesi)).timestamp())*1_000_000_000# UnixNanosbarBar(bar_typebar_type,openPrice.from_str(f{open_:.2f}),highPrice.from_str(f{high:.2f}),lowPrice.from_str(f{low:.2f}),closePrice.from_str(f{close:.2f}),volumeQuantity.from_int(random.randint(100,1000)),ts_eventts,ts_initts,)bars.append(bar)priceclosereturnbars,bar_type# ---------- 3. 主流程配引擎、喂数据、跑回测 ----------defmain():venueVenue(BINANCE)instrumentTestInstrumentProvider.ethusdt_binance()instrument_idinstrument.idbars,bar_typemake_bars(instrument_id)# 策略配置configStrategyConfig(instrument_idinstrument_id,bar_typebar_type,fast_period9,slow_period21,)strategyEmaCrossStrategy(configconfig)# 回测引擎engineBacktestEngine(configBacktestEngineConfig(trader_idTRADER-001,starting_balances[Money(1_000_000,Currency.USDT)],log_levelINFO,))engine.add_venue(venue)engine.add_instrument(instrument)engine.add_strategy(strategy)# 喂合成数据forbarinbars:engine.add_data(bar)engine.run()# 跑起来# 结果摘要print(\n 回测统计 )print(f期初资金:{1_000_000}USDT)print(f期末资金:{engine.trader.get_account(instrument.account_type).balance(Currency.USDT)})print(f各品种已实现盈亏:{engine.portfolio.realized_pnls()})engine.trader.generate_order_fills_report()engine.trader.generate_account_report()engine.dispose()if__name____main__:main()几个我铺过路的提醒TestInstrumentProvider.ethusdt_binance()是官方测试工具集里的现成币种 instrument省得手搓CurrencyPair。如果某些精简版装出来没带 test_kit你就自己构造一个CurrencyPair第 6 章我会手把手教你。StrategyConfig里有instrument_id、bar_type这些字段由引擎注入策略里通过self.config.bar_type能取到。engine.add_data(bar)把合成 Bar 一条条喂进去真实场景通常从 Parquet 目录批量加载那是第 14 章的事。控制台输出长这样跑通后日志里会有引擎启动、策略注册、数据分发、订单成交、统计报表。核心几行大概是这种感觉具体数字因随机种子而异 [WARNING] 是否缺少 data catalog 提示若无 Parquet 目录会提醒 ENGINE STARTED 2023-01-01 00:00:00.000 | INFO | 数据开始分发, 共 10000 bars 2023-01-01 00:00:00.120 | INFO | STRATEGY-001 订阅了 BINANCE:ETHUSDT 1分钟bar ... [ORDER.FILLED] BINANCE:ETHUSDT BUY 1.0 102.34 (账本余额更新) [ORDER.FILLED] BINANCE:ETHUSDT SELL 1.0 105.88 回测统计 期初资金: 1000000 USDT 期末资金: 1_430_215.37 USDT 各品种已实现盈亏: {instrument_id: Decimal(430215.38)}供应链注realized_pnls()返回的是各 instrument 已实现盈亏的字典我直接打印它比折腾 format 更省事成交明细看两份 reportgenerate_order_fills_report/generate_account_report输出即可。你跑出来的数字只要合理几百笔成交、盈亏有正有负就说明链路通了。运行流程 Mermaid 图是, 且空仓否, 且多仓创建策略实例EmaCrossStrategy配置 BacktestEngine账户/日志/方向注入 instrument注册策略生成合成随机游走 Barengine.add_data 逐条喂入engine.run 事件驱动回测on_bar 计算双 EMA快线 慢线?submit_order BUYsubmit_order SELL撮合成交 更新组合engine.dispose 输出统计小结pip install nautilus_trader一条命令Python 3.12 最稳镜像源提速。一个大纲拆三层策略类EMA 交叉 数据合成随机游走 引擎BacktestEngine。on_bar里用双 EMA 比较发信号submit_order下单engine.run()跑完自动输出统计。跑不通先看两个地方Python 版本对不对、import 路径是否随版本变化。点赞收藏关注三连你的第一份回测结果已经到手了。下节预告代码能跑只是第一步。第 3.2 节我把它逐行拆开Strategy 基类、on_start/on_bar/on_stop 这些事件处理器、register_indicator_for_bars 和 subscribe_bars 到底在干嘛、订单提交那条链路是怎么串起来的。知其所以然才算真入门。