如何用 Python 计算滚动夏普比率?(看风险调整收益随时间的变化)
如何用 Python 计算滚动夏普比率?
一句话回答: 全样本只给一个夏普值,掩盖了「近期是不是变差了」。滚动夏普用滚动窗口内的「平均收益 ÷ 收益标准差 × √252」算出随时间变化的夏普序列,能看出风险调整后收益何时走强、何时恶化。用 AlphaFeed 取回前复权日线算日收益,几行 pandas 的 rolling 就能得到。
为什么会有这个问题 / 传统做法的痛点
一只票/一个策略整段算出夏普=1.2,但它可能前两年很好、近半年很差——静态值把这掩盖了。做风控、决定是否继续持有/运行策略,你需要知道当前阶段的风险调整收益。滚动夏普正好把它画成时间序列。坑在于:用前复权收益、统一年化口径(日频 ×√252)、窗口别太短(否则噪音大)。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线算日收益
from alphafeed import AlphaFeed
import numpy as np
import pandas as pd
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
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)
ret = df["close"].pct_change()
ret.index = pd.to_datetime(df["trade_date"])
第 2 步:计算滚动夏普
TRADING_DAYS = 252
RF_DAILY = 0.0 # 简化:无风险利率按 0
def rolling_sharpe(ret, window=120, rf=0.0):
excess = ret - rf
mean = excess.rolling(window).mean()
std = excess.rolling(window).std(ddof=1)
return np.sqrt(TRADING_DAYS) * mean / std
df_rs = pd.DataFrame({
"sharpe_60": rolling_sharpe(ret, 60),
"sharpe_120": rolling_sharpe(ret, 120),
})
print(df_rs.tail(5).to_string())
参数与含义:
| 参数/字段 | 含义 | 常用值 |
|---|---|---|
window | 滚动窗口(交易日) | 60(约3月)/120(约半年) |
rf | 无风险日利率 | 简化为 0 |
√252 | 日频年化因子 | A 股约 252 交易日/年 |
| 滚动夏普 ↑ | 风险调整收益转好 | — |
第 3 步:对比滚动夏普与静态夏普
cur = df_rs["sharpe_120"].dropna()
static = np.sqrt(TRADING_DAYS) * ret.mean() / ret.std(ddof=1)
print(f"当前滚动夏普(120)={cur.iloc[-1]:.2f}")
print(f"区间[min={cur.min():.2f}, max={cur.max():.2f}, 均值={cur.mean():.2f}]")
print(f"静态全样本夏普={static:.2f}(滚动揭示了它掩盖的时变)")
第 4 步:批量算多只的当前滚动夏普
codes = ["600519.SH", "000001.SZ", "601318.SH", "000858.SZ"]
data = af.klines.batch(codes, period="1d", count=400,
adjust="forward", to_dataframe=True)
rows = []
for s, k in data.items():
r = k.sort_values("trade_date")["close"].pct_change()
rs = rolling_sharpe(r, 120)
rows.append({"symbol": s, "sharpe_120_now": round(float(rs.dropna().iloc[-1]), 2)})
print(pd.DataFrame(rows).to_string(index=False))
关键坑与注意事项
- 用前复权收益:除权日伪跳变会污染夏普。
- 统一年化口径:日频 ×√252;周频 ×√52、月频 ×√12,别混用。
- 窗口是权衡:60 日灵敏但噪音大,120/250 日稳但滞后;可多窗口对照。
- 夏普把上涨波动也当风险:若想只罚下行,用滚动索提诺(见索提诺/卡玛一文)。
- 窗口内近似常数无风险利率:严谨可减去货基/国债日收益,对横向对比影响小。
- 样本要足够:
window需预热,count要明显大于窗口,忽略头部 NaN。
常见问题(FAQ)
Q:滚动夏普和静态夏普差别大吗? A:常常很大。静态值是整段平均,滚动值能暴露「近期恶化」或「某段特别好」,对风控和是否继续持有更有参考意义。
Q:窗口用 60 还是 120? A:短线/预警用 60,稳健评估用 120/250。建议至少看两个窗口,别只信一个。
Q:为什么要 √252? A:把日频夏普年化。日收益的均值和标准差都按 √T 规则年化,A 股一年约 252 个交易日。
Q:需要付费吗?
A:单只历史日线有免费额度;批量算多只滚动夏普(klines.batch)需 Starter 及以上。详见定价页。
小结
滚动夏普把「一个静态夏普数字」变成「随时间变化的风险调整收益曲线」,能看清近期是变好还是变差。用 AlphaFeed 取前复权日收益,rolling().mean()/rolling().std()*√252 几行即可。关键是前复权、统一年化口径、合理选窗口,并结合索提诺等指标综合判断。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。