跳到正文

Arithmion SDK 快速上手

从 pip install 到第一个带样本外证据的回测判定。真实的类名、真实的默认值、以及为什么第一次跑大概率会被验证器拦下来。

第 7 篇 · 共 8 篇 约 9 分钟

上手一个量化框架,最省时间的顺序不是”先读完 API 文档”,而是先把一条最短的链走通:装上、写一个策略类、跑一次回测、拿一份验证判定。这条链上只有四个动作,但第四个动作——判定——才是这套 SDK 跟别家不一样的地方,也是这篇文章大部分篇幅会花在那儿的原因。

先把名字说清楚,不然你 import 的时候会懵。Arithmion 是品牌,Python 包叫 apexquant。平台是从一个叫 ApexQuant 的毕业论文项目长出来的,品牌改了,包名没改——改名要动所有 import、entry-point 组名和版本标记,换不来任何功能。所以下文你看到 apexquant,读作 Arithmion。

装哪一层

SDK 分两层,你先得决定装哪一层。

契约层是 apexquant.core:抽象基类、带类型的数据载体、插件注册表。它整个开源在 arithmion-core,clone 下来 pip install -e . 就是全部接口,要求 Python ≥ 3.11,运行时依赖只有 pandas 和 loguru——轻到可以随手装进任何环境。你只想摸一下契约、看看自己的策略代码会长什么样,装这个就够了。

实现层是引擎、数据源、训练器、agent 运行时,随平台发布。真的要跑回测、要拿验证判定,需要完整产品。

两层共享同一条 import 路径,所以你针对契约写的代码在两边不用改一个字。

第一段代码:策略就是一个类

契约层里最小可用的策略长这样,这段直接来自 arithmion-core 的 README:

from apexquant.core.bar import Bar
from apexquant.core.strategy import BaseStrategy, Signal

class Momentum(BaseStrategy):
    name = "momentum"
    freq = "daily"

    def on_bar(self, bar: Bar) -> Signal:
        if bar.close > bar.open:
            return Signal.BUY
        return Signal.HOLD

提示词

用 Arithmion 的 BaseStrategy 写一个最小的日线动量策略,收阳买入,否则持有。

on_bar 是唯一必须实现的方法。引擎逐 bar 调用它,你返回一个 Signal(BUY/SELL/SHORT/COVER/HOLD)或者一个挂单对象。其余全是可选的生命周期钩子:on_start、on_end、on_fill(成交回调)、on_regime_change、on_prediction(模型预测异步送达时)。

那行 freq = "daily" 值得单独说一句,因为新手最容易在这里踩坑:频率是策略设计的一部分,不是运行时偏好。一条 MA200 在日线上跨 200 天,在 15 分钟线上不到三天——同一份代码跑在两个频率上就是两个策略。所以频率写在类属性上,runner 在加载数据之前、不实例化就读它。

横截面轮动是另一个基类。做多空分位、按目标权重调仓这类活儿,你继承 PortfolioStrategy(apexquant.core.portfolio_strategy),实现 on_rebalance(view) 返回 {ticker: 目标权重},权重按当前净值计。bar 缓冲、目标与持仓的差分、T+1 成交撮合都是引擎的事——这个基类存在的全部意义,就是替代”在 on_bar 里手搓缓冲区和净值账本”这种人人都会写错一遍的形状。新手拿到多标的任务的第一反应往往是回到 on_bar 里自己攒数组,看到这段就别去了。

数据长什么样

引擎吃的 bar 数据有一个规范形状:长格式的 OHLCV 帧,索引是 tz-aware UTC 的 timestamp,外加一列 symbol。做横截面因子研究时,你会用到另外两个形状,契约层提供了无损转换:apexquant.core.panel 里的 bars_to_prices 把长格式变成宽的收盘价矩阵(行是时间戳、列是标的),panel_to_factor_series 把一列因子面板变成 (timestamp, symbol) 的 MultiIndex Series。因子面板本身的合法性由 validate_panel 把关——时间戳必须 tz-aware,(timestamp, symbol) 不许重复,因子列必须是数值。

这些约束听起来啰嗦,但它们挡的是同一类事故:数据形状对不上时,很多 pandas 操作不会报错,只会静静地广播出一堆 NaN,然后你在三小时后对着一个 IC 为零的因子怀疑人生。

因子研究:一行拿到评价报告

如果你上手的第一件事是因子而不是策略,路径更短。apexquant.notebook.evaluate_factor 是 notebook 里直接可调的因子评价入口,跟平台 POST /api/research/factor/evaluate 跑的是同一个引擎:

from apexquant.notebook import evaluate_factor

report = evaluate_factor(bars, "momentum_60", quantiles=5, periods=(1, 5, 10))
report["ic_summary"]

提示词

用 Arithmion 的 notebook.evaluate_factor 评价一个内置动量因子,分 5 个分位,看 1/5/10 期的前向收益。

factor 参数吃三种东西:内置因子名、你自己的 (timestamp, symbol) 堆叠 Series、或者一张宽的因子矩阵。内置的目前是 momentum_20、momentum_60、reversal_5、volatility_20 四个——数量不多,但足够让你在写自己的因子之前先把整条链跑通一遍,确认环境没坏。

ic_summary 里最该看的是 rank_ic_mean(Spearman 秩相关的均值)、rank_icir(均值除以标准差)和 rank_ic_t_stat。t 统计量这一项做了一件容易被忽略的事:当你的前向收益横跨多个 bar(periods 里的 5 和 10),IC 序列本身是自相关的,朴素的 icir × √n 会把显著性吹起来,所以它走的是 Newey-West 修正,带宽取主周期减一。报告里同时给了 Pearson 那一族的对应数字,两族之间不能互相除——它们是同一批日期上的两个不同统计量。

还有一个默认值值得盯一眼:cost_bps 默认是 0.0,也就是明确的”不计成本”基线。这是有意的——纯研究口径下你要的就是干净的数;但分位曲线在这个口径下每根 bar 免费换手,那个数字不是可交易的收益。真要看能不能落地,把 cost_bps 填上你市场的真实值再跑一遍,两个数一起看。

回测跑起来,以及它默认多贵

回测由引擎跑,成本模型挂在 config 的 backtest 段上,默认值是这套:

"backtest": {
    "initial_capital": 100_000,
    "commission": 0.001,      # 每边 0.1%(10 bp)
    "slippage": 0.0005,       # 5 bp,作用在次一开盘的成交价上
    "min_commission": 1.0,    # 每边最低 $1
    "position_size": 0.25,
    "max_positions": 6,
}

提示词

给出 Arithmion 回测引擎 backtest 配置段的默认成本参数,并解释每项含义。

这套默认是刻意保守的——10 bp 佣金加 5 bp 滑点对不少市场偏贵。这是特性不是 bug:回测成本宁可偏高,让”扣完成本还活着”的策略更可信。做低成本市场、或者手里有真实成交数据时,改成你自己的数。

注意 slippage 那行注释里的”次一开盘”。策略在 bar 收盘时给出信号,成交发生在下一根 bar 的开盘价再叠加滑点。这不是引擎的怪癖,是防前视最基本的约束,它被焊死在成交路径上,你关不掉。

第一次跑会被拦下来,这是设计

跑完的回测是一个 run,run 要过验证器才有资格往下走。内置统计验证器的默认配置是 min_dsr: 0.95、require_oos: True、min_oos_sharpe: 0.0、min_periods: 20。关键机制是钳制:你传进来的验证配置会被逐项跟默认值比,数值项取更严的那个,require_oos 只要默认为真就关不掉。默认门槛是地板,你可以往上加码,不能往下挖洞。

所以你第一次天真地跑一个回测再去要判定,几乎必然拿到这句话:

No out-of-sample result present (required)

读清楚这句话的语义——它说的是证据缺失,不是策略糟糕。补证据的办法是明确的:回测时带上 walk-forward 配置,run 才会带样本外块。

walk_forward = {
    "enabled": True,
    "is_months": 12,     # 样本内窗口
    "oos_months": 3,     # 样本外窗口
    "step_months": 3,    # 每次前进
    "mode": "true_oos",
}

提示词

给出 Arithmion run_backtest 的 walk-forward 配置:12 个月训练、3 个月样本外、每 3 个月滚动一次。

代价是真金白银的算力:带上 walk-forward,这个 run 的开销大约是 窗口数 × 一次回测。这也是它默认关着的原因。

窗口本身怎么切,契约层给了可以直接读的实现。apexquant.core.training.rolling.rolling_windows(n, train_size=..., test_size=..., step=..., expanding=...) 返回一串 Window(每个是 train_start/train_end/test_start/test_end 四个半开区间下标,test_start == train_end)。expanding=False 是滚动窗口,训练集长度固定往前推;expanding=True 把每个窗口的 train_start 锚在 0,训练集随时间扩张。走 walk-forward 这条路时,这两个模式的区别就是”只信最近几年”和”信全部历史”的区别,值得你在跑之前想清楚要哪个。

什么样算是可以往下走的第一次回测

先说一句得罪人的:第一次回测的数字,绝大多数情况下不值得高兴,值得的是它给了你一个可以往下减的起点。

Sharpe 落在 0.5 到 1.5 之间(扣完成本)是正常区间。低于 0.5,成本一涨就没了;超过 2.0——尤其是在单个资产上——第一反应应该是查泄露,而不是庆祝。这不是谦虚,是概率:一个真在单标的上拿到 Sharpe 2 的策略,通常伴随着极高的换手和极窄的容量,而你在快速上手阶段大概率还没建那种执行能力。

样本外衰减是第二个要看的数。假设你的样本内 Sharpe 是 0.87,walk-forward 之后样本外 0.62,这就是 29% 的衰减,属于正常区间。衰减超过 50%,大概率是过拟合,别往下走。衰减低于 10% 反而更值得警惕——要么是运气好,要么你的信号简单到根本就是个结构性的东西(比如变相的市场 beta),那就不是 alpha。

换手率是最容易被忽略的一项。年化换手超过 5 倍,除非你能拿到机构费率,否则散户成本结构下日级再平衡基本是给券商打工。回到上面那套默认成本参数算一遍:每边 10 bp 佣金加 5 bp 滑点,往返 30 bp,一年换手 5 倍就是 150 bp 的年化拖累。你的策略毛收益顶不顶得住这个数,是一道算术题,不是一道信仰题。

三条都不满足也不叫失败,叫信息。它告诉你下一轮该改的是信号、成本假设还是再平衡频率。

这套 SDK 明确不做的事

它不给你能赚钱的现成策略。SDK 是基础设施,不是信号服务:因子、模型、执行逻辑都得你自己构建,平台的职责是让这件事更快、更难做错。

它也不适合几类场景,说清楚边界比背功能表有用。微秒级延迟的活儿不要来。 高频做市对接交易所 co-lo 是 C++ 和 Rust 的地盘,这套按 bar 执行的引擎在毫秒量级,做隔夜波段没事,做市致命。流动性极差的资产也不要来。 回测的成交模型建立在”有成交量可吃”这个假设上,日均成交几万美金的小盘股、OTC 债券、小 DEX 上的代币,你需要的是自己的数据管道和自己的冲击模型。上万资产的组合优化不在射程内。 一万个标的跑 Markowitz 再日级再平衡,你要的是专门的优化器,cvxpy 之类的更合适。

除此之外——动量、价值、质量、因子组合、流动性好的股票上的机器学习信号——这套工具是对的。

下一步

流水线跑通之后,往下走的顺序基本是固定的。先扩股票池:从几只票的手工列表扩成一个真正的横截面配置,分位数才有意义。再换因子:内置的四个跑完,写自己的,evaluate_factor 的入口对自定义因子和内置因子是同一个。然后组合:两三个低相关因子做 z-score 叠加,看 Sharpe 升还是不升——很多时候不升,那也是结论。最后加风控:仓位大小、行业中性、杠杆上限,这些在契约层的 risk 域里有对应的插件位。

你写的东西通过 entry point 注册(apexquant.strategies、apexquant.datasources、apexquant.metrics 这些组),平台扫描发现之后就把它当一等公民用。契约层的文档写在代码里,strategy.py 和 bar.py 的 docstring 是最值得先读的两份。

想深入的话可以读这些

  • arithmion-core 仓库(契约层源码与文档):github.com/Arithmion-git/arithmion-core。
  • Chan, E. (2008). Quantitative Trading. Wiley. ISBN 978-0470284889. 第 7 章给了一个带真实交易成本建模的动量回测的经典走读——上面那套保守成本默认值背后是同一个思路。