从回测到实盘:开源量化交易工具栈怎么选

系统化交易的难点,常常来自数据、研究、回测、组合构建和交易执行之间的衔接。GitHub 上的 paperswithbacktest/awesome-systematic-trading 把这条链路拆成多个目录,整理了 97 个用于研究和实际交易的库与包、40 多种策略论文、55 本书、23 个视频,以及博客和课程。

这份清单适合当作量化交易工具栈的地图。它不会替开发者做出唯一选择,却能把一个交易系统需要面对的工程边界逐项暴露出来:数据从哪里来,信号如何计算,策略如何回测,风险如何度量,组合如何优化,订单怎样通过经纪商 API 送出。

系统化交易资源库官方封面

先把交易系统拆成六层

一个可维护的量化项目,至少可以拆成六层:

  1. 数据层:获取行情、基本面、订单簿和其他时间序列数据。
  2. 研究层:清洗数据、计算指标、提取特征、分析统计关系。
  3. 策略层:把规则或模型转成可执行的入场、出场和仓位逻辑。
  4. 回测层:在历史数据上重放事件,模拟成交、资金和持仓变化。
  5. 组合与风险层:分配资产权重,控制波动、集中度和回撤。
  6. 执行层:连接交易所或经纪商,处理订单、行情流、异常和状态恢复。

清单中的分类基本沿着这六层展开。TuShareyfinanceAkShareOpenBB Terminal 位于数据入口;ta-libpandas-tafinta 负责指标;backtraderbacktesting.pyziplinevectorbtvnpy 负责回测或交易框架;PyPortfolioOptRiskfolio-Libpyfolio 对应组合优化与风险分析;ccxtIb_insync 则连接执行端。

系统化交易工具链官方配图

这种分层方式解决了一个常见问题:开发者把“能跑出收益曲线”误认为“交易系统已经完成”。收益曲线只是研究输出,真正的系统还要回答数据更新、时区、复权、滑点、订单拒绝、断线重连和持仓对账等问题。

回测框架怎么区分

事件驱动:贴近交易过程

backtraderziplinevnpyRqalphanautilus_traderaat 都属于事件驱动方向。系统按时间顺序处理行情事件,再调用策略逻辑,更新订单和持仓状态。

事件驱动框架适合描述真实交易中的过程:某个 bar 到达后计算信号,订单进入未成交状态,下一条行情到来时再判断成交或撤单。它的代码结构通常更接近实盘,因此便于把研究逻辑迁移到模拟交易或 live trading。

代价是模型运行会带有更多状态管理。你需要处理事件顺序、订单生命周期和手续费规则,也需要明确同一根 K 线中最高价、最低价和收盘价的可见性。策略逻辑写得越复杂,测试边界越多。

向量化:适合快速研究

vectorbt 的路径不同。它使用 pandas 和 NumPy 对象,并通过 Numba 加速,在较短时间内批量测试大量策略。对于参数扫描、信号组合和初步研究,向量化能够显著降低试错成本。

向量化的优势在于计算密度。假设策略只需要根据收盘价计算均线交叉,信号可以批量生成,多个窗口参数也可以一起运行。研究者能迅速发现参数空间中是否存在稳定区域,随后再把少量候选策略放进更接近实际成交过程的回测器。

两类框架并不冲突。一个更稳妥的工作流是:先用向量化框架搜索和筛选,再用事件驱动框架重放交易细节,最后在模拟盘验证订单状态。研究阶段追求速度,执行阶段追求状态完整性。

数据层决定回测上限

清单把数据来源单独列出,原因很实际:策略代码再漂亮,数据处理错误也会让结果失去意义。

TuShare 面向中国股票历史数据,AkShare 提供金融数据接口,yfinance 用 Python 方式下载海外市场数据,pandas-datareader 提供远程数据访问。基本面方向还有能够收集公司简介、财务报表、比率和股票数据的 Fundamental Analysis Data

接入数据时,至少要把以下字段作为系统契约的一部分:

字段要回答的问题
时间戳数据属于哪个市场时区?多个来源如何对齐?
价格口径是原始价格、前复权价格,还是后复权价格?
成交信息回测使用收盘成交、下一根开盘成交,还是盘口成交?
股票集合是否包含退市标的?股票池在每个时点如何生成?
缺失值停牌、节假日、网络中断怎样标记?
版本原始数据和清洗后的数据是否可以重现?

最危险的错误通常没有异常堆栈。比如用未来才知道的成分股列表构造过去的股票池,或者把复权后的价格用于模拟真实成交,程序都能正常运行,结果却已经包含未来信息。数据层应该记录下载时间、数据版本和处理步骤,让同一份研究可以重新执行。

指标、特征与模型应当分开

指标库和机器学习库位于清单的不同目录,这种安排值得保留在项目结构中。

ta-lib 提供金融市场技术分析能力;pandas-ta 列出 130 多个指标和 60 多种 TA-Lib 蜡烛图模式;go-tart 用 Go 实现技术分析库,并支持增量更新;ta-rust 则面向 Rust。它们解决的是信号计算问题。

QLibFinRLMlFinLabTradingGym 解决的是机器学习量化研究问题。QLib 是面向 AI 的量化投资平台,FinRL 以深度强化学习为重点,MlFinLab 强调可重复和可解释的工具,TradingGym 可以作为训练强化学习代理或规则算法的交易环境。

工程上可以把一条特征流水线写成这样的接口:

python
class FeaturePipeline:
    def fit(self, train_data):
        """只使用训练区间计算需要学习的参数。"""
        raise NotImplementedError

    def transform(self, data):
        """将已知参数应用到新数据。"""
        raise NotImplementedError

    def fit_transform(self, train_data):
        self.fit(train_data)
        return self.transform(train_data)

关键点是 fittransform 分离。标准化参数、缺失值填充规则或特征选择结果,都不能用完整历史数据一次性计算后再回到过去。模型训练、参数选择和测试应当有时间边界,测试区间只在最后一次评估时使用。

组合优化是信号之后的第二个决策

单个资产的预测信号并不会自动变成组合。多个信号同时出现时,系统还要决定每个资产放多少资金,以及资产之间的相关性如何处理。

PyPortfolioOpt 支持有效边界、Black-Litterman 和分级风险平价;Riskfolio-Lib 面向组合优化和定量战略资产配置;Deepdow 把组合优化与深度学习连接起来;quantstatsffn 用于组合分析;pyfolio 面向投资组合与风险分析。

组合层可以用一个简单的约束问题表达:

text
最大化:预期收益 - 风险惩罚
约束:权重之和 = 1
约束:单个资产权重不超过上限
约束:行业或资产类别暴露不超过上限
约束:换手率不超过预算

实际运行时,估计误差常常比求解器更麻烦。预期收益、协方差矩阵和相关性都来自样本,样本窗口稍微变化,权重就可能剧烈变化。因此组合模块应当保存输入矩阵、约束条件和求解结果,方便比较每次调仓之间的变化。

用仓库里的策略表读懂指标

README 的策略部分列出了 40 多篇策略论文,并按资产类别组织,表格包含夏普比率、波动率、再平衡频率、实现代码和论文链接。它同时保留了正值与负值结果,这一点很重要,因为策略清单不应只展示成功案例。

例如表中列出的几项数据如下:

策略夏普比率波动率再平衡
时间序列动量效应0.57620.5%月度
资产类别趋势跟踪0.50210.4%月度
成对切换0.6919.5%季度
比特币隔夜季节性0.89220.8%日内交易
股票低波动因子0.71711.5%月度
股票短期反转效应0.81621.4%每周
动量因素效应-0.00821.8%月度
应计项目异常-0.27213.7%每年一次

这些数字适合用来理解研究结果的结构,不能脱离论文、样本区间、交易成本和资产范围单独使用。夏普比率需要和波动率、调仓频率一起阅读。一个日内策略的执行成本、数据粒度和成交假设,与月度组合完全不同;负值记录也提醒我们,策略名称本身不等于有效信号。

回测结果如何避免失真

量化回测最常见的风险有四类。

未来函数

特征计算使用了当前时点之后才会出现的数据,或者调仓时直接使用当天收盘价,却把成交也记在当天收盘。处理办法是为每个字段写明可见时间,并让信号至少在下一个可成交时点生效。

生存者偏差

只使用当前仍然存在的股票、基金或交易对,会自动排除退市和失败样本。股票池应该按历史时点重建,数据层也应记录标的进入和退出集合的日期。

过拟合

在同一段历史数据上反复调整参数,收益曲线会越来越漂亮,策略却未必更可靠。参数搜索需要留出验证区间,最终测试区间只使用一次。多策略比较也要记录尝试过的组合数量,避免只报告最好的那条曲线。

成交与成本

手续费、滑点、最小交易单位、涨跌停限制、流动性和订单延迟都会改变结果。事件驱动回测器需要把订单状态建模清楚,向量化研究则要把成本函数明确写入收益计算。纸面收益无法替代模拟成交。

一套可执行的选型顺序

可以按以下顺序搭建个人量化交易工具栈:

  1. 先固定数据契约:明确标的、时间粒度、复权口径、时区和版本。
  2. 选一个研究入口:Python 生态可以从 pandas、NumPy 和 vectorbt 开始,快速验证信号。
  3. 选一个事件驱动回测器:把候选策略放入 backtraderziplinevnpy 或其他事件框架,重放订单与持仓。
  4. 补齐分析模块:用 ta-libpandas-ta 计算指标,用 quantstatsffnpyfolio 检查风险。
  5. 再接组合优化:根据资产数量和约束选择 PyPortfolioOptRiskfolio-Lib 或其他优化工具。
  6. 最后连接执行端:加密货币方向可看 ccxt,Interactive Brokers 方向可看 Ib_insync,同时实现订单对账和断线恢复。

这套顺序把研究速度和执行可靠性分开管理。开发者不需要一开始就搭建完整的分布式平台,但每增加一层,都要保留输入、输出和版本记录。

结语

awesome-systematic-trading 更像一份工程地图,而不是单一框架的推荐表。它把从数据获取到订单执行所需的组件放在同一个索引中,也把事件驱动与向量化、传统因子与机器学习、研究分析与实盘连接之间的差异展示出来。

对于个人开发者,第一步通常是从一条可复现的数据流水线和一个小型回测开始,逐步加入成本模型、组合约束和订单状态。对于已有 Go 或 React 工程背景的人,Python 可以承担研究和模型部分,Go 则适合承接数据服务、执行服务或状态管理,边界清晰后再决定是否拆分部署。

来源: