Quantitative Risk Management & FE¶
最大回撤计算¶
In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 生成累积收益曲线
np.random.seed(42)
returns = np.random.normal(0.001, 0.02, 1000)
cumulative_returns = (1 + returns).cumprod()
# 计算最大回撤
def calculate_max_drawdown(cumulative_returns):
# 计算历史最高点
running_max = np.maximum.accumulate(cumulative_returns)
# 计算相对于历史最高点的回撤
drawdown = (cumulative_returns - running_max) / running_max
# 最大回撤
max_drawdown = drawdown.min()
return max_drawdown
mdd = calculate_max_drawdown(cumulative_returns)
print(f"最大回撤: {mdd:.2%}")
# 可视化
plt.figure(figsize=(12, 6))
plt.plot(cumulative_returns)
plt.title('累积收益与最大回撤')
plt.xlabel('交易日')
plt.ylabel('累积收益')
plt.show()
Duplicate key in file WindowsPath('d:/Users/wukek/anaconda3/Lib/site-packages/matplotlib/mpl-data/matplotlibrc'), line 412 ('axes.unicode_minus: True # use Unicode for the minus symbol rather than hyphen. See')
最大回撤: -33.99%
VaR计算¶
In [2]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
# 假设收益率数据
returns = np.random.normal(0.001, 0.02, 1000)
# 参数法VaR
def parametric_var(returns, alpha=0.05):
mu = np.mean(returns)
sigma = np.std(returns)
var = mu + sigma * stats.norm.ppf(alpha)
return -var
# 历史模拟法VaR
def historical_var(returns, alpha=0.05):
return -np.percentile(returns, alpha*100)
# 打印结果
print(f"参数法VaR(95%): {parametric_var(returns):.4f}")
print(f"历史模拟法VaR(95%): {historical_var(returns):.4f}")
参数法VaR(95%): 0.0304 历史模拟法VaR(95%): 0.0305
VaR的基础实现¶
In [3]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf
from scipy.stats import norm
### 下载股票数据
tickers = ['AAPL', 'MSFT', 'AMZN', 'GOOGL']
weights = np.array([0.3, 0.3, 0.2, 0.2])
start_date = '2021-01-01'
end_date = '2023-01-01'
### 获取历史数据
data = yf.download(tickers, start=start_date, end=end_date, auto_adjust=True, proxy="http://127.0.0.1:7897")['Close']
returns = data.pct_change().dropna()
### 计算组合收益
portfolio_returns = returns.dot(weights)
### 设置参数
confidence_level = 0.95
investment_value = 1000000 ### 100万投资
### 计算历史模拟法VaR
historical_var = -np.percentile(portfolio_returns, (1-confidence_level)*100)
historical_var_value = historical_var * investment_value
print(f"95% 历史模拟法VaR: {historical_var:.4f} ({historical_var_value:.2f}元)")
### 计算参数法VaR
mean = np.mean(portfolio_returns)
std = np.std(portfolio_returns)
parametric_var = -(mean + std * norm.ppf(1-confidence_level))
parametric_var_value = parametric_var * investment_value
print(f"95% 参数法VaR: {parametric_var:.4f} ({parametric_var_value:.2f}元)")
### 蒙特卡洛模拟法VaR
np.random.seed(42) ### 设置随机种子以确保结果可重复
n_simulations = 10000 ### 模拟次数
### 使用历史均值和标准差生成随机收益率
mc_returns = np.random.normal(mean, std, n_simulations)
mc_var = -np.percentile(mc_returns, (1-confidence_level)*100)
mc_var_value = mc_var * investment_value
print(f"95% 蒙特卡洛模拟法VaR: {mc_var:.4f} ({mc_var_value:.2f}元)")
### 可视化收益率分布与VaR
plt.figure(figsize=(12, 8))
### 绘制收益率分布直方图
plt.hist(portfolio_returns, bins=50, alpha=0.5, density=True,
label='历史收益率分布')
### 添加VaR线
plt.axvline(-historical_var, color='r', linestyle='--',
label=f'历史模拟法VaR (95%): {historical_var:.4f}')
plt.axvline(-parametric_var, color='g', linestyle='--',
label=f'参数法VaR (95%): {parametric_var:.4f}')
plt.axvline(-mc_var, color='b', linestyle='--',
label=f'蒙特卡洛模拟法VaR (95%): {mc_var:.4f}')
### 添加正态分布曲线
x = np.linspace(min(portfolio_returns), max(portfolio_returns), 1000)
plt.plot(x, norm.pdf(x, mean, std), 'k-', lw=2,
label='正态分布拟合')
plt.legend(fontsize=10)
plt.title('投资组合收益率分布与VaR比较', fontsize=15)
plt.xlabel('日收益率', fontsize=12)
plt.ylabel('概率密度', fontsize=12)
plt.grid(True, alpha=0.3)
### 计算10天VaR (使用平方根法则)
time_horizon = 10 ### 10天持有期
historical_var_10d = historical_var * np.sqrt(time_horizon)
parametric_var_10d = parametric_var * np.sqrt(time_horizon)
mc_var_10d = mc_var * np.sqrt(time_horizon)
print("\n10天持有期VaR (平方根法则):")
print(f"历史模拟法VaR (10天): {historical_var_10d:.4f} ({historical_var_10d*investment_value:.2f}元)")
print(f"参数法VaR (10天): {parametric_var_10d:.4f} ({parametric_var_10d*investment_value:.2f}元)")
print(f"蒙特卡洛模拟法VaR (10天): {mc_var_10d:.4f} ({mc_var_10d*investment_value:.2f}元)")
### 计算各资产对组合VaR的贡献
cov_matrix = returns.cov()
portfolio_variance = weights.T @ cov_matrix @ weights
portfolio_std = np.sqrt(portfolio_variance)
### 边际贡献
marginal_contrib = (cov_matrix @ weights) / portfolio_std
component_var = weights * marginal_contrib * norm.ppf(confidence_level)
percent_contrib = component_var / np.sum(component_var) * 100
var_contrib = pd.DataFrame({
'Asset': tickers,
'Weight': weights * 100,
'VaR Contribution': component_var,
'Contribution %': percent_contrib
})
print("\n各资产VaR贡献:")
print(var_contrib)
YF deprecation warning: set proxy via new config function: yf.set_config(proxy=proxy)
[*********************100%***********************] 4 of 4 completed
95% 历史模拟法VaR: 0.0310 (30966.71元) 95% 参数法VaR: 0.0308 (30766.11元) 95% 蒙特卡洛模拟法VaR: 0.0310 (30952.66元) 10天持有期VaR (平方根法则): 历史模拟法VaR (10天): 0.0979 (97925.33元) 参数法VaR (10天): 0.0973 (97290.97元) 蒙特卡洛模拟法VaR (10天): 0.0979 (97880.90元) 各资产VaR贡献: Asset Weight VaR Contribution Contribution % Ticker AAPL AAPL 30.0 0.008500 27.685683 AMZN MSFT 30.0 0.010901 35.505576 GOOGL AMZN 20.0 0.005891 19.188091 MSFT GOOGL 20.0 0.005410 17.620650
d:\Users\wukek\anaconda3\Lib\site-packages\IPython\core\events.py:82: UserWarning: Glyph 8722 (\N{MINUS SIGN}) missing from current font. func(*args, **kwargs) d:\Users\wukek\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:152: UserWarning: Glyph 8722 (\N{MINUS SIGN}) missing from current font. fig.canvas.print_figure(bytes_io, **kw)
In [4]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf
from scipy.stats import norm
### 下载股票数据
tickers = ['AAPL', 'MSFT', 'AMZN', 'GOOGL']
weights = np.array([0.3, 0.3, 0.2, 0.2])
start_date = '2021-01-01'
end_date = '2023-01-01'
### 获取历史数据
data = yf.download(tickers, start=start_date, end=end_date, auto_adjust=True, proxy="http://127.0.0.1:7897")['Close']
returns = data.pct_change().dropna()
### 计算组合收益
portfolio_returns = returns.dot(weights)
### 历史模拟法ES
def calculate_historical_es(returns, alpha=0.95):
var = -np.percentile(returns, (1-alpha)*100)
es = -np.mean(returns[returns <= -var])
return es
### 参数法ES (正态分布假设)
def calculate_parametric_es(mu, sigma, alpha=0.95):
z_score = norm.ppf(1-alpha)
es = -(mu + sigma * norm.pdf(z_score)/(1-alpha))
return es
### 计算实例
hist_es = calculate_historical_es(portfolio_returns)
para_es = -calculate_parametric_es(
np.mean(portfolio_returns),
np.std(portfolio_returns))
### 打印结果
print(f"历史模拟法ES (95%): {hist_es:.4f}")
print(f"参数法ES (95%): {para_es:.4f}")
### 可视化ES
plt.figure(figsize=(12, 6))
plt.hist(portfolio_returns, bins=50, alpha=0.5, density=True,
label='历史收益率分布')
plt.axvline(-hist_es, color='r', linestyle='--',
label=f'历史模拟法ES (95%): {hist_es:.4f}')
plt.axvline(-para_es, color='g', linestyle='--',
label=f'参数法ES (95%): {para_es:.4f}')
plt.title('投资组合收益率分布与ES比较', fontsize=15)
plt.xlabel('日收益率', fontsize=12)
plt.ylabel('概率密度', fontsize=12)
plt.legend(fontsize=10)
plt.grid(True, alpha=0.3)
plt.show()
[*********************100%***********************] 4 of 4 completed d:\Users\wukek\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:152: UserWarning: Glyph 8722 (\N{MINUS SIGN}) missing from current font. fig.canvas.print_figure(bytes_io, **kw)
历史模拟法ES (95%): 0.0414 参数法ES (95%): 0.0384
In [5]:
# 计算10天ES (使用平方根法则)
time_horizon = 10 ### 10天持有期
hist_es_10d = hist_es * np.sqrt(time_horizon)
para_es_10d = para_es * np.sqrt(time_horizon)
print("\n10天持有期ES (平方根法则):")
print(f"历史模拟法ES (10天): {hist_es_10d:.4f}")
print(f"参数法ES (10天): {para_es_10d:.4f}")
10天持有期ES (平方根法则): 历史模拟法ES (10天): 0.1309 参数法ES (10天): 0.1213
夏普比率(Sharp Ratio)计算¶
In [6]:
import numpy as np
import pandas as pd
# 假设数据
returns = np.random.normal(0.001, 0.02, 252) # 252个交易日
risk_free_rate = 0.03 / 252 # 日化无风险利率
# 计算夏普比率
def sharpe_ratio(returns, risk_free_rate, periods=252):
"""
计算年化夏普比率
periods: 年化系数,日频数据为252,月频为12
"""
excess_returns = returns - risk_free_rate
return (np.mean(excess_returns) * periods) / (np.std(excess_returns, ddof=1) * np.sqrt(periods))
sr = sharpe_ratio(returns, risk_free_rate)
print(f"年化夏普比率: {sr:.4f}")
年化夏普比率: 0.1010
索提诺比率(Sortino Ratio)计算¶
In [7]:
import numpy as np
# 计算索提诺比率
def sortino_ratio(returns, risk_free_rate, target_return=0, periods=252):
"""
计算年化索提诺比率
target_return: 目标收益率,默认为0
"""
excess_returns = returns - risk_free_rate
downside_returns = returns[returns < target_return] - target_return
# 计算下行偏差
downside_deviation = np.sqrt(np.sum(downside_returns**2) / len(returns)) * np.sqrt(periods)
# 防止分母为零
if downside_deviation == 0:
return np.nan
return (np.mean(excess_returns) * periods) / downside_deviation
sr = sortino_ratio(returns, risk_free_rate)
print(f"年化索提诺比率: {sr:.4f}")
年化索提诺比率: 0.1435
均值-方差优化¶
In [8]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import minimize
# 假设数据:5个资产的期望收益和协方差矩阵
mu = np.array([0.05, 0.08, 0.12, 0.07, 0.10]) # 期望收益
Sigma = np.array([
[0.04, 0.02, 0.01, 0.02, 0.01],
[0.02, 0.06, 0.02, 0.03, 0.01],
[0.01, 0.02, 0.09, 0.03, 0.02],
[0.02, 0.03, 0.03, 0.05, 0.02],
[0.01, 0.01, 0.02, 0.02, 0.04]
]) # 协方差矩阵
# 目标函数:组合方差
def portfolio_variance(weights, cov_matrix):
return weights.T @ cov_matrix @ weights
# 约束条件
def return_constraint(weights, expected_returns, target_return):
return weights.T @ expected_returns - target_return
# 优化求解
n_assets = len(mu)
target_returns = np.linspace(min(mu), max(mu), 100)
efficient_weights = []
for target in target_returns:
constraints = [
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1},
{'type': 'eq', 'fun': lambda w: return_constraint(w, mu, target)}
]
bounds = tuple((0, 1) for _ in range(n_assets))
initial_weights = np.ones(n_assets) / n_assets
result = minimize(
portfolio_variance,
initial_weights,
args=(Sigma,),
method='SLSQP',
bounds=bounds,
constraints=constraints
)
if result.success:
efficient_weights.append(result.x)
print(f"有效前沿的权重:\n{np.array(efficient_weights)}")
# 可视化有效前沿
plt.figure(figsize=(10, 6))
# plt.plot(target_returns, [portfolio_variance(w, Sigma) for w in efficient_weights], label='有效前沿', color='blue')
plt.plot([portfolio_variance(w, Sigma) for w in efficient_weights], target_returns, label='有效前沿', color='blue')
plt.title('有效前沿')
plt.ylabel('期望收益率')
plt.xlabel('组合波动率')
plt.grid()
plt.legend()
plt.show()
有效前沿的权重: [[9.99999991e-01 4.99600361e-16 0.00000000e+00 8.56816768e-09 1.66533454e-16] [9.64646457e-01 0.00000000e+00 0.00000000e+00 3.53535431e-02 5.55111512e-17] [9.29292922e-01 8.32667268e-17 1.94289029e-16 7.07070776e-02 0.00000000e+00] [8.93939388e-01 0.00000000e+00 8.32667268e-17 1.06060612e-01 2.77555756e-17] [8.58585853e-01 0.00000000e+00 0.00000000e+00 1.41414147e-01 8.32667268e-17] [8.23232319e-01 0.00000000e+00 0.00000000e+00 1.76767681e-01 0.00000000e+00] [7.87878784e-01 0.00000000e+00 1.11022302e-16 2.12121216e-01 0.00000000e+00] [7.76188870e-01 0.00000000e+00 8.67361738e-19 2.08035384e-01 1.57757454e-02] [7.61296936e-01 0.00000000e+00 1.08420217e-18 2.09286252e-01 2.94168123e-02] [7.50506958e-01 2.88288157e-03 0.00000000e+00 2.01778604e-01 4.48315566e-02] [7.37454915e-01 1.07087244e-02 7.58941521e-19 1.94745757e-01 5.70906041e-02] [7.23781826e-01 1.73909581e-02 8.67361738e-19 1.89510393e-01 6.93168235e-02] [7.10565298e-01 2.30081371e-02 0.00000000e+00 1.84224130e-01 8.22024353e-02] [6.97348863e-01 2.86252870e-02 0.00000000e+00 1.78937730e-01 9.50881198e-02] [6.84132483e-01 3.42423373e-02 0.00000000e+00 1.73651306e-01 1.07973873e-01] [6.70916123e-01 3.98592901e-02 0.00000000e+00 1.68364915e-01 1.20859672e-01] [6.57699843e-01 4.54761896e-02 1.30104261e-18 1.63078425e-01 1.33745543e-01] [6.44483700e-01 5.10929726e-02 8.67361738e-19 1.57791785e-01 1.46631543e-01] [6.31267615e-01 5.67096313e-02 0.00000000e+00 1.52505130e-01 1.59517624e-01] [6.18051582e-01 6.23262504e-02 1.73472348e-18 1.47218415e-01 1.72403752e-01] [6.04835569e-01 6.79429036e-02 0.00000000e+00 1.41931644e-01 1.85289883e-01] [5.91619556e-01 7.35594580e-02 0.00000000e+00 1.36644940e-01 1.98176046e-01] [5.78403604e-01 7.91760499e-02 0.00000000e+00 1.31358107e-01 2.11062238e-01] [5.65187649e-01 8.47925895e-02 0.00000000e+00 1.26071315e-01 2.23948446e-01] [5.51971741e-01 9.04091343e-02 1.51788304e-18 1.20784442e-01 2.36834683e-01] [5.38755856e-01 9.60256293e-02 0.00000000e+00 1.15497564e-01 2.49720950e-01] [5.25539930e-01 1.01642206e-01 0.00000000e+00 1.10210699e-01 2.62607165e-01] [5.14950772e-01 1.05255814e-01 6.92509883e-04 1.02342873e-01 2.76758030e-01] [5.05826858e-01 1.07918183e-01 9.37027615e-03 9.79906394e-02 2.78894044e-01] [4.94973265e-01 1.12346523e-01 1.30718921e-02 9.20264535e-02 2.87581866e-01] [4.85510169e-01 1.15779177e-01 1.83768953e-02 8.54774910e-02 2.94856268e-01] [4.76039002e-01 1.19220106e-01 2.36806169e-02 7.89356064e-02 3.02124669e-01] [4.66560172e-01 1.22668820e-01 2.89837546e-02 7.24009163e-02 3.09386337e-01] [4.57074177e-01 1.26124811e-01 3.42870401e-02 6.58734125e-02 3.16640559e-01] [4.47581559e-01 1.29587463e-01 3.95911582e-02 5.93530633e-02 3.23886756e-01] [4.38082942e-01 1.33056124e-01 4.48967971e-02 5.28397216e-02 3.31124416e-01] [4.28578956e-01 1.36530087e-01 5.02046240e-02 4.63332489e-02 3.38353084e-01] [4.19070348e-01 1.40008595e-01 5.55153078e-02 3.98333543e-02 3.45572394e-01] [4.09557851e-01 1.43490790e-01 6.08294639e-02 3.33398022e-02 3.52782092e-01] [4.00042318e-01 1.46975790e-01 6.61477747e-02 2.68522073e-02 3.59981910e-01] [3.90524717e-01 1.50462559e-01 7.14709076e-02 2.03700943e-02 3.67171722e-01] [3.81006174e-01 1.53949906e-01 7.67996250e-02 1.38928902e-02 3.74351405e-01] [3.71488061e-01 1.57436381e-01 8.21347983e-02 7.41985451e-03 3.81520906e-01] [3.61972154e-01 1.60920136e-01 8.74774401e-02 9.49932705e-04 3.88680337e-01] [3.49666731e-01 1.62746204e-01 9.24685892e-02 0.00000000e+00 3.95118475e-01] [3.36917454e-01 1.64223873e-01 9.74266006e-02 0.00000000e+00 4.01432072e-01] [3.24200318e-01 1.65598462e-01 1.02361884e-01 0.00000000e+00 4.07839336e-01] [3.11559098e-01 1.67088329e-01 1.07602237e-01 0.00000000e+00 4.13750335e-01] [2.98847468e-01 1.68458498e-01 1.12546865e-01 0.00000000e+00 4.20147169e-01] [2.86332193e-01 1.69757128e-01 1.17910843e-01 0.00000000e+00 4.25999836e-01] [2.73828982e-01 1.70421675e-01 1.22670897e-01 0.00000000e+00 4.33078446e-01] [2.61371770e-01 1.71614258e-01 1.28073987e-01 0.00000000e+00 4.38939985e-01] [2.48895968e-01 1.72500892e-01 1.33124649e-01 2.95445092e-18 4.45478492e-01] [2.36477592e-01 1.73857039e-01 1.38788392e-01 6.23416249e-19 4.50876978e-01] [2.24013928e-01 1.75014706e-01 1.44140434e-01 8.45677695e-18 4.56830932e-01] [2.11416219e-01 1.75181743e-01 1.48166736e-01 4.40457133e-18 4.65235302e-01] [1.98927794e-01 1.76570930e-01 1.53688395e-01 4.01154804e-18 4.70812882e-01] [1.86442926e-01 1.77919682e-01 1.59178513e-01 0.00000000e+00 4.76458879e-01] [1.74346446e-01 1.75971530e-01 1.62342695e-01 0.00000000e+00 4.87339329e-01] [1.62636102e-01 1.78669437e-01 1.71118279e-01 0.00000000e+00 4.87576181e-01] [1.48922454e-01 1.81674212e-01 1.75192467e-01 3.51010453e-18 4.94210868e-01] [1.36380822e-01 1.82832133e-01 1.80349844e-01 6.55942314e-18 5.00437201e-01] [1.23788150e-01 1.83952369e-01 1.85341938e-01 0.00000000e+00 5.06917542e-01] [1.11126473e-01 1.85031987e-01 1.90120896e-01 0.00000000e+00 5.13720644e-01] [9.85809262e-02 1.86155944e-01 1.95234523e-01 1.07336015e-17 5.20028607e-01] [8.60328680e-02 1.87282629e-01 2.00344596e-01 2.16840434e-18 5.26339907e-01] [7.34800101e-02 1.88410814e-01 2.05444173e-01 6.07153217e-18 5.32665002e-01] [6.09228006e-02 1.89541243e-01 2.10535113e-01 3.03576608e-18 5.39000844e-01] [4.83611347e-02 1.90675069e-01 2.15618311e-01 1.51788304e-18 5.45345485e-01] [3.57945245e-02 1.91813879e-01 2.20694130e-01 0.00000000e+00 5.51697466e-01] [2.32221584e-02 1.92959768e-01 2.25762638e-01 0.00000000e+00 5.58055436e-01] [1.06428349e-02 1.94115470e-01 2.30823567e-01 6.93889390e-18 5.64418129e-01] [0.00000000e+00 1.94558627e-01 2.40013171e-01 0.00000000e+00 5.65428203e-01] [3.10316767e-18 1.75230553e-01 2.56038632e-01 1.15313467e-17 5.68730815e-01] [0.00000000e+00 1.56785240e-01 2.72946856e-01 9.74113741e-18 5.70267903e-01] [6.20633538e-18 1.38339915e-01 2.89855066e-01 4.69122533e-17 5.71805019e-01] [1.26778476e-17 1.19894595e-01 3.06763281e-01 0.00000000e+00 5.73342125e-01] [0.00000000e+00 1.01449273e-01 3.23671495e-01 0.00000000e+00 5.74879231e-01] [0.00000000e+00 8.30039472e-02 3.40579704e-01 1.99625115e-17 5.76416348e-01] [0.00000000e+00 6.45586872e-02 3.57487983e-01 0.00000000e+00 5.77953330e-01] [7.75791923e-18 4.61133059e-02 3.74396134e-01 0.00000000e+00 5.79490560e-01] [0.00000000e+00 2.76679843e-02 3.91304349e-01 0.00000000e+00 5.81027666e-01] [0.00000000e+00 9.22265223e-03 4.08212551e-01 7.79527894e-18 5.82564797e-01] [0.00000000e+00 0.00000000e+00 4.34343434e-01 0.00000000e+00 5.65656566e-01] [1.24126708e-17 0.00000000e+00 4.69696972e-01 4.95362919e-18 5.30303028e-01] [0.00000000e+00 1.38777878e-17 5.05050504e-01 6.76971646e-18 4.94949496e-01] [7.75791923e-19 2.08166817e-17 5.40404040e-01 4.30549104e-18 4.59595960e-01] [0.00000000e+00 0.00000000e+00 5.75757576e-01 1.80454191e-17 4.24242424e-01] [1.55158385e-17 0.00000000e+00 6.11111111e-01 0.00000000e+00 3.88888889e-01] [0.00000000e+00 1.95156391e-17 6.46464646e-01 0.00000000e+00 3.53535354e-01] [0.00000000e+00 2.77555756e-17 6.81818183e-01 2.77555756e-17 3.18181817e-01] [0.00000000e+00 0.00000000e+00 7.17171718e-01 8.32667268e-17 2.82828282e-01] [0.00000000e+00 1.94289029e-16 7.52525252e-01 0.00000000e+00 2.47474748e-01] [0.00000000e+00 1.11022302e-16 7.87878787e-01 1.94289029e-16 2.12121213e-01] [5.55111512e-17 1.11022302e-16 8.23232321e-01 0.00000000e+00 1.76767679e-01] [0.00000000e+00 3.05311332e-16 8.58585856e-01 1.11022302e-16 1.41414144e-01] [5.55111512e-17 0.00000000e+00 8.93939390e-01 0.00000000e+00 1.06060610e-01] [0.00000000e+00 0.00000000e+00 9.29292925e-01 0.00000000e+00 7.07070753e-02] [0.00000000e+00 8.32667268e-17 9.64646459e-01 1.11022302e-16 3.53535408e-02] [2.77555756e-16 8.32667268e-17 9.99999994e-01 0.00000000e+00 6.33299382e-09]]
风险平价投资组合¶
In [9]:
import numpy as np
from scipy.optimize import minimize
# 假设协方差矩阵
Sigma = np.array([
[0.04, 0.02, 0.01],
[0.02, 0.06, 0.02],
[0.01, 0.02, 0.05]
])
# 风险贡献函数
def risk_contribution(weights, cov_matrix):
portfolio_vol = np.sqrt(weights.T
@ cov_matrix @ weights)
marginal_contrib = cov_matrix @ weights
risk_contrib = weights * marginal_contrib / portfolio_vol
return risk_contrib
# 风险平价目标函数:各资产风险贡献的方差
def risk_parity_objective(weights, cov_matrix):
risk_contrib = risk_contribution(weights, cov_matrix)
target_risk = 1.0 / len(weights)# 目标风险贡献相等
return np.sum((risk_contrib - target_risk)**2)
# 优化求解
n_assets = Sigma.shape[0]
constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]
bounds = tuple((0.01, 1) for _ in range(n_assets))
initial_weights = np.ones(n_assets) / n_assets
result = minimize(
risk_parity_objective,
initial_weights,
args=(Sigma,),
method='SLSQP',
bounds=bounds,
constraints=constraints
)
risk_parity_weights = result.x
print("风险平价权重:", risk_parity_weights)
print("风险贡献:", risk_contribution(risk_parity_weights, Sigma))
风险平价权重: [0.01 0.53086716 0.45913284] 风险贡献: [0.00080595 0.11302956 0.07983188]
Black-Litterman模型¶
In [10]:
import numpy as np
# 市场数据
# 市场权重
mkt_weights = np.array([0.3, 0.4, 0.3])
Sigma = np.array([
[0.04, 0.02, 0.01],
[0.02, 0.06, 0.02],
[0.01, 0.02, 0.05]
]) # 协方差矩阵
delta = 2.5 # 市场风险厌恶系数
# 步骤1:计算市场隐含收益
Pi = delta * Sigma @ mkt_weights
# 步骤2:表达投资者观点
# 观点1:资产1的收益率为7%
# 观点2:资产2的收益率比资产3高2%
P = np.array([
[1, 0, 0],
[0, 1, -1]
])
Q = np.array([0.07, 0.02])
tau = 0.05 # 先验分布缩放因子
Omega = np.diag([0.01, 0.02]) # 观点不确定性
# 步骤3:结合市场与观点
temp1 = np.linalg.inv(tau * Sigma)
temp2 = P.T @ np.linalg.inv(Omega) @ P
temp3 = temp1 @ Pi
temp4 = P.T @ np.linalg.inv(Omega) @ Q
mu_bl = np.linalg.inv(temp1 + temp2) @ (temp3 + temp4)
print("Black-Litterman期望收益:", mu_bl)
Black-Litterman期望收益: [0.05948536 0.09058119 0.06587844]
凯利准则实现¶
In [11]:
import numpy as np
import matplotlib.pyplot as plt
# 凯利准则函数
def kelly_criterion(win_rate, win_loss_ratio):
"""
计算凯利比例
win_rate: 胜率
win_loss_ratio: 盈亏比(赢时的收益/输时的损失)
"""
return win_rate - (1 - win_rate) / win_loss_ratio
# 模拟不同资金管理策略的表现
def simulate_strategy(initial_capital, win_rate, win_loss_ratio, fraction, n_trades):
capital = initial_capital
capital_history = [capital]
for _ in range(n_trades):
# 决定交易结果
is_win = np.random.random() < win_rate
# 计算盈亏
bet_size = capital * fraction
if is_win:
capital += bet_size * (win_loss_ratio - 1)
else:
capital -= bet_size
capital_history.append(capital)
# 破产检查
if capital <= 0:
break
return capital_history
# 参数设置
win_rate = 0.6
win_loss_ratio = 2
kelly = kelly_criterion(win_rate, win_loss_ratio)
print(f"凯利比例: {kelly:.4f}")
凯利比例: 0.4000
固定分数风险模型¶
In [12]:
import numpy as np
# 假设交易数据
account_value = 100000 # 账户价值
risk_per_trade = 0.01 # 每笔交易风险1%
stop_loss_points = 50 # 止损点数
point_value = 10 # 每点价值(美元)
# 计算头寸规模
def fixed_fractional_position_size(account_value,
risk_percent, stop_loss_points, point_value):
# 计算每笔交易可承受的最大损失金额
max_loss_amount = account_value * risk_percent
# 计算头寸规模(合约数量)
position_size = max_loss_amount / (stop_loss_points * point_value)
# 通常需要向下取整
return int(position_size)
# 计算头寸规模
position_size = fixed_fractional_position_size(
account_value, risk_per_trade,
stop_loss_points, point_value
)
print(f"账户价值: ${account_value}")
print(f"每笔交易风险: {risk_per_trade*100}%")
print(f"止损点数: {stop_loss_points}点")
print(f"建议头寸规模: {position_size}手合约")
账户价值: $100000 每笔交易风险: 1.0% 止损点数: 50点 建议头寸规模: 2手合约
目标波动率控制¶
In [13]:
import numpy as np
import pandas as pd
# 假设数据
np.random.seed(42)
returns = pd.Series(np.random.normal(0.001, 0.02, 252),
index=pd.date_range('2024-01-01',
periods=252, freq='B'))
def target_volatility_scaling(returns, target_vol,
lookback=60, cap=2.0):
"""
基于目标波动率的头寸规模调整
returns: 收益率序列
target_vol: 年化目标波动率
lookback: 波动率计算的回看天数
cap: 杠杆上限
"""
# 计算波动率(使用简单滚动标准差)
vol = returns.rolling(window=lookback).std() * np.sqrt(252)
# 计算权重调整因子
scaling_factor = target_vol / vol
# 应用上限
scaling_factor = np.minimum(scaling_factor, cap)
# 填充开始的NaN值
scaling_factor = scaling_factor.fillna(1.0)
return scaling_factor
# 应用目标波动率控制
target_vol = 0.15 # 15%年化目标波动率
scaling = target_volatility_scaling(returns, target_vol)
scaled_returns = returns * scaling
# 比较原始策略和调整后策略的表现
cumulative_returns = (1 + returns).cumprod() - 1
scaled_cumulative_returns = (1 + scaled_returns).cumprod() - 1
# 可视化结果
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(cumulative_returns, label='原始策略', color='blue')
plt.plot(scaled_cumulative_returns, label='目标波动率调整策略', color='orange')
plt.title('目标波动率调整策略 vs 原始策略')
plt.xlabel('日期')
plt.ylabel('累计收益率')
plt.legend()
plt.grid()
plt.show()
In [14]:
# 计算夏普比率
def calculate_sharpe_ratio(returns, risk_free_rate=0.0):
excess_returns = returns - risk_free_rate
return np.mean(excess_returns) / np.std(excess_returns)
# 计算原始策略和调整后策略的夏普比率
original_sharpe = calculate_sharpe_ratio(returns)
scaled_sharpe = calculate_sharpe_ratio(scaled_returns)
print(f"原始策略夏普比率: {original_sharpe:.4f}")
print(f"目标波动率调整策略夏普比率: {scaled_sharpe:.4f}")
# 计算最大回撤
def calculate_max_drawdown(returns):
cumulative_returns = (1 + returns).cumprod()
peak = cumulative_returns.cummax()
drawdown = (cumulative_returns - peak) / peak
return drawdown.min()
# 计算原始策略和调整后策略的最大回撤
original_max_drawdown = calculate_max_drawdown(returns)
scaled_max_drawdown = calculate_max_drawdown(scaled_returns)
print(f"原始策略最大回撤: {original_max_drawdown:.4f}")
print(f"目标波动率调整策略最大回撤: {scaled_max_drawdown:.4f}")
# 计算年化收益率
def calculate_annualized_return(returns):
return (1 + returns.mean()) ** 252 - 1
# 计算原始策略和调整后策略的年化收益率
original_annualized_return = calculate_annualized_return(returns)
scaled_annualized_return = calculate_annualized_return(scaled_returns)
print(f"原始策略年化收益率: {original_annualized_return:.4f}")
print(f"目标波动率调整策略年化收益率: {scaled_annualized_return:.4f}")
# 计算信息比率
def calculate_information_ratio(returns, benchmark_returns):
excess_returns = returns - benchmark_returns
return np.mean(excess_returns) / np.std(excess_returns)
# 假设基准收益率为0
benchmark_returns = pd.Series(0, index=returns.index)
# 计算原始策略和调整后策略的信息比率
original_information_ratio = calculate_information_ratio(returns, benchmark_returns)
scaled_information_ratio = calculate_information_ratio(scaled_returns, benchmark_returns)
print(f"原始策略信息比率: {original_information_ratio:.4f}")
print(f"目标波动率调整策略信息比率: {scaled_information_ratio:.4f}")
# 计算年化波动率
def calculate_annualized_volatility(returns):
return returns.std() * np.sqrt(252)
# 计算原始策略和调整后策略的年化波动率
original_annualized_volatility = calculate_annualized_volatility(returns)
scaled_annualized_volatility = calculate_annualized_volatility(scaled_returns)
print(f"原始策略年化波动率: {original_annualized_volatility:.4f}")
print(f"目标波动率调整策略年化波动率: {scaled_annualized_volatility:.4f}")
# 计算年化收益率与年化波动率的比率
def calculate_return_to_volatility_ratio(returns):
annualized_return = calculate_annualized_return(returns)
annualized_volatility = calculate_annualized_volatility(returns)
return annualized_return / annualized_volatility
# 计算原始策略和调整后策略的年化收益率与年化波动率比率
original_return_to_volatility = calculate_return_to_volatility_ratio(returns)
scaled_return_to_volatility = calculate_return_to_volatility_ratio(scaled_returns)
print(f"原始策略年化收益率与年化波动率比率: {original_return_to_volatility:.4f}")
print(f"目标波动率调整策略年化收益率与年化波动率比率: {scaled_return_to_volatility:.4f}")
# 计算年化收益率与最大回撤的比率
def calculate_return_to_drawdown_ratio(returns):
annualized_return = calculate_annualized_return(returns)
max_drawdown = calculate_max_drawdown(returns)
return annualized_return / abs(max_drawdown)
# 计算原始策略和调整后策略的年化收益率与最大回撤比率
original_return_to_drawdown = calculate_return_to_drawdown_ratio(returns)
scaled_return_to_drawdown = calculate_return_to_drawdown_ratio(scaled_returns)
print(f"原始策略年化收益率与最大回撤比率: {original_return_to_drawdown:.4f}")
print(f"目标波动率调整策略年化收益率与最大回撤比率: {scaled_return_to_drawdown:.4f}")
# 计算年化收益率与信息比率的比率
def calculate_return_to_information_ratio(returns, benchmark_returns):
annualized_return = calculate_annualized_return(returns)
information_ratio = calculate_information_ratio(returns, benchmark_returns)
return annualized_return / information_ratio if information_ratio != 0 else np.nan
# 计算原始策略和调整后策略的年化收益率与信息比率比率
original_return_to_information = calculate_return_to_information_ratio(returns, benchmark_returns)
scaled_return_to_information = calculate_return_to_information_ratio(scaled_returns, benchmark_returns)
print(f"原始策略年化收益率与信息比率比率: {original_return_to_information:.4f}")
print(f"目标波动率调整策略年化收益率与信息比率比率: {scaled_return_to_information:.4f}")
原始策略夏普比率: 0.0479 目标波动率调整策略夏普比率: 0.0133 原始策略最大回撤: -0.2551 目标波动率调整策略最大回撤: -0.2551 原始策略年化收益率: 0.2623 目标波动率调整策略年化收益率: 0.0414 原始策略信息比率: 0.0479 目标波动率调整策略信息比率: 0.0133 原始策略年化波动率: 0.3071 目标波动率调整策略年化波动率: 0.1930 原始策略年化收益率与年化波动率比率: 0.8541 目标波动率调整策略年化收益率与年化波动率比率: 0.2146 原始策略年化收益率与最大回撤比率: 1.0283 目标波动率调整策略年化收益率与最大回撤比率: 0.1623 原始策略年化收益率与信息比率比率: 5.4758 目标波动率调整策略年化收益率与信息比率比率: 3.1196
止损策略实现¶
In [15]:
import numpy as np
import pandas as pd
# 模拟价格数据
np.random.seed(42)
price_data = pd.DataFrame({
'close': np.random.normal(0, 1, 100).cumsum() + 100,
'high': None,
'low': None
})
price_data['high'] = price_data['close'] + np.random.uniform(0, 2, 100)
price_data['low'] = price_data['close'] - np.random.uniform(0, 2, 100)
# 固定止损
def fixed_stop_loss(entry_price, current_price,
stop_percent=0.05, position='long'):
if position == 'long':
stop_price = entry_price * (1 - stop_percent)
return current_price <= stop_price
else: # short position
stop_price = entry_price * (1 + stop_percent)
return current_price >= stop_price
# 跟踪止损
def trailing_stop_loss(prices, stop_percent=0.05, position='long'):
"""
prices: 价格序列
stop_percent: 跟踪止损百分比
position: 'long'或'short'
"""
stops = np.zeros_like(prices)
triggered = False
if position == 'long':
# 初始化止损水平
highest = prices[0]
stops[0] = highest * (1 - stop_percent)
for i in range(1, len(prices)):
if triggered:
stops[i] = -1 # 已触发止损
else:
# 更新最高价
highest = max(highest, prices[i])
# 更新止损水平
stops[i] = highest * (1 - stop_percent)
# 检查是否触发止损
if prices[i] <= stops[i]:
triggered = True
# short position类似实现
return stops, triggered
In [16]:
# 计算固定止损
entry_price = price_data['close'][0]
fixed_stop = fixed_stop_loss(entry_price, price_data['close'],
stop_percent=0.05, position='long')
# 计算跟踪止损
trailing_stops, trailing_triggered = trailing_stop_loss(
price_data['close'], stop_percent=0.05, position='long')
# 打印结果
print(f"固定止损触发: {fixed_stop}")
print(f"跟踪止损触发: {trailing_triggered}")
# 可视化止损水平
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(price_data['close'], label='价格', color='blue')
plt.plot(trailing_stops, label='跟踪止损', color='red', linestyle='--')
plt.axhline(y=entry_price * (1 - 0.05), color='green', linestyle='--',
label='固定止损水平')
plt.title('固定止损与跟踪止损比较')
plt.xlabel('时间')
plt.ylabel('价格')
plt.legend()
plt.grid()
plt.show()
# 计算固定止损水平
fixed_stop_level = entry_price * (1 - 0.05)
# 可视化固定止损水平
plt.figure(figsize=(12, 6))
plt.plot(price_data['close'], label='价格', color='blue')
plt.axhline(y=fixed_stop_level, color='green', linestyle='--',
label='固定止损水平')
plt.title('固定止损水平')
plt.xlabel('时间')
plt.ylabel('价格')
plt.legend()
plt.grid()
固定止损触发: 0 False 1 False 2 False 3 False 4 False ... 95 True 96 True 97 True 98 True 99 True Name: close, Length: 100, dtype: bool 跟踪止损触发: True
压力测试实现¶
In [17]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 假设我们有一个投资组合权重
weights = np.array([0.3, 0.4, 0.3])
# 正常市场条件下的收益和协方差
# 年化期望收益
returns = np.array([0.08, 0.10, 0.06])
cov_matrix = np.array([
[0.04, 0.02, 0.01],
[0.02, 0.06, 0.02],
[0.01, 0.02, 0.05]
]) # 年化协方差矩阵
# 正常情况下的投资组合统计
port_return = weights @ returns
port_vol = np.sqrt(weights @ cov_matrix @ weights)
print(f"正常情况 - 期望收益: {port_return:.2%}, 波动率: {port_vol:.2%}")
# 历史情景分析 - 2008金融危机
# 危机期间资产收益
crisis_returns = np.array([-0.30, -0.40, -0.20])
crisis_port_return = weights @ crisis_returns
print(f"2008金融危机情景 - 投资组合收益: {crisis_port_return:.2%}")
# 假设情景 - 相关性趋于1
crisis_corr = np.ones((3, 3))
np.fill_diagonal(crisis_corr, 1)
std_devs = np.sqrt(np.diag(cov_matrix))
crisis_cov = np.outer(std_devs, std_devs) * crisis_corr
crisis_port_vol = np.sqrt(weights @ crisis_cov @ weights)
print(f"相关性趋于1情景 - 投资组合波动率: {crisis_port_vol:.2%}")
正常情况 - 期望收益: 8.20%, 波动率: 17.06% 2008金融危机情景 - 投资组合收益: -31.00% 相关性趋于1情景 - 投资组合波动率: 22.51%
In [18]:
# 可视化正常市场条件下的投资组合
plt.figure(figsize=(10, 6))
plt.bar(['资产1', '资产2', '资产3'], weights, color=['blue', 'orange', 'green'])
plt.title('正常市场条件下的投资组合权重')
plt.xlabel('资产')
plt.ylabel('权重')
plt.grid(axis='y')
plt.show()
# 可视化危机情景下的投资组合
plt.figure(figsize=(10, 6))
plt.bar(['资产1', '资产2', '资产3'], crisis_returns, color=['blue', 'orange', 'green'])
plt.title('2008金融危机情景下的资产收益')
plt.xlabel('资产')
plt.ylabel('收益率')
plt.grid(axis='y')
plt.show()
# 可视化相关性趋于1情景下的投资组合
plt.figure(figsize=(10, 6))
plt.bar(['资产1', '资产2', '资产3'], crisis_cov.diagonal(), color=['blue', 'orange', 'green'])
plt.title('相关性趋于1情景下的资产波动率')
plt.xlabel('资产')
plt.ylabel('波动率')
plt.grid(axis='y')
plt.show()