量化交易中的数学基础
由bqu1vdra创建,最终由bqu1vdra 被浏览 2 用户
量化交易就是”用数学和代码做交易”。价格、收益、风险、回撤……在数学里都可以被抽象成变量和公式。本质上,我们就是在处理一串随机数(收益序列),用统计和概率的工具去度量”好不好”“稳不稳”。
本文将系统介绍量化交易中常用的数学公式和指标,包括:夏普比率、Sortino 比率、VaR、CVaR、期望收益、均值不利变动(MAE)、回撤、Calmar 比率等。
全文默认你已经知道”买卖股票/期货”这种基本概念,但可以不了解高深数学。
一、从最基础开始:收益率与波动率
收益率的数学定义
对数收益的好处是:多个时期可以直接相加对应”复利”,数学上很方便。
在量化里,我们把rt视为随机变量R的一次”抽样”,然后用统计量来刻画它:
期望(平均收益):
方差 & 标准差(波动率):
直观解释:
二、风险收益比:夏普比、Sortino 比、Calmar 比
夏普比(Sharpe Ratio) 公式:
其中:
更精确一点:如果我们有一串周期收益 风险收益率为
则样本夏普是:
直观理解:
夏普比 = “每承受 1 单位波动风险,能多赚多少超额收益”。
- 夏普 ≈ 1:一般
- 夏普 ≈ 2:很不错
- 夏普 > 3:通常是非常优秀(也要警惕过拟合)
年化夏普
N是一年交易日数(股票常用 252)
Python 代码示例:计算夏普比
import numpy as np
def sharpe_ratio(returns, risk_free_rate=0.0, periods_per_year=252):
"""
returns: 一维 np.array 或 pandas.Series,周期收益,例如每日收益
risk_free_rate: 年化无风险收益率,例如 0.02 代表 2%
"""
rf_per_period = risk_free_rate / periods_per_year
excess = returns - rf_per_period
mean_excess = excess.mean()
std_excess = excess.std(ddof=1) # ddof=1 是样本标准差
if std_excess == 0:
return np.nan
sharpe = np.sqrt(periods_per_year) * mean_excess / std_excess
return sharpe
Sortino 比率:只惩罚”下行波动”的夏普
有些策略,比如趋势策略,向上猛涨和向下暴跌都会带来高波动,但我们只讨厌”跌”,不讨厌”涨”。
Sortino 比率就是只计算”下行波动”的夏普。
公式:
其中:
数学表达:
直观理解:
Sortino 比率 = “每承受 1 单位’亏钱波动’,能赚多少超额收益”。 当策略有很多剧烈上行收益时,Sortino 会比 Sharpe 大,说明”风险其实没那么可怕”。
def sortino_ratio(returns, target_return=0.0, periods_per_year=252):
"""
target_return: 年化目标收益(通常设为 0 或无风险收益率)
"""
target_per_period = target_return / periods_per_year
downside = np.minimum(0, returns - target_per_period)
downside_std = downside.std(ddof=1)
if downside_std == 0:
return np.nan
mean_return = returns.mean()
sortino = np.sqrt(periods_per_year) * (mean_return - target_per_period) / abs(downside_std)
return sortino
回撤与 Calmar 比
回撤(Drawdown)
公式:
更完整地说:
某个时刻的回撤:
全过程的最大回撤(Max Drawdown, MDD):
直观:从账户最高点跌到最低点,跌了多少。
Calmar 比率
公式:
其中: CAGR 公式:
直观解释:
Calmar = “每承受 1 单位最大回撤,能带来多少年化收益”。 它比夏普更关注”路径风险”,尤其适合评估 CTA、对冲基金等长期策略。
Python:最大回撤与 Calmar
import pandas as pd
import numpy as np
def max_drawdown(equity_curve):
"""
equity_curve: pandas.Series 或 np.array,表示随时间变化的账户净值
返回:max_dd (负数),drawdown_series(同长度的回撤序列)
"""
equity = pd.Series(equity_curve)
running_max = equity.cummax()
drawdown = equity / running_max - 1.0
max_dd = drawdown.min() # 最低(最负)的值
return max_dd, drawdown
def calmar_ratio(equity_curve, years):
"""
years: 回测覆盖的年数,例如 3 年数据则 years=3
"""
equity = np.array(equity_curve)
cagr = (equity[-1] / equity[0])**(1 / years) - 1
max_dd, _ = max_drawdown(equity)
if max_dd == 0:
return np.nan
# max_dd 是负数,所以取绝对值
return cagr / abs(max_dd)
三、尾部风险:VaR 与 CVaR(条件 VaR)
VaR(Value at Risk,风险价值)
数学写法:
直白地说:在给定置信水平 (比如 95%)下,未来某一时间(比如 1 天)最大可能亏损不超过 VaR。 通常表述成:“1 天 95% VaR = 100 万”
含义是:在正常市场下,有 95% 的概率,明天亏损不超过 100 万;也就是有 5% 的概率亏损大于 100 万。
更标准的数学写法(以”亏损L“为正数):
计算方法(常用三种)
- 历史模拟法(Historical VaR)
用过去 N 天的真实收益
换算成亏损 取95%分位数
2. 参数法(Parametric VaR,正态假设)
假设收益 对给定置信度alpha ,找正态分布分位点
3. 蒙特卡洛模拟
- 建立收益或价格的随机模型
- 模拟很多路径,统计亏损分布
CVaR / ES(条件 VaR、预期损失)
定义:
意思是:
在已经发生”很糟糕”的那部分(损失超过 VaR)的情况下,平均还会亏多少。 相比 VaR 只给出一个”门槛”,CVaR 给你看真正”尾部”的平均损失,监管和风险管理更喜欢用它。
Python:用历史模拟计算 VaR & CVaR
下面把收益视为随机变量,假设我们有一串每日策略收益 returns(单位是百分比或小数形式),资金规模记为 1(方便),则损失就是 -returns。
def var_cvar_from_returns(returns, alpha=0.95):
"""
returns: np.array 或 pandas.Series,策略的周期收益(如日度收益)
alpha: 置信水平,例如 0.95 或 0.99
返回: VaR, CVaR(都以"亏损比例"表示,是正数)
"""
returns = np.asarray(returns)
losses = -returns # 亏损为正
var = np.quantile(losses, alpha) # alpha 分位数
tail_losses = losses[losses >= var]
if len(tail_losses) == 0:
cvar = var
else:
cvar = tail_losses.mean()
return var, cvar
例子(概念性的):
假设 var = 0.03,表示 95% VaR 为 3%
cvar = 0.06,表示最糟的 5% 情况下,平均亏损是 6%
四、交易系统视角:期望值(Expectancy)
公式:
变量解释:
更数学化一点,把一笔交易的收益记为随机变量X ,则:
其中:
很多交易书上喜欢把亏损写成正数,于是公式变成:
例子:
则:
平均每笔交易期望赚 0.3%。
即使胜率不高,只要盈亏比足够大,期望仍然为正 —— 这就是趋势交易或”止损让利润奔跑”的数学基础。
Python:根据交易结果计算期望值
假设你有每笔交易的收益列表:
def trade_expectancy(trade_returns):
"""
trade_returns: 每笔交易的收益,例如 +0.02, -0.01 代表 +2%, -1%
返回: expectancy, win_rate, avg_win, avg_loss
"""
trade_returns = np.asarray(trade_returns)
wins = trade_returns[trade_returns > 0]
losses = trade_returns[trade_returns < 0]
win_rate = len(wins) / len(trade_returns) if len(trade_returns) > 0 else np.nan
avg_win = wins.mean() if len(wins) > 0 else 0.0
avg_loss = -losses.mean() if len(losses) > 0 else 0.0 # 转成正数表示亏损幅度
expectancy = win_rate * avg_win - (1 - win_rate) * avg_loss
return expectancy, win_rate, avg_win, avg_loss
五、单笔交易的风险剖面:MAE
MAE(Mean Adverse Excursion,均值不利变动)
公式:
解释:
直观理解:
MAE = “即使最后赚钱了,中间最难受的时候亏到什么程度”。
用途:
- 统计历史交易的 MAE,可以帮助你决定止损应该放多远
- 如果绝大多数盈利交易的 MAE 不超过 2%,那你把止损放 2% 附近可能比较合理 如果止损放得比 MAE 典型值还紧,很多原本可以获利的交易会被”洗出去”
- 通常也会配套看 MFE(Mean Favorable Excursion,均值有利变动),即持仓期间曾经出现的最大浮盈,用来研究”止盈”和”移动止损”的策略。
Python:一个简化版 MAE 计算示例
真正的 MAE 需要逐笔交易的”内层价格路径”。这里给一个概念性的写法:
def mae_for_trade(prices_during_trade, direction='long'):
"""
prices_during_trade: 列表,包含从开仓到平仓的价格序列
direction: 'long' 或 'short'
"""
entry_price = prices_during_trade[0]
prices = np.asarray(prices_during_trade)
if direction == 'long':
min_price = prices.min()
mae = entry_price - min_price # 价格跌了多少
else: # 空头
max_price = prices.max()
mae = max_price - entry_price # 价格涨了多少,对空头不利
return mae
在实盘/回测系统中,会在每笔交易生命周期内实时更新”到目前为止的最低点/最高点”,最终统计所有交易的 MAE 分布。
六、组合层面的数学:相关性、分散化与夏普的提升
虽然前面主要是”单策略/单资产”的指标,但量化交易中非常重要的一块,是组合优化。这里简要说一下最核心的数学思想。
这就是”不要把鸡蛋放在一个篮子里”的数学版本。
七、把这些指标串起来:一个小小的”量化评估流程”
假设你写了一个简单策略,得到一串每日收益 daily_returns,和对应的账户净值 equity_curve。你可以这样评估它:
import numpy as np
periods_per_year = 252
years = len(daily_returns) / periods_per_year
# 1. 年化收益(CAGR)
cagr = (equity_curve[-1] / equity_curve[0])**(1/years) - 1
# 2. 夏普 & Sortino
sharpe = sharpe_ratio(daily_returns, risk_free_rate=0.02, periods_per_year=periods_per_year)
sortino = sortino_ratio(daily_returns, target_return=0.0, periods_per_year=periods_per_year)
# 3. 回撤 & Calmar
max_dd, dd_series = max_drawdown(equity_curve)
calmar = calmar_ratio(equity_curve, years)
# 4. VaR & CVaR(例如 95%)
var95, cvar95 = var_cvar_from_returns(daily_returns, alpha=0.95)
得到这些指标后,你可以这样解读:
- CAGR 高、夏普 & Sortino 高 —— 策略长期赚钱且”单位风险收益高”
- MaxDD 不大、Calmar 不错 —— 账户回撤可以承受
- VaR/CVaR 可用来设置仓位上限,比如”日 VaR 不超过总资金的 2%”
- 再配合 Expectancy + MAE/MFE 分析单笔交易结构,看止损/止盈是否合理
\