Arithmion SDK 概览
Arithmion 的 Python 契约层长什么样、为什么分成契约与实现两层、验证门为什么只能调严不能调松。读完你会知道自己的代码写在哪一层、跑在什么规则之下。
大多数量化工具对用户的错误很宽容:你喂什么它跑什么,最后吐一个数字。Arithmion 的设计立场相反——研究者骗自己的那几条常见路径,能在接口层堵死的就在接口层堵死。这不是宣传语,这篇文章会具体到类名和默认值,讲清楚这个立场落在代码的什么地方。
先把名字说清楚。Arithmion 的 Python 包叫 apexquant——平台从一个叫 ApexQuant 的毕业论文项目生长出来,品牌改了,包名没改,因为改名要动所有 import、entry-point 组名和版本标记,换不来任何功能收益。所以你在代码里看到 apexquant 或 aq,读作 Arithmion 就对了。
两层架构:契约与实现
SDK 分两层,分界线画得很硬。
契约层是 apexquant.core:抽象基类、带类型的数据载体、插件注册表。它不含引擎、不含任何数据供应商的代码、不含 agent 运行时,运行期依赖只有 pandas 和 loguru。这一层整个开源在 arithmion-core 仓库里,主仓通过同步脚本把 src/apexquant/core 原样导出过去,所以你针对契约写的代码在开源包和完整产品里跑的是同一份接口。
实现层是 apexquant.bundled 加引擎和 agent 运行时,只随平台发布。回测引擎、数据源实现、训练器、验证器的具体逻辑都在这一层。
这个切分决定了你的代码写在哪:写策略、写数据源、写指标,你 subclass 契约层的基类,通过 entry point 注册(apexquant.strategies、apexquant.datasources、apexquant.metrics 这些组),平台扫描发现之后就把你的实现当一等公民用。你不需要读实现层的代码,契约就是全部约定。接口目前在 v0.x,没有冻结承诺,破坏性变更记录在 CHANGELOG 里,带版本的契约携带 __apexquant_api_version__ 元组。
契约层里有什么,一句话过一遍。数据契约:bar(OHLCV bar)、signals(信号代理)、result 与 backtest.result/backtest.serialize(回测结果载体和它的标准 JSON 线格式)、panel(横截面因子面板)、trade_plan、config_schema(插件声明配置面的类型化 schema)、model_meta(描述训练产物的 meta.json schema)。插件契约:strategy 与 portfolio_strategy、predictor(逐 bar 推断)、datasource 与 streaming(有界批数据与无界实时数据)、broker、engine、metric 与 validation、risk(风险管理器、仓位器、止损策略)、training(训练器与 walk-forward 窗口切分器)、universe 与 calendar(时点正确的股票池和交易日历)。机制:registry 和 plugins 负责域注册与 entry-point 扫描。
写一个策略是什么体验
最小可用的策略是这样的,这段代码直接来自 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 调用它,返回信号或挂单。其余都是可选的生命周期钩子:on_start、on_end、on_fill(成交回调)、on_regime_change、on_prediction(模型预测异步送达时)。
那行 freq = "daily" 值得单独讲,因为它体现了 SDK 的一个设计判断:频率是策略设计的一部分,不是运行时偏好。 一条 MA200 在日线上跨 200 天,在 15 分钟线上不到三天——同一份代码跑在两个频率上就是两个策略。所以频率声明在类属性上,runner 在加载数据之前、不实例化就读它。这个设计来自一次真实的用户事故:频率只能从全局配置来的时候,一个日线策略跑在默认 15 分钟的机器上,八只股票全部 no_data,而唯一的修法是改一个会拖累所有其他策略的全局默认。
模型预测通过 bar.signals 送进策略,这是一个按路径寻址的代理:
vol = bar.signals["layer1/volatility/lightgbm_v3"] # 某个具体模型
layer1 = bar.signals.layer1 # 该层所有模型的均值
val = bar.signals.get("layer1/volatility/lightgbm_v3", 0.5) # 带默认值
策略和模型之间只隔一层字典式访问,没有隐藏的融合逻辑——要几个模型怎么加权,写在你的策略里,看得见。
横截面轮动是另一个契约:PortfolioStrategy。你实现 on_rebalance(view),返回 {ticker: 目标权重},权重按当前净值计。bar 缓冲、目标与持仓的差分、T+1 成交撮合都是引擎的事——这个基类存在的意义,就是替代”在 on_bar 里手搓缓冲区和净值账本”这种人人都会写错一遍的形状。注意 T+1 这个细节:信号在今天的 bar 上算出,单子在下一根 bar 成交。这不是引擎的怪癖,是防前视的最基本约束,引擎替你把它焊死了。
同样的思路贯穿数据层:多时间尺度策略拿到的低频聚合(bar.higher_tf["daily"])只包含已经走完的周期,股票池契约(universe)是时点正确的——回测某一天时,池子里只有那一天真实存在的成分。前视偏差和幸存者偏差不靠用户自觉,靠数据通道的形状。
为什么时点正确性值得这么大动干戈
因为大多数回测谎言都始于”用了决策当时还不存在的信息”,而这是回测策略上实盘就失效的最常见原因。三种典型形态:
行情前视。 用今天的收益率决定今天的仓位。说出来蠢,写起来极容易犯——处理日频数据时,人很自然想用当天收盘价做当天决策。在收盘算特征又按同一个收盘价成交,就是在用未来信息。正确做法是下一根 bar 成交,也就是引擎的 T+1 规则。
基本面前视。 季度盈利”属于”时点 ,但公告发生在某个 ,间隔从当天到六周以上不等。按报告期连接数据等于用了当时没人知道的数字;正确的连接是按公告可得时点。Arithmion 的时点数据处理遵循后一种语义——测试基建里甚至有一条硬规矩:date_published 必须是明确的 naive 时间戳,谁想糊弄谁先撞断言。
幸存者偏差。 用今天的指数成分回测,等于只看活下来的公司;退市股票退市前的收益往往难看得很,而那正是评估真实风险必须看到的数据。幸存者偏差抬高的回测年化,量级通常在每年一到几个百分点,随样本期和股票池变化。时点股票池契约就是为这件事存在的。
验证门:只能调严,不能调松
真正体现设计立场的地方在验证层。跑完的回测是一个 run,run 要经过验证器(validation 契约)出具判定。内置的统计验证器默认配置是:min_dsr: 0.95(要求”真实 Sharpe 超过折减基准”的概率不低于 0.95,针对多重检验偏差)、require_oos: True(必须携带样本外结果块)、min_oos_sharpe: 0.0、min_periods: 20(样本太短时高阶矩统计不可信,直接拒绝)。
关键机制是钳制:调用方传进来的验证配置会被逐项和默认值比较,数值项取更严的那个,require_oos 只要默认为真就不可能被关掉。换句话说,默认门槛是地板,你可以往上加码,不能往下挖洞。想拿一个没有样本外证据的 run 去要通过判定,验证器的回答是固定的:“No out-of-sample result present (required)“——注意这句话的语义,它说的是证据缺失,不是策略糟糕。补证据的办法也是明确的:回测时带上 walk-forward 配置,run 才会有样本外块。
这比”抛异常拦前视”这种戏剧化设计朴素得多,但方向一致:你敷衍时它跟你过不去,你严谨时它不挡路。
Agent 原生:同一套工具,人和 AI 平权
Arithmion 是 agent 原生平台,这对 SDK 意味着一件具体的事:平台能力以工具的形式暴露,research agent 调的和你在界面上点的是同一套。取数(get_market_data)、跑代码(run_python)、存取策略(save_strategy/validate_strategy)、回测(run_backtest,支持自定义股票列表和数据源、walk-forward、配置覆盖)、实验管理(run_experiment、diff_runs)、验证与晋级(validate_run、promotion_check)、部署(create_paper_deployment 等)。
安全边界也是在工具层焊死的,不是靠模型自觉。三条值得知道:
其一,agent 建部署时 mode 根本不是参数——代码里硬编码 paper,模型说什么字符串都到不了实盘分支。其二,启动工具会拒绝启动一个已被人晋级成 live 的部署:人可以在 agent 背后把部署升级,但 agent 不能成为扣扳机的那个。其三,平台上不存在”晋级到实盘”的工具。promotion_check 只回答”这个 run 有没有资格被晋级”——资格的唯一来源是通过的验证判定;真正把钱放上去,是人的决定,走人的入口。
外来的回测结果也进不了后门:通过 import_run 导入的 run 会被标记 origin=external,默认没有晋级资格——平台没亲眼看着跑出来的数字,不作为上钱的依据。
完整的生命周期长这样
把上面的件串起来,一个策略从想法到上线的门链是:
run_backtest(..., walk_forward=...) # 产生带样本外块的 run
↓
validate_run(run_id) # DSR、OOS、样本量等出具判定,门槛只能加严
↓
promotion_check(run_id) # 无通过判定 = 无晋级资格
↓
create_paper_deployment / start_... # agent 可以走到这一步:模拟盘
↓
(实盘晋级) # 没有对应工具,人类专属
每一步的产物都留痕:run 带指标和验证判定,部署带模式和状态,策略带版本。你随时可以回答”这个策略凭什么在跑”,答案在记录里,不在某个人的记忆里。如果你在运营一个多人团队,这条链就是风控要查的东西;一个人做,它是你六个月后跟自己对账的凭据。
从哪里上手
想先摸契约再决定要不要上整套产品,顺序是现成的:clone arithmion-core 仓库、pip install -e .,拿到的就是 apexquant.core 全部接口,依赖轻到可以随手装;照上面的 Momentum 例子写一个 BaseStrategy 子类,读一遍 strategy.py 和 bar.py 的 docstring——契约层的文档就写在代码里。装了完整产品的话,自带的 quickstart 示例会在打包的样本数据上把三层级联完整跑一遍,离线、CPU、不需要配任何数据源,适合确认环境没坏。再往后,你写的东西通过 entry point 注册,平台就认识它了。
SDK 不做什么
有几件事它明确不做,知道边界比背功能列表有用。
它不提供预制的赚钱策略。SDK 是基础设施,不是信号服务:因子、模型、执行逻辑都得你自己构建,平台负责让这件事更快、更难做错。没有”拿来就赚钱的模板”,想要信号服务请另找别家。
它不隐藏复杂性。没有”魔法模式”,指标怎么算、验证为什么不过、成交怎么撮合,都能查到具体位置。代价是上手比全托管平台陡,好处是每个结论可审计。
它也不想包打天下。平台聚焦股票类资产的因子与信号研究,数据面通过 datasource 契约扩展(内置源覆盖常见行情接口,外部 Parquet 数据集也能注册进来);期权定价不在原生范围内;回测按 bar 执行,高频微结构不是它的战场。策略代码——尤其是来路不明的策略代码——在隔离容器里执行,这是把”开放插件”和”保护宿主”同时做到的前提。
说句实话
SDK 有主见,你会跟它较劲。第一次因为没带 walk-forward 而拿到”证据缺失”的判定,你会烦;第五次,你会意识到它拦下的是一个你差点当真的过拟合数字。摩擦是设计出来的,而且集中投放在骗自己成本最高的地方——验证和晋级;其他地方,契约能多薄就多薄,一个 on_bar 就能跑。
接下来三篇展开细节:数据流水线与存储讲时点正确、无幸存者偏差的数据体系怎么建;回测引擎讲执行模拟、成本模型和验证门;从研究到生产的工作流把这篇末尾那条门链展开成完整的生命周期。四篇合起来,就是用 Arithmion 做严肃量化工作的操作手册。
参考资料
- López de Prado, M. Advances in Financial Machine Learning, Ch. 1 (Machine Learning for Finance). Wiley, 2018. ISBN: 978-1-119-48208-6.
- arithmion-core 仓库(契约层源码与文档):github.com/Arithmion-git/arithmion-core。
- Aronson, D. Evidence-Based Technical Analysis, Ch. 6 (Statistical Inference). Wiley, 2006. ISBN: 978-0-470-00874-4.
- Harris, L. Trading and Exchanges: Market Microstructure for Practitioners. Oxford University Press, 2003. ISBN: 978-0-19-514470-3.