
简介本资源是一套面向量化交易开发者与金融工程学习者的Python开源量化交易平台开发框架适用于具备基础Python编程能力、希望快速构建策略回测与实盘对接能力的中高级用户。资源包含完整可运行源码、详尽项目说明文档及配套工具脚本覆盖策略开发、数据接入、订单执行、风控模块等核心环节支持本地化部署与二次扩展。压缩包共168个文件21.48MB其中67个Python源文件构成主框架逻辑52个Markdown文档提供架构解析、API说明与使用指南另有11个Jupyter Notebook用于策略示例演示以及bat/sh脚本、CSS样式文件和国际化资源文件pot/po体现工程化交付特征。目前已有94人学习下载读者可直接获取结构清晰的模块化代码库、开箱即用的环境配置工具、多格式文档支撑体系及完整的构建与本地化流程说明显著降低量化平台从0到1的开发门槛。 三年前我开始用Python做量化交易的时候跟绝大多数人一样先是在网上搜了一堆现成的开源框架Backtrader、Zipline、vn.py装了又卸。折腾了一圈发现有些框架太重光是把数据格式转成它要求的结构就要写不少适配代码有些框架回测结果很漂亮但一接实盘就各种水土不服。后来我索性自己动手基于Python从零搭了一套量化交易平台开发框架把源码和项目说明一起打包整理了出来。这篇博文不会跟你逐行贴完整代码那太长了而是把这套框架的核心设计思路、关键模块拆分、回测和实盘之间那些绕不开的坑讲清楚最后标注源码里哪几个文件最值得优先读。如果你刚入门Python量化一段时间想从零搭一套自己的开发框架或者已经在用现成框架但觉得哪里都别扭、想搞明白内部到底怎么运转的这篇文章都能给你一个完整的参考。项目已经开源在GitHub上但比起直接clone后跑起来我更建议大家先理解里面的设计取舍再拿去改造成适合自己的版本。毕竟量化这个领域别人的框架永远只是脚手架你自己的策略逻辑和资金管理才是真正住进去的人。1. 放着现成框架不用自己搭一套的核心理由1.1 现成框架的真正痛点在哪里先说结论不是现成框架不好而是它们各自有非常明确的“主场”你的需求一旦偏离这个主场别扭感就出来了。拿Backtrader来说它的社区生态和文档在开源量化框架里算很成熟了回测模式也很灵活。但回到“自己动手”的动机上我实际用下来最大的感受是策略代码和数据源、交易接口之间的耦合仍然偏紧。比如你想接入一个自定义的数据源或者想在主循环里插入一个特殊的调度任务往往需要去翻它的事件流机制甚至要继承它的引擎类去改内部行为。如果你只是做日线级别的简单策略Backtrader足够用了但如果你要做的是一套支持多种行情频率、多策略同时跑、随时插拔数据源的“平台”它就不是最顺手的选择。vn.py则是另一条路线。它更像一套完整的量化交易系统从行情、交易、风控到GUI都有覆盖功能确实全。但问题也出在“全”上你只想跑个策略验证一下想法也得先拉起整个事件引擎、准备一堆配置、安装各种依赖。对于中小个人团队或者单打独斗的开发者来说这个学习成本和系统开销是偏高的。1.2 我列出的核心需求清单经过几个月的折腾我把自己真正需要的功能和约束排了个优先级这个清单直接决定了自研框架的边界轻量不引入重量级消息中间件、不依赖庞大的第三方库栈核心引擎用纯Python实现外部依赖尽量只有一个pandas。解耦数据源、策略、回测引擎、实盘交易接口、风控模块各自独立谁都能替换。可回测可实盘同一份策略代码能在历史数据上快速验证也能平滑切换到实盘环境差异只体现在“执行器”上。多策略并行交易信号和资金管理分工明确方便按策略独立回测、独立风控。易于定制没有魔法框架的每个核心类都简单到可以读完整源码。1.3 这套框架的整体架构设计整个框架采用事件驱动模型可以理解为一条流水线行情数据进来经过加工变成Bar数据然后按顺序推送给策略策略计算后输出信号信号经过风控模块校验后交给执行器回测时是模拟撮合器实盘时是真实经纪商接口。数据源模块 → 事件总线 → 策略引擎 → (信号) → 风控模块 → 执行器模块 ↑ | └────────── 数据落库/日志/监控 ←──────┘事件总线是这套框架的中枢它不关心数据具体从哪来也不关心策略内部怎么计算。它只负责“把事件从生产者送到订阅者”。好处是新增一个数据源、接入一个新的策略、切换执行器都只动相应模块总线本身不用改。我对比了一下常见的开源方案画了个简单的选型参考框架定位适合场景不适合的场景Backtrader中型回测/策略框架单策略回测、参数优化多策略并行、实盘接口多端接入vn.py完整交易系统期货实盘、需要GUI和柜台接入轻量快速验证、研究型回测Zipline算法研究平台金融研究、复杂组合策略实盘交易、A股特殊规则自研框架本文轻量平台化个人/小团队自研策略体系需要极速执行、机构级订单管理我的设计目标就是表格里最后一行——做不了极速执行和机构级订单管理但能为个人和小团队提供一个干净、可扩展、能跑通的量化研究闭环。2. 数据层与策略层先把“地基”的设计逻辑说清楚2.1 数据源抽象为什么统一数据接口是第一步很多自研量化框架的第一版都折在了数据层。因为行情源真是五花八门有的是从交易所API直接拉有的是从数据库读有的是从CSV文件导入还有的是从另一个行情中间件订阅。如果策略代码里到处都出现df[close]或者quote[last_price]换一个数据源就要全局搜索替换改到你怀疑人生。我这套框架的数据层只做了一件事统一定义Bar数据对象。所有数据源无论底层是tick聚合还是分钟线落地最终都输出同一种数据结构dataclass class BarData: symbol: str exchange: str datetime: datetime interval: str # 1m, 5m, 1d open: float high: float low: float close: float volume: float turnover: float 0.0 open_interest: float 0.0这个结构参考了vn.py的BarData字段设计但砍掉了不适合通用场景的扩展字段。有人会问为什么不用pandas的DataFrame作为统一格式原因很简单DataFrame适合批量计算但在事件驱动的循环里按“一根Bar一根Bar”推给策略时用轻量对象比切DataFrame再取行要自然得多。回测时如果需要向量化计算指标策略内部自己把收到Bar转成DataFrame即可。有了统一的BarData数据源接口就很简单class BaseDataSource(ABC): abstractmethod def get_bars(self, symbol: str, start: datetime, end: datetime) - list[BarData]: ... abstractmethod def subscribe(self, symbol: str, callback: Callable[[BarData], None]) - None: ...get_bars是回测和预加载用的一次性拿历史数据subscribe是实盘或增量行情用的推一根Bar就调一次回调。这样数据源接入就变成了“实现两个方法”的事后面你想接什么源都只需要写一个适配器。2.2 策略基类让策略代码只关注信号逻辑策略层是所有框架里用户最经常改的地方。我定义一个非常薄的Strategy基类核心就是几个回调方法class Strategy(ABC): def __init__(self): self.portfolio None self.logger None abstractmethod def on_init(self) - None: 策略初始化加载参数、计算指标 abstractmethod def on_bar(self, bar: BarData) - None: 每根Bar触发一次 def on_order(self, order) - None: 委托状态变化时触发 def on_trade(self, trade) - None: 成交回报时触发 def buy(self, price: float, volume: float) - str: return self.portfolio.send_order(sidebuy, priceprice, volumevolume) def sell(self, price: float, volume: float) - str: return self.portfolio.send_order(sidesell, priceprice, volumevolume)策略不直接跟数据源、不直接跟经纪商打交道。它只能做两件事接收回调、下买单卖单。至于这个单子是被回测引擎撮合了还是被实盘接口发到了交易所策略不关心。这个“策略不知道自己在回测还是实盘”的设计正是实现同一套代码两套环境运行的关键。为了让策略内部代码好写我还给Strategy挂了一个indicators的轻量工具内置了MA、EMA、RSI、MACD等常见指标的计算。所有指标都封装成函数输入Bar序列输出数值序列。2.3 组合层Portfolio的双重身份Portfolio这个类很多人一开始会忽略觉得“策略里直接算仓位就行”。但一个像样的框架里Portfolio至少要干三件事记录持仓和现金维护账户状态。校验买卖信号是否真的“买得动”比如买入量超过了当前现金可买量直接拒绝或降量。生成统一格式的订单对象送进执行器。在回测环境Portfolio是“虚拟账户”在实盘环境Portfolio是“本地下单镜像”。策略只通过self.portfolio操作资金和持仓永远不需要知道底层到底是谁在执行。这就是为什么我坚持Portfolio要跟Strategy分开——如果策略直接操作账户那回测和实盘的统一性就崩了。3. 回测引擎撮合逻辑里最容易出问题的三个地方3.1 撮合精度Bar内撮合 vs 收盘价撮合回测引擎最核心的问题是“这单到底以什么价格成交”。最粗糙的写法是在K线Bar收盘后收到收盘价直接按收盘价撮合。这对日线级别的粗略回测可能够用但如果你想做十分钟、五分钟甚至更细级别的策略误差会大到离谱。我在这套框架里实现了两类撮合模式收盘价撮合close_price_mode适合日线、研究型快速验证简单且容易复现。日内极值撮合bar_stop_mode假设Bar内部有最高价、最低价、开盘价和收盘价。当策略在Bar收盘时发出买单如果Bar的最高价高于买入价就认为这根Bar内曾经有机会成交按买入价成交如果最高价都没到买入价就认为没法成交。第二种模式实际上是对未来函数的一种修正。但如果策略逻辑里用了bar.high或bar.low做决策再用极值撮合就会引入前视偏差这是回测里最隐蔽的坑后面细说。代码示意一下这个撮合逻辑def match_order(self, order: Order, bar: BarData): if order.side buy: if self.mode close: order.price bar.close return True elif self.mode bar_stop: # 最低价才可能让买单成交 if bar.low order.price: order.price min(order.price, bar.open) return True return False3.2 滑点与手续费不模拟你会回测赚钱、实盘亏钱每个人都希望回测曲线漂亮但滑点和手续费是量化世界里最无情的“真相修正器”。不少新手用现成框架时把这两个参数设成零跑出年化很高的曲线一上实盘就发现怎么都对不上。我在这套框架里把交易成本拆成三块佣金按成交金额的万分之几算不同市场费率不同做成配置项。滑点固定滑点和比例滑点两种模式。固定滑点表示每一手成交价跟信号价差多少跳比例滑点则是按成交金额的万分之几加价。冲击成本这个比较进阶用成交量和市场深度估算回测时如果不想思考就按比例滑点覆盖掉。回测引擎在撮合完成后直接从账户余额里扣成本并且记录到每笔成交的明细里。看回测报告时net_profit一定是扣完所有成本之后的数值。这里有个经验值可以分享我个人回测A股日线策略时最低会按“双边万分之三佣金 千分之一滑点”来跑如果策略收益在这个成本假设下还能稳住才值得继续往下看如果连这个成本都扛不住别指望实盘能好到哪去。3.3 前视偏差回测“看起来很准”的最大元凶前视偏差是量化回测里最容易被忽略又最致命的问题。一句话概括用了未来才知道的数据去做决策。常见的前视偏差有三种指标计算越界策略在on_init里一次性计算整个历史区间的均线然后回测到第N根Bar时直接引用这根Bar之前算好的指标值。如果指标值在计算时用了整段历史数据那这根Bar上的指标值就已经包含了后面的信息。Bar信息泄漏在Bar收盘后再用当根Bar的high、low作为信号触发条件同时又在同一根Bar内按这个价格成交。比如策略判断“如果这根Bar的收盘价突破了上轨就买入”这本身没问题但如果还用了“这根Bar的最高价达到某个值时买入”来触发那实际上你是在已知整根Bar结果之后做的交易实盘里根本没机会执行。调仓时点错误组合优化或再平衡时用了调仓日当天的收益率数据来决定调仓权重。这等同于你提前知道今天会涨才决定重仓。我这套框架的做法是在策略接口里明确拆出两条时间线on_bar回调发生在Bar收盘之后策略只能用截至当前Bar收盘的数据做计算指标计算采用滚动窗口每次都只保证用当前Bar和之前的数据不预取未来。为了从机制上减少前视偏差indicators工具用的是增量更新方式def sma(self, value: float, window: int) - float: # 内部维护一个固定长度的队列每来一个值更新一次 self.sma_cache[window].append(value) if len(self.sma_cache[window]) window: return None return sum(self.sma_cache[window][-window:]) / window这样策略在每一根Bar上拿到的指标值都只依赖已经走过的Bar。4. 实盘对接与Broker抽象让交易接口也能即插即用4.1 同一个策略怎么从回测平滑切换到实盘回测跑通了接下来所有人都要面对一个现实怎么把策略挂到实盘上如果回测框架和实盘接口是两个完全独立的系统那实盘上线前必然得重写一遍策略逻辑这些重写带来的bug基本是不可避免的。所以我从设计之初就定了一个硬规矩回测引擎和实盘Broker实现同一个接口。这个接口长这样class BrokerBase(ABC): abstractmethod def connect(self, config: dict) - None: ... abstractmethod def send_order(self, order: Order) - str: ... abstractmethod def cancel_order(self, order_id: str) - None: ... abstractmethod def query_balance(self) - AccountInfo: ... abstractmethod def query_positions(self) - list[PositionInfo]: ... abstractmethod def subscribe_tick(self, symbols: list[str], callback) - None: ...回测时BacktestBroker负责把send_order扔进撮合逻辑实盘时LiveBroker把Order转成经纪商API的请求格式。因为上层策略和Portfolio只知道BrokerBase接口所以切换环境只需要改一行配置。这种设计不是我的原创业内很多框架都这么做但在自研小框架里保持了同样的思路收益非常大。我见过一个朋友的自研框架回测和实盘各写一套最后两边信号永远对不上排查了快一个月就是因为他实盘下单的代码和回测撮合的不是同一个订单对象。4.2 订单状态机实盘开发绕不开的核心实盘交易接口要比回测复杂得多因为订单状态会有各种中间态。我参考业内成熟设计在这套框架里实现了一个轻量订单状态机状态含义触发时机PENDING_NEW已提交等待交易所确认send_order之后NEW已接受挂在订单簿上交易所回报PARTIALLY_FILLED部分成交部分数量成交回报FILLED全部成交成交回报CANCELLED已撤销主动撤单/交易所拒绝REJECTED被拒绝报单被拒绝每个订单对象内部维护current_state框架回调on_order时会按照状态机的合法迁移路径更新状态。如果从FILLED突然收到一笔“DELETED”回报框架会直接报警而不是静默改状态。这个小设计在实盘里救过我很多次。尤其是部分成交的场景如果没有状态机策略就不知道该继续等还是该撤单。我见过一些自研框架在部分成交后直接当全部成交处理导致实际仓位和策略心里想的仓位不一致最后风控混乱。4.3 断线重连与数据补丁实盘稳定性的地基实盘环境里最容易被低估的问题是“网络断线了怎么办”。很多框架第一版只考虑正常流程等真的断了线才发现策略像无头苍蝇一样乱下单。Broker模块里我加了三个层面的防护连接状态心跳每隔几秒发送心跳超时后触发on_disconnect回调。自动重连策略指数退避重连比如第一次等2秒第二次等4秒第八次之后每30秒重连一次。补丁机制行情中断后重新连接时先拉取断点期间的历史Bar把缺失的K线补上再恢复正常推送。行情和交易通道分开处理。行情断了只影响信号更新交易通道断了则所有订单请求排队缓冲直到重连成功后再提交同时在日志里标记这些订单是“延迟提交”。有人可能会觉得这些功能太繁琐但我做量化这些年最大的体会是实盘系统平时看起来很无聊所有精彩的故事都发生在故障那几分钟而这两分钟的还原能力决定了你的系统值不值得信任。5. 风控与监控平时不显眼关键时刻救你两次5.1 前置风控拦截宁可少赚不可大亏风控模块不是实盘才需要考虑的东西回测阶段就应该一并设计进去。我框架里的风控模块叫RiskManager它挂在策略引擎和Broker之间。策略发出的每一笔订单都要先过一遍风控检查不通过就原路打回并记录日志。检查项我用到了这几类单笔最大订单金额单笔下单不得超过账户总资金的比例。单标的最大持仓任一symbol持仓量不得超过策略资金可承受的最大风险敞口。最大回撤熔断当组合回撤超过设定阈值比如20%暂停新开仓只允许平仓。日内最大亏损熔断当天亏损超过一定金额停止所有新开仓。订单频率限制防止策略在极端行情下疯狂发单。回测和实盘用同一套RiskManager逻辑这就保证了回测时风控规则也被实时验证避免“回测时不设风控、实盘上了风控导致信号对不上”的尴尬。5.2 实时监控、日志与告警让系统自己“喊救命”实盘系统一旦跑起来你不可能每时每刻盯在电脑前。完善的日志和告警是必须的。我在框架里定义了一套标准日志字段时间戳精确到毫秒事件类型数据、信号、订单、成交、风控、错误关联ID比如某个策略实例ID方便定位这笔订单是哪个策略发的告警这块我用的是轻量方案一个AlertEngine当监听到异常事件时通过邮件、企业微信机器人或钉钉机器人推送。异常事件包括断线重连、订单拒单、风控熔断触发、策略异常退出等。我自己的习惯是日志至少要留30天告警阈值宁严勿松。有一次凌晨三点策略因为交易所返回了一笔奇怪的成交回报仓位数字和策略内心想的不一致风控模块监测到了“持仓偏差超过阈值”直接发出告警并熔断了当日的新开仓操作。等早上起来我才发现当时的情况如果不熔断后面连续的下单可能会造成更大的敞口。这套联动的监控逻辑是真正让自研框架从“能跑”变成“敢跑实盘”的关键。6. 从源码到项目说明我建议你重点读这几个文件6.1 源码目录设计的思路项目源码目录结构如下我用的是标准化布局每个模块一个包quantframe/ ├── core/ │ ├── event_bus.py # 事件总线 │ ├── bar.py # BarData 数据定义 │ ├── order.py # Order 订单对象与状态机 │ └── timer.py # 定时器用于回测时间推进/实盘调度 ├── datasource/ │ ├── base.py # 数据源抽象 │ ├── csv_source.py # CSV 数据源示例 │ └── sqlite_source.py # SQLite 数据源示例 ├── strategy/ │ ├── base.py # Strategy 基类 │ └── indicators.py # 内置指标工具增量更新 ├── backtest/ │ ├── broker.py # 回测撮合器 │ ├── portfolio.py # 虚拟账户 │ └── engine.py # 回测主引擎 ├── live/ │ ├── broker.py # 实盘经纪商接口 │ └── reconnect.py # 断线重连与数据补丁 ├── risk/ │ └── manager.py # 前置风控模块 ├── monitor/ │ ├── logger.py # 日志系统 │ └── alert.py # 告警引擎 └── examples/ ├── ma_cross_strategy.py ├── backtest_config.yaml └── live_config.yaml如果你拿到源码不知道从哪里看起我建议按这个顺序读core/bar.py理解核心数据长什么样。core/event_bus.py理解事件怎么流转。strategy/base.py理解策略怎么写。backtest/broker.py理解回测撮合这是整个框架最核心也最容易出错的部分。examples/ma_cross_strategy.py跑通一个最小示例然后再去扩展。6.2 最小示例双均线策略的回测配置项目说明里的示例策略非常简单就是经典的双均线交叉策略但你把它跑一遍之后就能立刻理解框架的事件流是怎么走的。回测配置文件长这样data: source: csv path: data/btc_1d.csv symbol: BTCUSDT interval: 1d strategy: name: ma_cross params: fast: 5 slow: 20 initial_cash: 100000.0 risk: max_single_order_ratio: 0.1 max_open_position: 1 daily_loss_limit: 0.08 backtest: mode: close commission: 0.0003 slippage: 0.001 start_date: 2020-01-01 end_date: 2024-01-01运行后日志会输出每一笔订单、成交和账户变化最后生成一份绩效摘要包含总收益率、年化收益、最大回撤、夏普比率、盈亏比、交易次数等。6.3 项目说明里值得看的“参数经验表”我在项目说明里附了一张参数经验表是从自己跑各种策略里总结出来的直接抄过来参数回测建议值注意事项佣金双边万三如果是期货注意不同品种手续费差异滑点千分之一高频策略需要更精细的模拟资金规模不要用太小资金跑小资金滑点比例可能更高回测周期至少包含一轮牛熊避免只跑单边行情数据频率Bar周期与应用场景匹配日线策略不需要分钟级回测这些参数的取值没有绝对标准但先按这个经验值跑一遍再看策略表现能筛掉一大批不靠谱的想法。7. 配置管理、研究环境与三大实操心得7.1 配置管理用YAML统一管理不把参数写死在代码里量化策略的参数分为两类一类是策略本身的参数如均线周期、止损比例另一类是系统参数如数据源路径、Broker类型、风控阈值。在自研框架里如果你把这些东西硬编码在代码里每改一次就要改源码再跑调试效率会非常低。这套框架的所有配置都用YAML文件管理运行的时候通过ConfigLoader加载。实盘和回测的配置分开避免改着改着把回测的佣金设置带到实盘里。策略参数也可以通过--params命令行参数覆盖YAML里的默认值这样做参数搜索时就不用改文件了直接循环启停进程就行。7.2 研究环境Jupyter是先遣部队框架是正规军我不建议一开始就在框架里写策略。更高效的工作流是先在Jupyter Notebook里用pandas快速验证想法画出收益曲线、看看信号分布觉得靠谱了再移植进框架做正式回测。框架负责的是“工程化验证”从Notebook到框架的迁移过程要写单元测试重点检查指标计算是否跟Notebook里一致、下单时机是否有偏差。我在项目说明里专门加了一章讲解怎么把Jupyter里验证过的“伪代码”一步步翻译成框架里的策略类。这个过程踩过不少坑最典型的就是指标计算方式不一致——在Notebook里你用rolling().mean()在框架里你如果用了增量更新的SMA结果略有差异这很正常但要在单元测试里设定容差范围避免后面实盘时才发现信号偏移。7.3 心得一事件总线的线程模型要提前想清楚实盘环境的数据回调通常是多线程或异步的比如行情推送线程、交易回报线程、主控线程。如果事件总线不做线程隔离多线程同时往队列里塞事件你会在日志里看到很多难以复现的偶发bug。我这套框架用的是单线程事件循环 多生产者单消费者的模型。所有外部数据、交易回报都通过线程安全的队列推入事件循环策略回调只在事件循环线程里执行。这样就避免了策略内部的资源共享问题也保证日志输出的前后顺序是确定的。如果你的框架想做得更实时可以用多线程执行策略但那就必须在策略类里加锁复杂度会高很多。我的建议是先保证确定性再考虑性能。7.4 心得二回测绩效评估别只盯着年化收益率很多自研框架的绩效报告只输出年化收益率、最大回撤就结束了。我在项目说明里列出了几个个我认为更重要的指标卡玛比率年化收益/最大回撤衡量你每承担一份回撤能换来多少收益。交易胜率和盈亏比胜率再高如果盈亏比太低长期也不一定赚。单笔最大亏损占总资金比例这个指标比最大回撤更“近”因为它直接反映你单次决策的风险控制。连续亏损次数策略的心理承受能力往往取决于这个数值。同样一个策略年化收益完全一样但一个回撤40%、一个回撤10%长期执行体验和最终结果会天差地别。7.5 心得三源码之外你要维护的是“策略迭代流程”最后一点想说的可能有些抽象但很重要。源码只是系统的一部分围绕量化平台真正长期有价值的是迭代流程数据怎么更新、回测怎么跑、参数怎么换、结果怎么记录、实盘怎么升级。我在项目说明里附了一个简单的迭代流程模板每次实验都记录策略版本、数据版本、参数配置、回测结果文件、代码commit号、备注。有了这套记录三个月后再看一个策略你还能回想起当时为什么这么设计。如果只是把源码clone下来跑通就扔一边那它跟普通demo没有区别。真正的价值是用这套框架跑出一套属于自己的、可追溯、可复现、可迭代的策略研究流程。项目已经开源在GitHub上源码、项目说明、示例策略、配置模板都打包好了。拿到手之后我建议先别急着改代码找一份日线行情数据跑一遍双均线示例再对着日志和绩效报告去读代码理解每个模块是怎么串起来的。等你觉得事件流、订单流、风控流都顺了再从自己的需求出发去换数据源、加指标、写策略。这样一轮下来这套框架才算真正长在你自己的交易体系里。本文还有配套的精品资源点击获取