QuantStats 核心绩效指标有哪些?对照表
依据官方 README stats 方法列表(约 60 个指标,此处列高频核心项)。
| 指标 | 方法 | 含义 | 适用场景 | 注意点 |
|---|---|---|---|---|
| 夏普比率 | qs.stats.sharpe(returns) | 单位总波动的超额收益 | 横评不同策略的风险调整收益 | 同时惩罚上行与下行波动,对非正态收益有偏 |
| 索提诺比率 | qs.stats.sortino(returns) | 只按下行波动调整的超额收益 | 更关注下跌风险、讨厌上行波动被扣分的场景 | 需要明确下行波动口径,结果对阈值敏感 |
| 调整后索提诺 | qs.stats.adjusted_sortino(returns) | 校正后的索提诺比率 | 对收益分布偏度/峰度敏感的更稳健口径 | 以官方实现口径为准 |
| 卡玛比率 | qs.stats.calmar(returns) | 年化收益 / 最大回撤 | 衡量每单位最大回撤换来的收益 | 对最大回撤极其敏感,回撤越大指标越差 |
| 最大回撤 | qs.stats.max_drawdown(returns) | 历史峰值到谷底的最大跌幅 | 评估最坏情况下的亏损承受力 | 只反映历史单次最差,不预测未来 |
| 波动率 | qs.stats.volatility(returns) | 收益率标准差(年化) | 衡量整体价格波动幅度 | 不含方向,只测波动大小 |
| VaR / CVaR | qs.stats.value_at_risk / conditional_value_at_risk | 在险价值 / 期望损失(尾部风险) | 机构风控常用的尾部风险度量 | 依赖置信度与分布假设,CVaR 比 VaR 更保守 |
| 胜率 / 盈亏比 | qs.stats.win_rate / win_loss_ratio | 正收益期占比 / 盈亏比例 | 策略择时/选股的方向性评估 | 按「收益期」而非逐笔交易口径(见下方说明) |
| 盈亏因子 | qs.stats.profit_factor(returns) | 正收益和 / 负收益和 | 衡量盈利期相对亏损期的累计强度 | 比率口径,需结合胜率一起看 |
| 凯利准则 | qs.stats.kelly_criterion(returns) | 理论上的下注比例上限 | 仓位管理参考,非实际下单建议 | 对收益估计极敏感,实盘常需减半(半凯利) |
QuantStats 指标怎么用?四步实操
从导入到多指标组合,四步即可落地;返回值为该指标在当前收益序列下的标量结果。
1. 导入并扩展 pandas:import quantstats as qs; qs.extend_pandas() —— 把指标方法挂到 pandas Series 上,之后可直接 stock.sharpe()。
2. 获取收益序列:stock = qs.utils.download_returns('META') —— 下载 META 的日收益序列;也可换成你自己的 pd.Series。
3. 计算夏普:qs.stats.sharpe(stock) 或扩展后 stock.sharpe() —— 返回单位风险的超额收益(示例值 0.76,仅为演示,非真实承诺)。
4. 组合多指标:qs.stats.sortino(stock)、qs.stats.max_drawdown(stock)、qs.stats.calmar(stock) —— 一次算出下行调整收益、最大回撤与卡玛比率,交叉印证策略风险。
QuantStats 指标分哪几类?约 60 个方法清单
按分析目的分成六类,每类列代表方法与用途;完整列表以 dir(qs.stats) 输出为准。
| 分类 | 代表方法 | 分析用途 |
|---|---|---|
| 收益 | cagr / expected_return / comp / compsum / geometric_mean / ghpr / ror / avg_return | 看策略「赚了多少、多快」,衡量复利与累计增长 |
| 风险调整 | sharpe / sortino / adjusted_sortino / calmar / rar / risk_return_ratio / information_ratio | 把收益「除以风险」,比较不同风险水平下的性价比 |
| 风险/尾部 | volatility / value_at_risk(var) / conditional_value_at_risk(cvar) / tail_ratio / expected_shortfall / skew / kurtosis | 量化波动与极端亏损,评估尾部风险与分布形态 |
| 回撤 | max_drawdown / drawdown_details / to_drawdown_series / recovery_factor / ulcer_index | 度量峰值回撤与恢复能力,反映最大亏损与回血速度 |
| 胜率/盈亏 | win_rate / win_loss_ratio / avg_win / avg_loss / profit_factor / payoff_ratio / consecutive_wins / consecutive_losses | 拆解盈利期与亏损期的结构,判断择时/选股质量 |
| 其他/进阶 | kelly_criterion / r_squared / greeks / implied_volatility / risk_of_ruin / montecarlo 系列 / outliers 系列 | 仓位管理、期权希腊字母、破产概率、离群值等专项 |
什么场景该用哪些 QuantStats 指标?
不同分析目的对应不同指标,避免「一把尺子量所有策略」。
| 分析目的 / 场景 | 推荐指标 | 为什么选它 | 注意点 |
|---|---|---|---|
| 整体性价比 | sharpe / calmar / rar | 把收益除以风险,横评不同策略的风险调整后表现 | rf 与年化周期必须两边一致,否则不可比 |
| 下行风险 | sortino / adjusted_sortino | 只惩罚下跌波动,更贴合「怕亏」的心理与实际风险 | 区间内全为上涨时下行波动为 0,指标无意义 |
| 回撤承受 | max_drawdown / ulcer_index | 直接看最坏回撤与回撤持续时间,评估持有体验 | 日线口径不含盘中低点,盘中浮亏可能更深 |
| 尾部风险 | value_at_risk / conditional_value_at_risk / tail_ratio | 量化极端亏损概率,机构风控常用 | 依赖置信度与样本长度,样本太短会剧烈跳变 |
| 相对基准 | information_ratio / r_squared / rolling_beta | 衡量相对基准的超额收益与跟踪敏感度 | 策略与基准交易日历必须对齐,否则结果为 nan |
| 仓位管理 | kelly_criterion / risk_of_ruin | 估算理论仓位比例与破产概率,仅作参考 | 对历史收益估计极敏感,实盘常用半凯利 |
| 低频(周/月调仓) | cagr / volatility / calmar | 低频样本点少,比率型指标噪声大,更适合看年化与回撤 | 要显式声明年化周期(月频与日频不可混算) |
| 只关心极端亏损 | conditional_value_at_risk / expected_shortfall / tail_ratio | CVaR 描述「最差那部分」的平均亏损,比 VaR 更保守 | 口径以官方实现为准,跨平台对比前先统一置信度 |
Period-Based 与 Trade-Based 口径有什么区别?
1. Win Rate = 正收益期占比,而非逐笔胜率。
2. Consecutive Wins/Losses = 连续正/负收益期,而非连续盈利/亏损交易。
3. Payoff Ratio = 平均正收益期 / 平均负收益期,而非单笔盈亏均值比。
4. Profit Factor = 正收益和 / 负收益和,按周期聚合而非按笔聚合。
QuantStats 指标算出 NaN 或 inf 怎么排查?
指标为 NaN / inf 或数值离谱时,先怀疑收益序列本身,而不是指标方法。下表按「现象 → 常见原因 → 怎么判断 → 怎么处置」四列排查。
| 现象 / 报错 | 常见原因 | 怎么判断 | 怎么处置 |
|---|---|---|---|
qs.stats.sharpe() 返回 nan | 收益序列含缺失值(停牌、假期、拼接不同数据源未对齐) | returns.isna().sum() 不为 0 | 先 returns.dropna() 再算;跨市场数据先对齐交易日历 |
| 多个指标同时变 inf 或 -inf | 序列含 -100% 或超过 ±100% 的异常收益(复权、拆股未处理) | returns.min() / returns.max() 出现极端值 | 改用复权价重算;-100% 会让几何收益与波动率无定义 |
| 夏普高得不符合常识 | 用了周频/月频序列却按日频周期年化 | 看序列频率与调用时传入的年化参数 | 按频率显式声明年化周期(月频 12、周频 52,口径以官方实现为准) |
sortino() / adjusted_sortino() 为 nan | 样本区间内没有负收益,下行波动为 0 | (returns < 0).any() 为 False | 换含下跌的更长区间;全正收益序列的下行指标本身无意义 |
| 信息比率、滚动 beta 全为 nan | 策略与基准的日期索引不一致(时区、交易日不同) | stock.index.equals(benchmark.index) 为 False | 先 reindex / align 到同一交易日历,再算相对指标 |
| VaR 结果随置信度剧烈跳变 | 样本太短,尾部样本点不足 | 看 len(returns) 与收益分布直方图 | 扩大样本,或同时给 CVaR 做对照,避免单点结论 |
| 指标之间自相矛盾(夏普高但回撤深) | 收益分布非正态、尖峰厚尾,波动率低估了真实风险 | 看 qs.stats.skew() 与 kurtosis() | 补看 sortino、cvar、ulcer_index,不要用单指标下结论 |
1. 先给收益序列做体检:print(returns.isna().sum(), returns.min(), returns.max(), returns.index.freq) —— 一次看清缺失、极值、频率三个最容易出问题的点。预期输出:缺失计数、最小值、最大值与索引频率。
2. 再检查基准是否对齐:bench = qs.utils.download_returns('SPY'); print(returns.index.equals(bench.index)) —— 输出 False 说明两条序列不在同一交易日历上,此时任何相对指标都不可信。
3. 最后统一年化口径复算:按序列频率显式声明年化周期后重算 qs.stats.sharpe(returns) —— 与默认调用结果对照,两者差值就是口径带来的误差,而不是策略差异。
QuantStats 算出的指标和别人算的不一样,差在哪?
同一份收益序列,QuantStats 与国内行情软件、其他回测框架的结果常有差异,差异大多来自年化方式、无风险利率、回撤粒度三类口径。下表给对账思路,具体以各方文档与官方实现为准。
| 指标 | QuantStats 口径要点 | 别处常见的口径 | 差异从哪来 | 怎么核对 |
|---|---|---|---|---|
| 夏普比率 | 按收益率序列与调用时传入的无风险利率参数计算(默认值以官方实现为准) | 部分平台固定扣减一年期定存或国债利率 | 无风险利率取值不同,低波动策略差异被放大 | 两边都固定 rf 后再比,记录 rf 取值 |
| 年化收益(CAGR) | 由收益序列频率推导年化 | 有的平台按自然日 365 天折算 | 交易日与自然日年化的差异,在短样本上尤其明显 | 先统一年化周期,再看数值是否收敛 |
| 最大回撤 | 基于净值峰谷,按传入数据的粒度计算 | 有的平台用盘中价或复权后净值 | 数据粒度不同,盘中口径往往回撤更深 | 用同一份日线净值复算,确认粒度和复权方式 |
| 波动率 | 收益率标准差并按频率年化 | 有的用对数收益、有的用简单收益 | 收益计算方式不同,长期序列差异会累积 | 先统一收益计算方式,再比波动率 |
| 胜率 | 正收益「期」占比,统计单元是收益周期 | 交易软件多为逐笔交易胜率 | 周期口径与交易口径本质不同,不是谁算错 | 先确认统计单元,再决定能不能直接比 |
| 卡玛比率 | 年化收益 ÷ 最大回撤 | 有的在分子上再扣无风险利率 | 分子口径不同,结果系统性偏移 | 确认分子是否扣无风险利率,注明再比 |
| 信息比率 | 超额收益 ÷ 跟踪误差,需要显式传入基准序列 | 有的直接用某个指数收益当基准 | 基准标的与频率不一致时,跟踪误差完全不同 | 对齐基准与频率后再比,注明基准标的 |
怎么把 QuantStats 指标算成一页对照表?
从一条收益序列到可直接存档的指标表,五步即可复现;每步给出命令、说明与预期输出形态。
1. 准备收益序列:import quantstats as qs; stock = qs.utils.download_returns('META') —— 得到日频收益序列,也可以换成自己的 pd.Series(索引为交易日)。预期输出:一个日频 Series,长度等于区间内交易日数。
2. 清洗:stock = stock.dropna() —— 去掉缺失日后,指标才不会被 NaN 污染。预期输出:长度略减,stock.isna().sum() 为 0。
3. 组装指标字典:把 qs.stats.sharpe(stock)、qs.stats.sortino(stock)、qs.stats.max_drawdown(stock)、qs.stats.calmar(stock)、qs.stats.volatility(stock)、qs.stats.value_at_risk(stock) 放进同一个 dict —— 一次拿到收益、下行、回撤、尾部四类代表指标。
4. 落成表格:pd.DataFrame([metrics]).T.to_markdown() —— 得到可直接贴进研究记录的 Markdown 指标表(依赖 tabulate,官方依赖清单里已包含)。预期输出:一张 6 行的指标表。
5. 出报告对照:qs.reports.html(stock, 'SPY') —— 把指标与图表打包成 HTML,便于与同事核对同一份区间。预期输出:生成一个 HTML 文件,浏览器打开即可查看。
| 指标 | 这张表里该看什么 | 常见误读 | 结论怎么写才站得住 |
|---|---|---|---|
| 夏普比率 | 与同区间、同无风险利率的其他策略横向比 | 拿不同频率、不同 rf 的夏普直接比大小 | 写清样本区间、频率与 rf 取值,再给数值 |
| 索提诺比率 | 只关心下行波动时的风险调整收益 | 当成「更宽松的夏普」直接替换使用 | 与夏普并列给出,差异大时说明上行波动来自哪里 |
| 最大回撤 | 最坏持有体验与回撤恢复时间 | 当作未来最大亏损的上限 | 标注为历史单次最差值,并说明数据粒度 |
| 波动率 | 整体波动幅度,用于横向比较 | 把波动率当成风险的全部 | 补上尾部指标(VaR/CVaR)一起写 |
| 胜率 / 盈亏因子 | 方向性与盈亏结构的稳定性 | 与逐笔交易胜率混着讲 | 注明统计单元是收益周期,不是单笔交易 |
| 卡玛比率 | 每单位最大回撤换来的年化收益 | 与夏普混用,不看分母是什么 | 说明分母是最大回撤而非波动率 |
常见问题有哪些?
QuantStats 有多少个指标?
quantstats.stats 模块约 60 个方法,覆盖收益、风险、风险调整、回撤、尾部风险、胜率盈亏等,具体以官方 README 的 dir(qs.stats) 列表为准。
夏普比率怎么算?
qs.stats.sharpe(returns) 或扩展 pandas 后 returns.sharpe(),返回单位风险的超额收益;默认参数与年化口径以官方实现为准。
索提诺和夏普有什么区别?
夏普用总波动(含上行),索提诺只按下行波动惩罚;更怕下跌风险时可优先看 sortino/adjusted_sortino,具体口径以官方实现为准。
Win Rate 是逐笔胜率吗?
不是。它是「正收益期占比」,按收益率序列周期(日/周/月)计算,非逐笔成交口径;对持仓多日的自由交易者会有偏差。
这些指标和 EasyClaw 的量化分析一样吗?
本机 quant-analyst 技能也能输出 Sharpe、最大回撤等风险指标,但它是独立的量化研究路线,与 QuantStats 无已证实集成,输出形式与口径可能不同,选择时需自行比对。
指标结果能用于实盘吗?
不能。绩效指标仅代表历史收益序列表现,不构成投资建议或收益保证,实盘需自行核验数据、成本与风险。