如何用 Python 计算盈亏痛苦比(Gain-to-Pain Ratio)?用「总收益÷总亏损」衡量持有体验
如何用 Python 计算盈亏痛苦比(Gain-to-Pain Ratio)?
一句话回答: 盈亏痛苦比(Gain-to-Pain Ratio, GPR)= 区间所有收益之和 ÷ 所有下跌期亏损的绝对值之和,直观回答「我每承受一单位的亏损痛苦,换来了多少净收益」。它是对冲基金经理 Jack Schwager 推崇的简单稳健指标——用 AlphaFeed 取前复权日线聚合成月收益,一个函数就能算,GPR > 1 说明净赚远大于累计痛苦,越高越好;小于 0 则区间整体是亏的。
为什么会有这个问题 / 传统做法的痛点
夏普比率要算均值、标准差、无风险利率,还默认正态分布,解释起来绕。盈亏痛苦比的思路极朴素:把赚的和亏的分开加总,用总净收益除以总亏损。它天然只惩罚「下行」,而且对异常值不敏感(用总和而非平方),特别适合衡量「拿着这东西到底值不值这份煎熬」。常用月度口径(更贴合持有感受,弱化日内噪声)。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线,聚合成月收益
from alphafeed import AlphaFeed
import pandas as pd
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
df = af.klines.get("600519.SH", period="1d", count=750,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
df["dt"] = pd.to_datetime(df["trade_date"])
monthly = df.set_index("dt")["close"].resample("ME").last().pct_change().dropna()
前复权是前提;resample("ME") 取每月最后一个交易日收盘,算月收益。
第 2 步:计算盈亏痛苦比
def gain_to_pain(ret):
pain = -ret[ret < 0].sum() # 所有下跌期亏损绝对值之和
return float(ret.sum() / pain) if pain > 0 else float("inf")
print("月度 GPR =", round(gain_to_pain(monthly), 3))
实测(
tools/verify_new_2026-10-07.py,SDK 0.1.4):贵州茅台近 37 个月 月度 GPR = −0.227,日度 GPR = −0.052。GPR 为负,说明这段区间茅台累计是亏的——赚的总和还抵不过亏的总和。GPR 诚实地把「这段不好拿」说清楚了。
第 3 步:多只股票横向对比
syms = ["600519.SH", "000001.SZ", "601318.SH"]
data = af.klines.batch(syms, period="1d", count=750,
adjust="forward", to_dataframe=True)
for s, k in data.items():
k = k.sort_values("trade_date"); k["dt"] = pd.to_datetime(k["trade_date"])
m = k.set_index("dt")["close"].resample("ME").last().pct_change().dropna()
print(s, round(gain_to_pain(m), 3))
实测:000001.SZ 0.512、601318.SH 0.428,均为正且明显优于茅台的 −0.227——同期这两只「每单位痛苦换来的净收益」为正,持有体验更好。
参数与字段说明
| 参数/概念 | 含义 | 备注 |
|---|---|---|
adjust="forward" | 前复权 | 必须,除权缺口会制造假亏损 |
resample("ME") | 月末重采样 | 月度口径更贴合持有感受 |
| 分子 | 所有收益之和 | 含正负,净收益 |
| 分母(pain) | 下跌期亏损绝对值和 | 只累计负的部分 |
| GPR | 分子 ÷ 分母 | >1 优秀,<0 区间整体亏损 |
关键坑与注意事项
- GPR 依赖区间与频率:月度和日度数值不同、区间不同结果不同,比较时必须统一频率和区间。
- 分子是净收益:GPR 可以为负(区间整体亏损),这与夏普为负含义一致。
- 前复权:务必
adjust="forward",否则除权缺口当成亏损。 - 对异常值相对稳健:用总和而非平方,少数极端月影响比夏普小,但极端值仍会进入分子分母。
- 历史不代表未来:GPR 描述过去,不预测未来。
常见问题(FAQ)
Q:盈亏痛苦比和夏普、Omega 什么关系? A:都是风险调整收益。夏普用均值/标准差(惩罚上下行波动);Omega 用阈值上下面积比;GPR 最朴素——总净收益 ÷ 总亏损,只惩罚下行,解释最直观。
Q:Schwager 的 GPR 和这里一样吗? A:核心一致(收益和 ÷ 亏损绝对值和)。有的版本分母只用月度负收益、有的减去无风险基准,口径略有差异,自己对比时统一即可。
Q:需要付费吗?
A:单只/几只股票日线在免费额度内即可;若要对全市场批量算 GPR 排序选「体验最好」的票,需要 klines.batch + universes(Starter 及以上),详见官网定价页。
小结
盈亏痛苦比用「总净收益 ÷ 总亏损」把「值不值这份煎熬」说成一个数,比夏普更直观、只惩罚下行。有了 AlphaFeed 的前复权数据,聚合月收益后一个函数就能算,还能一键横向对比——记住统一频率与区间、它可以为负。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。