/** * A 股策略回测引擎(纯函数、零依赖)—— dsh-strategy 域。 * * 来源:本模块从 A 股策略研究台(/root/projects/a-share-strategy-lab)的实验代码 * 移植而来,去掉实验脚手架、补齐类型与契约,使其成为可复用的库函数。 * * 与上游 dsh-quant 的 backtest 域的区别(这是本 fork 的核心增量): * - 上游:单标的、全仓进出、固定 feeRate、无杠杆护栏 * - 本模块:**多标的组合**、**目标权重**语义、A 股真实成本拆分、 * **总权重硬护栏(防止隐性杠杆)**、逐 bar 开盘价成交(无未来函数) * * 契约: * - 信号在 bar i 收盘后给出目标权重,在 bar i+1 的**开盘价**成交 * - 权重向量 targetWeight[i][k] ∈ [0,1],引擎强制 Σw ≤ 1(超出则等比归一) * - 所有函数为纯函数,无共享状态,可并发调用 */ /** A 股交易成本参数(单边,按成交额比例;默认值为常见散户费率)。 */ export interface CostModel { /** 佣金率(双边收取) */ commission: number; /** 印花税率(仅卖出收取) */ stampDuty: number; /** 过户费率(双边收取) */ transferFee: number; /** 滑点率(按成交价比例,双边) */ slippage: number; } /** 默认成本模型:佣金万 2.5、印花税千 0.5、过户费十万分之一、滑点万 5。 */ export declare const DEFAULT_COST: CostModel; /** 计算买入单边成本率。 */ export declare function buyCostRate(cost?: CostModel): number; /** 计算卖出单边成本率(含印花税)。 */ export declare function sellCostRate(cost?: CostModel): number; /** 单根 K 线(与 dsh-quant 的 candle 结构兼容,另接受 close/open 等短名)。 */ export interface Bar { /** 时间戳(毫秒) */ t?: number; /** 开盘价 */ o?: number; /** 最高价 */ h?: number; /** 最低价 */ l?: number; /** 收盘价(必需) */ c: number; /** 成交量 */ v?: number; /** 兼容别名:open */ open?: number; /** 兼容别名:close */ close?: number; } /** 读取收盘价(兼容 c / close 两种字段名)。 */ export declare function barClose(b: Bar): number; /** 读取开盘价(兼容 o / open;缺失时退回收盘价)。 */ export declare function barOpen(b: Bar): number; /** * 简单移动平均。返回与输入等长,前 window-1 位为 null。 * @param values 数值序列 * @param window 窗口(>= 1) */ export declare function sma(values: readonly number[], window: number): (number | null)[]; /** 滚动总体标准差。返回与输入等长,前 window-1 位为 null。 */ export declare function rollingStd(values: readonly number[], window: number): (number | null)[]; /** 逐期简单收益率(等长,首位 0)。 */ export declare function simpleReturns(close: readonly number[]): number[]; /** 回测选项。 */ export interface BacktestOptions { /** 年化期数(每年 bar 数)。默认 243(A 股交易日);加密 7×24 用 365。 */ barsPerYear?: number; /** 成本模型。 */ cost?: CostModel; /** * 单 bar 总权重上限(硬护栏)。 * 信号给出的 Σw 超过该值时按比例归一 —— 这防止「对每只标的独立给满仓」 * 造成的隐性杠杆(实测该 bug 会把 Calmar 从 0.13 虚增到 132)。 */ grossCap?: number; } /** 回测结果。 */ export interface BacktestResult { /** 等长净值曲线(初始 1) */ equity: number[]; /** 逐期收益(等长,首位 0) */ returns: number[]; /** 区间总收益率 % */ totalReturnPct: number; /** 最大回撤 % */ maxDrawdownPct: number; /** 年化夏普(按 barsPerYear 年化) */ sharpe: number; /** 年化收益率 % */ annualReturnPct: number; /** Calmar = 年化收益 / 最大回撤 */ calmar: number; /** 平均单期换手率(占组合权益比例) */ avgTurnover: number; /** bar 数 */ bars: number; /** 归一前信号的最大总权重(> grossCap 表示策略曾超配) */ maxGrossRaw: number; /** 累计交易成本(占初始权益比例) */ totalCostRatio: number; } /** * 目标权重组合回测:每根 bar 给出目标权重,下一根开盘成交。 * * @param bars 每个标的的 K 线序列(等长) * @param signals targetWeight[i][k] = bar i 收盘后对标的 k 的目标权重 * @param options 年化期数、成本模型、总权重上限 * @returns 净值曲线与绩效指标 * * @example * ```ts * // 等权持有两只标的 * const w = bars.length === 2 ? [0.5, 0.5] : [] * const signals = bars[0].map(() => w) * const r = backtestTargetWeights(bars, signals) * ``` */ export declare function backtestTargetWeights(bars: readonly (readonly Bar[])[], signals: readonly (readonly number[])[], options?: BacktestOptions): BacktestResult; /** 单标的便捷回测:close 序列 + 目标仓位序列(0..1)。 */ export declare function backtestSingle(barSeries: readonly Bar[], targetWeights: readonly number[], options?: BacktestOptions): BacktestResult; /** 最大回撤(返回正比例,如 0.15 表示 15%)。 */ export declare function maxDrawdown(equity: readonly number[]): number; /** 年化收益率(小数)。 */ export declare function annualizedReturn(equity: readonly number[], barsPerYear: number): number; /** * 年化夏普比率(无风险利率按 0)。 * @param returns 逐期收益 * @param barsPerYear 年化期数(A 股 243,加密 365) */ export declare function sharpeRatio(returns: readonly number[], barsPerYear?: number): number; /** 下行波动率(年化,只统计负收益期)。 */ export declare function downsideDeviation(returns: readonly number[], barsPerYear?: number): number; /** * 等权买入持有基准:对传入的全部标的各分配 1/K 权重并全程持有。 * * 用途:任何策略回测都必须有基准对照 —— 在单边下跌市里, * "策略亏 3% 而基准亏 10%" 与 "策略赚 3% 而基准赚 10%" 是相反的结论。 * * @param bars 标的多序列 * @param options 年化期数与成本模型 * @returns 与 backtestTargetWeights 同结构的回测结果 */ export declare function equalWeightBenchmark(bars: readonly (readonly Bar[])[], options?: BacktestOptions): BacktestResult;