结果、指标与研究记录¶
正式 BacktestResult 表示一次共享现金账户运行。证券列顺序为 result.symbols,行顺序为 result.sessions;不会把参数组合放进证券列。金额账本与分析浮点数分别使用,指标不反向修改账本。
report() 从初始资金开始计算每天收益,包含首日成本,默认 252 个交易日年化。stats() 按原始 bar 统计且不默认年化。自定义输出见研究契约,完整流程见报告教程。
日常用法¶
result = Backtest(data, config=RunConfig(initial_cash=100_000)).run(
moving_average,
parameters={"fast": 5, "slow": 20, "allocation": 0.95},
backend="python",
)
benchmark = Benchmark(
sessions=data.timeline,
prices=index_levels,
name="自备指数",
source="说明价格或全收益口径及来源",
)
print(result.stats(benchmark=benchmark, periods_per_year=252, risk_free_rate=0))
figure = result.plot(benchmark=benchmark) # 需要 doribt[plot]
figure.savefig("comparison.png")
result.export("new-report", benchmark=benchmark, periods_per_year=252, plot=True)
上例变量由研究脚本提供;完整可运行版本见 research.py。策略的签名是 moving_average(ctx, *, fast, slow, allocation)。parameters 接受 JSON 对象/列表/标量,不接受 NaN、Infinity、非字符串键或任意 Python 对象。运行前检查函数签名并复制嵌套参数;参数实际传入回调,记录的是初始值,回调内对复制后可变参数的修改不污染调用者。WeightTargets 自带其完整预计算输入,不再接收额外策略参数。
基础结果仍可直接读 equity、cash、holdings、sellable、orders、fills、intents 和权益/税务记录。*_units 为整数万分之一元,equity 等便利属性为元。close_units 保留每个证券的记账估值价,pending_shares 为尚未入账但已计入经济权益的股份;不能只用已入账持仓解释全部净值。
目标与资金记录¶
IntentRecord 和 intents.csv/ledger.json 保存 sizing(quantity/close/execution)、weight_ppm(权重乘 1000000)与 sized_at。股数目标没有权重,weight_ppm 和 sized_at 为 null;权重目标的 quantity 是确定并经公司行动调整后的股数。尚未开盘定量的意图 quantity、sized_at 均为 null,不能当成清仓。sized_at 标识定量所用的 bar;分钟输入以区间结束时间标识该 bar,execution 模式实际用该 bar 的开盘价。
Order.max_spend_units 保存显式买单总支出上限;null 表示未设置。冻结额是资源预留,可以用额外空闲现金补足,不能把 frozen_cash_units 当作硬上限。reason="spending_limit" 表示明确上限比可用现金更紧;insufficient_cash 表示现金约束。run.json 的 execution 同时保存 cash_policy 和 spending_limit,具体行为见执行契约。
成交价格与滑点成本¶
使用 BarExecution 的成交记录同时保留 reference_price_units(原始开盘价)与 price_units(滑点及边界处理后的模拟结算价)。便利属性 reference_price/price 的单位为元;slippage_cost_units = quantity × (price_units - reference_price_units),slippage_cost 为对应元值,买入加价和卖出减价均形成正成本。它已进入成交金额与收益,不包含在 fees,不能再扣一次。
fills.csv 和 ledger.json 导出参考价整数字段;派生滑点成本可由上述公式重建。旧日线执行路径的参考价和滑点成本属性为 None,CSV 为空、JSON 为 null,不能解释成零成本。run.json 的 execution 保留 slippage_policy 和 reference_price="bar_open";通过 RunConfig 运行时 config 中也保存策略。cost 模式下的 price 可能越过行情范围,表示研究结算假设;持仓仍按原始收盘价估值。
指标口径¶
以下表格为原始 stats() 口径,令 E[t] 为每根 bar 完成后的权益。引擎没有期间外部入金或出金,不在最后一天强制清仓。净值包含应收分红、待入账股份及已确认税款负债。
输出 |
定义 |
|---|---|
|
|
|
相邻 bar |
|
末尾权益/初始资金 − 1 |
|
|
|
|
|
收益样本标准差( |
|
|
|
|
|
实际成交佣金+印花税+过户费;同时提供三个分项,股息税单列 |
|
已确认的股息税,包含已扣和尚未扣收; |
|
委托/实际成交条数;不是完整买卖交易轮次或胜率 |
|
无成交/部分成交的委托数;持续目标的不同交易日尝试分别计数 |
risk_free_rate 是年有效利率,默认 0;每区间门槛 q=(1+rate)**(1/P)-1。它只参与分析,不向现金账户计息。未指定 periods_per_year 时不计算年化、Sharpe、Sortino 或年化跟踪指标;非零无风险利率此时会报错。即便输入日线,也不猜测应采用 250、252 或其他天数。不按自然日跨度悄悄换一种 CAGR 算法。
少于两个收益区间时样本风险指标为 None,无收益区间时年化收益也为 None。零标准差/零下行偏差对应比率为 None,不显示无穷大。前一日权益为零或负数时下一期收益不可定义,数组用 NaN,undefined_return_periods 计数,风险指标为 None,不丢掉失败区间再算好看的统计。浮点计算超出可表示范围的指标也为 None。导出用 JSON null 或 CSV 空单元格,不写非标准 JSON NaN/Infinity。
基准¶
Benchmark(sessions=..., prices=..., name=..., source=...) 接受正的、有限的价格或指数水平,复制输入并要求日期与回测结果逐项相同;不会自动重排、前填、下载或复权。第一收盘归一化为 1。纯价格指数与全收益指数由来源明确,DoriBT 不把价格指数当成包含分红的收益指数。
benchmark_total_return、benchmark_max_drawdown使用同样的首日基准。excess_total_return是策略累计收益减基准累计收益,非相对财富比值。beta为策略与基准收益的样本协方差/基准样本方差;零方差时不可定义。annual_tracking_error为每期策略减基准收益的样本标准差 ×sqrt(P)。information_ratio为每期超额收益均值/样本标准差 ×sqrt(P);零跟踪误差时不可定义。
基准使用持有指数的理论路径,不替基准扣佣金或模拟成交;如需可交易基准,应另跑一份具有同样费用和交易约束的策略来比较。
图表¶
plot() 返回 Matplotlib Figure,上方显示净值与可选基准,下方显示负向回撤;提供基准时,中间另显示累计超额收益。可以继续使用 Figure/Axes 编辑,或保存 PNG、SVG、PDF。库不打开桌面窗口、不调用 pyplot.show()、不修改全局 Matplotlib 后端或字体设置。默认优先中文标题与图例,自动选择本机已安装的 Noto Sans SC/CJK SC、微软雅黑等中文字体;缺少这些字体时内置标签回退为英文,避免缺字。安装 Noto Sans CJK SC 后可使用中文;用户自定义基准名称保持原文,所需字体由调用环境提供。
plot 是可选安装项;导入和运行基础引擎不加载 Matplotlib。export(..., plot=True) 才请求绘图,缺依赖会明确报错并清理此次临时输出。
默认按图表含义配色:策略净值红色(#c83932)、基准蓝色(#477bb5)、累计超额收益金色(#b98b2f)、负向回撤浅红色(#df8a87),零线与网格用中性灰。累计超额收益为 result.nav - benchmark.nav,与累计收益差指标一致,单独以百分比坐标显示,不与净值共用纵轴。没有基准时不显示超额面板。
红/灰/绿用于表达上涨、平收或停牌、下跌的行情状态,不按此规则为上述研究系列分配颜色。当前导出为净值、超额和回撤图,不包含 K 线或行情状态图。净值线不随每段涨跌变色;需要定制可编辑返回的 Figure。分钟图保留每根 bar 的结束时点,横轴按输入市场时区显示;export(daily=True, plot=True) 则显示日末采样后的曲线。
导出契约¶
result.export(path, ...) 返回绝对目录路径。父目录必须存在,目标必须尚不存在。不提供隐式覆盖开关。所有文件先写到同级临时目录,读回校验成功后,通过 Windows 不替换 rename 或 Linux renameat2(RENAME_NOREPLACE) 一次发布;竞争进程抢先创建同名目录也不会被覆盖。写入/绘图/校验/发布失败时清理本次暂存,保留之前的成功目录。保证进程可见性和不覆盖,不宣称断电持久化;其他操作系统目前不支持该发布操作。
文件 |
内容 |
|---|---|
|
每根 bar 的现金、可用现金、权益、应收、税负债、净值、收益、回撤 |
|
每根 bar、每标的数量、可卖量、待入账股份、估值价、含待入账股份的价值 |
|
委托、成交、意图及目标调整,带关联 ID 和原因 |
|
登记权益、应收/到账/入账事件 |
|
税款确认、扣收和期末剩余税务批次 |
|
以上离散账本记录的有类型版本,保留空值与嵌套调整信息 |
|
指标以及本次指定的年化周期和无风险利率 |
|
自定义指标、完整时点对齐的曲线、同构表格及其单位/说明;独立 namespace |
|
|
|
实际运行假设、初始资金、参数、模型、来源、规则、权益和依赖版本 |
|
指定基准时保存全部基准输入及来源 |
|
请求绘图时保存的净值/可选超额/回撤图 |
|
|
CSV 为 UTF-8,日期为 ISO 8601,嵌套字段为 JSON 字符串,空表保留列头。整数记账字段保持整数;金额/价格 units 是 0.0001 CNY,税务每股收入 income_micros 是 0.000001 CNY/share。positions.csv 包含所有证券/bar 组合;未上市或已退市的空价格以内部 0 表示,不作为可交易价格。
当前交付标准 CSV/JSON,不要求 pandas/Arrow。Parquet、交互报告与产品侧资产管理可在实际消费者需要时追加。
来源记录与复现边界¶
result.run_info 是不可变 JSON 快照;to_dict() 每次返回新副本。它包含:
数据完整指纹、来源、证券顺序、日历、带有效期的规则、公司行动及带可知日期的研究复权因子;规则/行动另有 SHA-256。
初始资金及实际编译使用的佣金 ppm、最低佣金记账单位、滑点 ticks、税务政策版本。
收盘决策/下一输入开盘执行、意图定量模式、资金预留及明确支出上限、成交排序、原始价、不强制清仓等假设。
回调模块/名称与可读取时的源代码哈希;预计算权重则保留完整日期和数值及哈希;显式参数的初始快照。
Python、操作系统、DoriBT、NumPy,使用 Numba 时另记 Numba/llvmlite 版本;安装包内 Python 引擎文件的整体内容指纹。
不读取环境变量、不抓取闭包/全局变量/对象状态、不复制策略源代码或原始行情。回调来源不可读取时明确为 null,外部状态标记 not_captured。调用者仍需保留自己的原始数据、脚本、锁定环境以及显式随机种子;参数不能存放凭据,来源描述也不应带认证信息。指纹可以核对输入和程序是否一致,不能证明任意有外部状态的 Python 回调完全可复现。
run_info.fingerprint 标识执行记录;同一结果使用不同分析假设导出时,执行指纹保持相同,stats.json、基准和文件清单哈希随分析输入变化。可选绘图库版本不算执行依赖,其环境由研究项目的锁文件保留。
分钟结果¶
分钟回测的 result.sessions 为完整 bar 结束时点;账户/持仓 CSV 逐 bar 输出,包含 frozen_cash_units/frozen_quantity。Fill.timestamp 标识该成交最早可知的时点,同一 order_id 可有多行,订单金额/费用为累计值。
stats()/plot() 的基准须与完整分钟时点严格对齐,不自动重采样,不能直接按 252 个周期年化。report()/export(daily=True) 按日末采样,接受完整 bar 或精确交易日对齐的基准。