第十九章:回测框架:评估指标(准确率、召回率、夏普比率)

回测做完了,策略跑了一堆曲线,然后呢?

说实话,我见过不少交易员,回测曲线画得漂漂亮亮,一问具体指标就支支吾吾。嗯,这其实挺危险的。回测不是看个净值曲线就完事了,你得用科学的指标去衡量它——到底这个策略是真的能赚钱,还是纯粹运气好?

这一章,我们就来聊聊回测框架里最核心的三个评估指标:准确率、召回率、夏普比率。我个人习惯把它们分成两类来看——一类看“能不能抓到机会”,另一类看“赚得值不值得”。

19.1 准确率与召回率:你的策略到底准不准?

先说说准确率和召回率。这两个指标最早是从分类模型里来的,但在波动率曲面异常点检测这个场景下,它们非常实用。

准确率(Precision),说白了就是:你预测的异常点里,有多少是真的异常?

公式很简单:

Precision = TP / (TP + FP)

其中 TP 是真正例(你预测异常,实际也异常),FP 是假正例(你预测异常,实际正常)。

召回率(Recall),则是:所有真正的异常点里,你抓到了多少?

Recall = TP / (TP + FN)

FN 是假负例(你漏掉的异常点)。

我在项目中遇到过一件事。有一次我开发了一个波动率曲面异常检测模型,准确率高达 95%,我挺高兴的。结果一查召回率,只有 30%。什么意思?就是它虽然报的异常点基本都对,但漏掉了七成真正的异常。这在交易里是很要命的——你错过了太多该规避的风险。

核心观点:准确率和召回率要一起看。单独看一个,容易被误导。

19.2 F1 分数:一个折中的好办法

那有没有一个指标能综合这两者?有,F1 分数。

F1 = 2 * (Precision * Recall) / (Precision + Recall)

F1 是准确率和召回率的调和平均数。它不会让你偏科。如果你只追求准确率而忽略召回率,F1 会很低;反过来也一样。

我个人习惯在回测报告里同时展示这三个指标。准确率告诉你“别乱报”,召回率告诉你“别漏报”,F1 告诉你“整体水平”。

19.3 夏普比率:赚得多不如赚得稳

好,现在我们知道策略能不能抓到异常点了。但抓到之后呢?赚了多少?风险多大?

这就轮到夏普比率登场了。

夏普比率(Sharpe Ratio)衡量的是:每承担一单位风险,你能获得多少超额收益。

Sharpe Ratio = (Rp - Rf) / σp

其中:

  • Rp:策略年化收益率
  • Rf:无风险利率(通常用国债收益率)
  • σp:策略年化波动率(风险)

你想想看,如果一个策略年化收益 30%,但波动率高达 40%,夏普比率只有 0.75。另一个策略年化收益 15%,波动率只有 10%,夏普比率是 1.5。哪个更好?我个人更倾向于后者。为什么?因为波动率低,意味着回撤小,持有体验好,你更容易坚持执行下去。

实战小技巧:我一般要求策略的夏普比率至少大于 1.0,最好在 1.5 以上。低于 1.0 的策略,说实话,风险调整后的收益不太够看。

19.4 一个完整的回测评估示例

光说不练假把式。我们写一段 Python 代码,把这三个指标算出来。

import numpy as np
import pandas as pd

def calculate_precision_recall(y_true, y_pred):
    """
    计算准确率和召回率
    y_true: 真实标签(1=异常,0=正常)
    y_pred: 预测标签
    """
    tp = np.sum((y_true == 1) & (y_pred == 1))
    fp = np.sum((y_true == 0) & (y_pred == 1))
    fn = np.sum((y_true == 1) & (y_pred == 0))
    
    precision = tp / (tp + fp) if (tp + fp) > 0 else 0
    recall = tp / (tp + fn) if (tp + fn) > 0 else 0
    f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
    
    return precision, recall, f1

def calculate_sharpe_ratio(returns, risk_free_rate=0.02):
    """
    计算夏普比率
    returns: 策略日收益率序列
    risk_free_rate: 年化无风险利率
    """
    excess_returns = returns - risk_free_rate / 252  # 转换为日度
    sharpe = np.sqrt(252) * excess_returns.mean() / returns.std()
    return sharpe

# 模拟数据
np.random.seed(42)
y_true = np.random.choice([0, 1], size=1000, p=[0.95, 0.05])
y_pred = np.random.choice([0, 1], size=1000, p=[0.9, 0.1])

precision, recall, f1 = calculate_precision_recall(y_true, y_pred)
print(f"准确率: {precision:.3f}")
print(f"召回率: {recall:.3f}")
print(f"F1 分数: {f1:.3f}")

# 模拟收益率
daily_returns = np.random.normal(0.001, 0.02, 1000)
sharpe = calculate_sharpe_ratio(daily_returns)
print(f"夏普比率: {sharpe:.3f}")

这段代码很简单,但很实用。你把它集成到你的回测框架里,每次跑完策略,自动输出这几个指标,一目了然。

19.5 知识体系图

为了让你更直观地理解这几个指标之间的关系,我画了一张图:

回测评估指标体系 回测评估指标 异常检测能力 风险调整收益 准确率 召回率 F1 分数 夏普比率 年化收益率 / 波动率 准确率 + 召回率 → 异常点抓取质量 | 夏普比率 → 风险调整后收益

19.6 避坑指南

最后,分享几个我踩过的坑:

  • 不要只看夏普比率。 夏普比率高不代表策略一定好。如果策略用了高杠杆,夏普比率可能被扭曲。我一般会同时看最大回撤和卡玛比率(Calmar Ratio)。
  • 准确率和召回率的阈值怎么定? 这取决于你的风险偏好。如果你更怕漏掉异常点(比如风控场景),那就优先保证召回率。如果你更怕误报(比如交易成本高),那就优先保证准确率。
  • 样本外测试很重要。 我曾经在样本内跑出过夏普比率 2.5 的策略,一到样本外直接变成 0.3。嗯,过拟合了。所以一定要做样本外测试,或者用交叉验证。

警告: 不要为了美化回测指标而做数据窥探(Data Snooping)。反复调整参数直到指标好看,这叫“曲线拟合”,不是真正的策略开发。

好了,这一章的内容就到这里。准确率、召回率、夏普比率,这三个指标是你回测框架的基石。把它们用好了,你就能更客观地评价自己的策略,而不是凭感觉说“这个策略不错”。


公众号:蓝海资料掘金营,微信deep3321