返回文档
Python 量化入门

风险与绩效指标

夏普比率、最大回撤、年化收益、胜率、卡玛比率

回测报告里一堆指标,新手很容易只看"总收益"。但总收益高不代表策略好——可能是运气、可能波动巨大、可能回撤吓人。这一篇我们彻底搞懂常用的绩效指标。

收益类指标

1. 总收益(Total Return)

最直观的指标,简单粗暴:

python
total_return = (1 + returns).prod() - 1
# 或者
total_return = cum_value[-1] / cum_value[0] - 1

用途:快速看策略赚不赚钱 陷阱:不考虑时间长度和波动

2. 年化收益(Annualized Return)

把不同时间长度的收益拉到同一标准:

python
n_years = len(returns) / 252  # 252 个交易日 = 1 年
annual_return = (1 + total_return) ** (1 / n_years) - 1

用途:跨策略、跨周期对比 注意:复利计算,不是简单平均

风险类指标

3. 年化波动率(Annualized Volatility)

衡量收益的不稳定程度:

python
import numpy as np
annual_vol = returns.std() * np.sqrt(252)

用途:判断策略是否平稳 理解:年化波动 20% 意味着一年内收益大概率在 ±20% 之间波动

4. 最大回撤(Max Drawdown,MDD)

从历史最高点到最低点的最大跌幅,新手最该关注的指标

python
def max_drawdown(returns):
    cum = (1 + returns).cumprod()
    peak = cum.cummax()
    dd = (cum - peak) / peak
    return dd.min()

mdd = max_drawdown(df['strategy_return'])
print(f'最大回撤: {mdd:.2%}')

为什么重要

  • 一个年化 30% 但最大回撤 50% 的策略,你能拿得住吗?
  • 真实交易中,回撤决定你会不会爆仓、会不会割肉

经验值

  • 优秀策略:MDD < 15%
  • 可接受:15% - 25%
  • 危险:> 30%

5. 回撤持续时间(Drawdown Duration)

不只是回撤多深,还要看多久才能回本

python
def max_drawdown_duration(returns):
    cum = (1 + returns).cumprod()
    peak = cum.cummax()
    in_dd = cum < peak

    # 连续回撤的最大长度
    durations = []
    cur = 0
    for x in in_dd:
        if x:
            cur += 1
        else:
            if cur > 0:
                durations.append(cur)
            cur = 0
    return max(durations) if durations else 0

用途:评估策略的"忍耐成本" 经验:超过 1 年的回撤,大部分人都拿不住

风险调整收益

收益和风险都重要,单独看任一个都不够。下面是把两者结合的指标。

6. 夏普比率(Sharpe Ratio)

最经典的风险调整收益指标:

python
def sharpe_ratio(returns, rf=0.0, periods_per_year=252):
    excess = returns - rf / periods_per_year
    return excess.mean() / excess.std() * np.sqrt(periods_per_year)

公式:(年化收益 - 无风险利率) / 年化波动率

经验值

  • 2 → 非常好

  • 1 - 2 → 不错
  • 0.5 - 1 → 一般
  • < 0.5 → 不太行

⚠️ 夏普比率的陷阱 它把"上行波动"和"下行波动"一视同仁。一个稳定向上但偶尔大涨的策略,夏普会被"惩罚"。

7. 索提诺比率(Sortino Ratio)

夏普的改进版,只看下行波动:

python
def sortino_ratio(returns, rf=0.0, periods_per_year=252):
    excess = returns - rf / periods_per_year
    downside = excess[excess < 0].std() * np.sqrt(periods_per_year)
    if downside == 0:
        return np.nan
    return (excess.mean() * periods_per_year) / downside

用途:更符合人对"风险"的直觉感知 经验:通常比夏普高,因为分母更小

8. 卡玛比率(Calmar Ratio)

收益与最大回撤的比值:

python
def calmar_ratio(returns, periods_per_year=252):
    annual_return = (1 + returns).prod() ** (periods_per_year / len(returns)) - 1
    mdd = abs(max_drawdown(returns))
    return annual_return / mdd if mdd > 0 else np.nan

用途:直接告诉你"为了 1% 的回撤,你能赚多少" 经验

  • 3 → 极佳

  • 1 - 3 → 不错
  • < 1 → 一般

交易类指标

9. 胜率(Win Rate)

python
def win_rate(returns):
    positive = (returns > 0).sum()
    nonzero  = (returns != 0).sum()
    return positive / nonzero if nonzero > 0 else 0

陷阱:胜率高不一定赚钱。如果每次小赚(+1%)每次大亏(-10%),胜率 80% 也是亏的。

10. 盈亏比(Profit/Loss Ratio)

python
def profit_loss_ratio(returns):
    wins   = returns[returns > 0]
    losses = returns[returns < 0]
    if len(wins) == 0 or len(losses) == 0:
        return np.nan
    return wins.mean() / abs(losses.mean())

用途:和胜率配合看 经验:胜率 50% + 盈亏比 1.5 > 胜率 70% + 盈亏比 0.5

11. 凯利公式仓位(Kelly Criterion)

理论最优仓位比例:

python
def kelly_fraction(win_rate, profit_loss_ratio):
    return win_rate - (1 - win_rate) / profit_loss_ratio

用途:参考用,实战要用半凯利或更保守

完整的指标计算函数

把上面的指标整合成一个工具:

python
import pandas as pd
import numpy as np

def calc_metrics(returns, periods_per_year=252):
    """
    输入:日收益率 Series
    输出:所有指标 dict
    """
    returns = returns.dropna()
    if len(returns) == 0:
        return {}

    # 收益
    total = (1 + returns).prod() - 1
    n_years = len(returns) / periods_per_year
    annual = (1 + total) ** (1 / n_years) - 1 if n_years > 0 else 0

    # 风险
    vol = returns.std() * np.sqrt(periods_per_year)

    cum = (1 + returns).cumprod()
    dd = (cum / cum.cummax() - 1)
    mdd = dd.min()

    # 风险调整收益
    sharpe = (returns.mean() / returns.std() * np.sqrt(periods_per_year)) if returns.std() > 0 else 0
    downside = returns[returns < 0].std() * np.sqrt(periods_per_year) if (returns < 0).any() else 0
    sortino = (returns.mean() * periods_per_year / downside) if downside > 0 else np.nan
    calmar = (annual / abs(mdd)) if mdd != 0 else np.nan

    # 交易
    win_rate = (returns > 0).sum() / (returns != 0).sum() if (returns != 0).sum() > 0 else 0
    wins = returns[returns > 0]
    losses = returns[returns < 0]
    pl_ratio = wins.mean() / abs(losses.mean()) if len(losses) > 0 and losses.mean() != 0 else np.nan

    return {
        '总收益':       f'{total:.2%}',
        '年化收益':     f'{annual:.2%}',
        '年化波动':     f'{vol:.2%}',
        '最大回撤':     f'{mdd:.2%}',
        '夏普比率':     f'{sharpe:.2f}',
        '索提诺比率':   f'{sortino:.2f}',
        '卡玛比率':     f'{calmar:.2f}',
        '日胜率':       f'{win_rate:.2%}',
        '盈亏比':       f'{pl_ratio:.2f}',
    }

怎么综合判断一个策略

不要看单一指标,要看组合

指标看什么
年化收益是否值得做
最大回撤是否拿得住
夏普比率风险收益比
卡玛比率回撤的代价
胜率 + 盈亏比心理可承受性
回撤持续时间是否会让你怀疑人生

好策略的特征

  • ✅ 年化 > 15%
  • ✅ 最大回撤 < 20%
  • ✅ 夏普 > 1
  • ✅ 卡玛 > 1
  • ✅ 净值曲线平滑、向右上

小结

到这里你已经会用十几个指标全方位评估一个策略。下一篇 多因子选股入门,我们从单股票策略升级到组合选股。

📌 新手常见误区

  1. 只看总收益 → 忽略了风险
  2. 追求超高夏普 → 往往是过拟合
  3. 看不见回撤 → 实盘必爆仓
  4. 用太短的回测期 → 没经历过完整牛熊

一个策略至少要经历3 年以上的回测才有参考价值。