做量化交易这些年,我见过太多同行把90%的精力都花在策略开发上,却对绩效分析草草了事。直到2018年那波行情回撤,才让我真正明白:策略绩效分析不是走形式,而是量化交易的"体检中心"。它能告诉你策略的盈利质量、风险暴露和失效征兆,就像给策略做CT扫描一样。
举个真实案例:去年我们团队有个年化收益35%的CTA策略,表面数据很漂亮。但通过深度绩效分析发现,其收益主要来自两次极端行情,正常市况下胜率不足45%。果然在2023年Q2的市场平稳期,这个策略连续11天亏损。如果没有提前发现这个隐患,后果不堪设想。
年化收益率计算绝不是简单的收益*年化系数。我常用的是几何年化公式:
python复制def annualized_return(daily_returns, periods=252):
cum_return = np.prod(1 + daily_returns) - 1
return (1 + cum_return) ** (periods/len(daily_returns)) - 1
这里有个关键细节:对于杠杆策略,必须区分毛收益率和净收益率。去年有个加密货币套利策略,毛年化68%看起来很诱人,但扣除资金成本后实际只有9.2%。
重要提示:千万不要只看最近三个月的短期收益!我建议至少包含一个完整市场周期(通常3-5年)的数据。
最大回撤的计算有门道。很多新手直接用peak-to-trough,但更科学的方法是:
python复制def max_drawdown(returns):
wealth = (1 + returns).cumprod()
peaks = wealth.expanding().max()
drawdowns = (wealth - peaks) / peaks
return drawdowns.min()
波动率指标要特别注意时间尺度。日波动率年化时,传统做法是乘以√252,但对高频策略这个系数可能失真。我的经验是:
夏普比率计算中最容易踩的坑是无风险利率选择。2020年后很多同行还在用3%的基准,实际上:
信息比率的计算更需要小心分母的选择。我见过最离谱的错误是用策略波动率当active risk,正确的应该是策略与基准的跟踪误差:
python复制def information_ratio(returns, benchmark_returns):
active_returns = returns - benchmark_returns
return np.mean(active_returns) / np.std(active_returns)
用Python做收益分布分析时,不要满足于简单的直方图。我推荐这个分析流程:
python复制from scipy.stats import jarque_bera
jb_test = jarque_bera(returns)
print(f"JB统计量:{jb_test[0]:.2f}, p值:{jb_test[1]:.4f}")
python复制print(f"偏度:{returns.skew():.2f}, 峰度:{returns.kurtosis():.2f}")
去年我们发现一个期权策略的收益峰度高达9.8,这意味着极端行情会带来毁灭性打击,后来果然在2022年3月遭遇单日-47%的回撤。
计算相关性时,90%的人都会忽略这些要点:
我的相关性分析模板:
python复制corr_matrix = returns.rolling(window=63).corr(pairwise=True)
sns.clustermap(corr_matrix, center=0, cmap='vlag')
常规的回测远远不够,我必做的五类压力测试:
用Pyfolio做压力测试时,建议自定义这些场景:
python复制import pyfolio as pf
stress_scenarios = {
'Flash Crash': ('2008-10-15', '2008-12-15'),
'COVID Crash': ('2020-02-15', '2020-04-15')
}
pf.timeseries.perf_funcs.STRESS_TEST_CASES.update(stress_scenarios)
我总结的过拟合检测七步法:
最实用的工具是Alphalens的因子IC分析:
python复制from alphalens import performance
ic = performance.factor_information_coefficient(factor_data)
处理A股数据时,这些清洗步骤必不可少:
我的数据清洗代码模板:
python复制def clean_data(df):
# 剔除ST股票
df = df[~df['name'].str.contains('ST')]
# 处理涨跌停
df['returns'] = np.where(df['close']==df['high'],
df['high']/df['open']-1,
df['returns'])
return df
精确的成本模型应该包含:
我的成本计算函数:
python复制def transaction_cost(size, price, is_buy):
commission = max(size*price*0.0002, 5) # 万2最低5元
stamp_duty = 0 if is_buy else size*price*0.001
slippage = size**0.8 * 0.0005 # 非线性滑点模型
return commission + stamp_duty + slippage
我现在的绩效分析系统架构:
code复制数据层(MySQL) → 计算引擎(Spark) → 分析模块(Python) → 可视化(Dash)
核心调度代码:
python复制from airflow import DAG
with DAG('performance_analysis') as dag:
data_task = PythonOperator(get_raw_data)
calc_task = SparkSubmitOperator(calc_metrics)
report_task = PythonOperator(generate_report)
data_task >> calc_task >> report_task
我的Pyfolio增强版模板:
python复制def enhanced_performance_analysis(returns):
# 基础分析
pf.tears.create_full_tear_sheet(returns)
# 自定义分析
plt.figure()
plot_drawdown_underwater(returns)
plot_rolling_sharpe(returns)
# 压力测试
stress_test(returns)
用Jinja2模板生成Word报告:
python复制from docxtpl import DocxTemplate
doc = DocxTemplate("template.docx")
context = {
'metrics': calc_metrics(returns),
'charts': [plot_to_base64(fig) for fig in figures]
}
doc.render(context)
doc.save("report.docx")
最后分享一个血泪教训:永远要在绩效分析里检查策略容量。我们曾经有个很好的套利策略,分析时一切完美,实盘时才发现超过2000万就严重滑点。现在我的分析清单里一定会包含容量压力测试,建议你们也加上这一项。