- import numpy as np
- import matplotlib.pyplot as plt
- # ============================================================
- # 1. 蒙特卡洛模拟函数(备用)
- # ============================================================
- def monte_carlo_simulation(S0, mu, sigma, T, n_sim=1000, n_steps=252):
- """
- 几何布朗运动路径模拟。
- 返回形状 (n_sim, n_steps+1) 的价格矩阵。
- """
- dt = T / n_steps
- drift = (mu - 0.5 * sigma**2) * dt
- vol = sigma * np.sqrt(dt)
- Z = np.random.standard_normal((n_sim, n_steps))
- log_returns = drift + vol * Z
- log_cumsum = np.cumsum(log_returns, axis=1)
- paths = np.zeros((n_sim, n_steps + 1))
- paths[:, 0] = S0
- paths[:, 1:] = S0 * np.exp(log_cumsum)
- return paths
- # ============================================================
- # 2. 目标函数(可替换为任何标量输出)
- # ============================================================
- def gbm_expected_final_price(params: dict) -> float:
- """
- 解析计算 GBM 期末价格的数学期望:E[S_T] = S0 * exp(mu * T)
- 参数:
- params (dict): 包含 'S0', 'mu', 'sigma', 'T' 键的字典。
- 返回:
- float: 期望期末价格。
- """
- return params['S0'] * np.exp(params['mu'] * params['T'])
- # ---------- 如需模拟版本,取消下面注释即可 ----------
- # def gbm_mean_simulation(params: dict, n_sim=2000, n_steps=252) -> float:
- # paths = monte_carlo_simulation(params['S0'], params['mu'], params['sigma'],
- # params['T'], n_sim, n_steps)
- # return paths[:, -1].mean()
- # ============================================================
- # 3. 飓风图绘制函数
- # ============================================================
- def plot_tornado(
- base_params: dict,
- param_ranges: dict,
- target_func,
- target_label: str = 'Expected Final Price',
- figsize=None
- ):
- """
- 绘制飓风图,展示各参数独立变化时目标函数相对于基准的变化幅度。
- 参数:
- base_params : dict
- 基准参数值,如 {'S0':100, 'mu':0.05, 'sigma':0.2, 'T':1.0}
- param_ranges : dict
- 参数变化比例范围,值为 (低值比例, 高值比例)。
- 例如 {'S0': (-0.1, 0.1)} 表示 S0 在基准的 ±10% 之间变动。
- target_func : callable
- 接收参数字典,返回标量数值。
- target_label : str
- 目标函数的名称,用于轴标签。
- figsize : tuple, optional
- 图形大小,默认自适应。
- 返回:
- fig, ax : matplotlib 对象。
- """
- # 计算基准情景输出
- base_val = target_func(base_params)
- # 收集每个参数的低/高输出变化量
- data = []
- for param, (low_pct, high_pct) in param_ranges.items():
- # 低情景
- low_params = base_params.copy()
- low_params[param] = base_params[param] * (1 + low_pct)
- low_val = target_func(low_params)
- low_delta = low_val - base_val
- # 高情景
- high_params = base_params.copy()
- high_params[param] = base_params[param] * (1 + high_pct)
- high_val = target_func(high_params)
- high_delta = high_val - base_val
- data.append((param, low_delta, high_delta))
- # 按最大影响宽度排序(降序)
- data.sort(key=lambda x: max(abs(x[1]), abs(x[2])), reverse=True)
- # 绘图
- if figsize is None:
- figsize = (max(8, len(data)*1.5), max(4, len(data)*0.5))
- fig, ax = plt.subplots(figsize=figsize)
- y_idx = np.arange(len(data))
- for i, (param, low_d, high_d) in enumerate(data):
- ax.barh(i, high_d - low_d, left=low_d, height=0.5,
- color='steelblue', alpha=0.8, edgecolor='black')
- # 基准线
- ax.axvline(x=0, color='black', linewidth=1)
- ax.set_yticks(y_idx)
- ax.set_yticklabels([d[0] for d in data])
- ax.set_xlabel(f'Change in {target_label}')
- ax.set_title(f'Sensitivity Tornado Chart\nBase value: {base_val:.2f}')
- ax.grid(axis='x', linestyle=':', alpha=0.6)
- ax.invert_yaxis() # 最宽的放在最上面
- fig.tight_layout()
- return fig, ax
- # ============================================================
- # 4. 主程序:示例运行
- # ============================================================
- if __name__ == '__main__':
- # 基准参数
- base = {
- 'S0': 100.0, # 初始价格
- 'mu': 0.05, # 预期收益率 5%
- 'sigma': 0.2, # 波动率 20%
- 'T': 1.0 # 期限 1 年
- }
- # 各参数的变化范围(相对于基准的比例)
- ranges = {
- 'S0': (-0.10, 0.10), # S0 在 90 ~ 110
- 'mu': (-0.50, 0.50), # mu 在 2.5% ~ 7.5% (相对变化 ±50%)
- 'sigma': (-0.20, 0.20), # sigma 在 0.16 ~ 0.24
- 'T': (-0.20, 0.20), # T 在 0.8 ~ 1.2 年
- }
- # 绘制飓风图(使用快速解析期望)
- # 若需要基于模拟的期末价格均值,将 target_func 换为 gbm_mean_simulation 即可
- fig, ax = plot_tornado(base, ranges, gbm_expected_final_price,
- target_label='Expected Final Price')
- plt.show()
复制代码
|