027-金融数据可视化
金融数据可视化是数据分析中的重要应用领域,需要专业的图表类型和分析方法。本章将详细介绍如何使用Matplotlib创建各种金融图表,包括股票价格图、技术指标、风险分析图表等。
导入必要的库
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import matplotlib.dates as mdates
from matplotlib.patches import Rectangle
import matplotlib.patches as mpatches
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 设置图表样式
plt.style.use('default')
股票价格图表
K线图(蜡烛图)
class StockCharts:
def __init__(self):
# 生成模拟股票数据
self.dates = pd.date_range('2023-01-01', periods=252, freq='D')
self.stock_data = self.generate_stock_data()
def generate_stock_data(self):
"""生成模拟股票数据"""
np.random.seed(42)
# 初始价格
initial_price = 100
# 生成随机价格变化
returns = np.random.normal(0.001, 0.02, len(self.dates))
# 计算价格序列
prices = [initial_price]
for ret in returns[1:]:
prices.append(prices[-1] * (1 + ret))
# 生成OHLC数据
data = []
for i, (date, close) in enumerate(zip(self.dates, prices)):
# 生成开盘价、最高价、最低价
open_price = close * (1 + np.random.normal(0, 0.005))
high = max(open_price, close) * (1 + abs(np.random.normal(0, 0.01)))
low = min(open_price, close) * (1 - abs(np.random.normal(0, 0.01)))
# 生成成交量
volume = np.random.randint(1000000, 5000000)
data.append({
'Date': date,
'Open': open_price,
'High': high,
'Low': low,
'Close': close,
'Volume': volume
})
return pd.DataFrame(data)
def plot_candlestick(self, start_idx=0, end_idx=50):
"""绘制K线图"""
print("=== K线图 ===")
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 12),
gridspec_kw={'height_ratios': [3, 1]})
fig.suptitle('股票K线图与成交量', fontsize=16, fontweight='bold')
# 选择数据范围
data = self.stock_data.iloc[start_idx:end_idx]
# 绘制K线图
for i, row in data.iterrows():
date = mdates.date2num(row['Date'])
open_price = row['Open']
high = row['High']
low = row['Low']
close = row['Close']
# 确定颜色(红涨绿跌)
color = 'red' if close >= open_price else 'green'
# 绘制影线
ax1.plot([date, date], [low, high], color='black', linewidth=1)
# 绘制实体
height = abs(close - open_price)
bottom = min(open_price, close)
rect = Rectangle((date - 0.3, bottom), 0.6, height,
facecolor=color, edgecolor='black', alpha=0.8)
ax1.add_patch(rect)
# 设置K线图格式
ax1.set_title('K线图')
ax1.set_ylabel('价格')
ax1.grid(True, alpha=0.3)
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
ax1.xaxis.set_major_locator(mdates.DayLocator(interval=5))
# 绘制成交量
for i, row in data.iterrows():
date = mdates.date2num(row['Date'])
volume = row['Volume']
color = 'red' if row['Close'] >= row['Open'] else 'green'
ax2.bar(date, volume, width=0.6, color=color, alpha=0.7)
# 设置成交量图格式
ax2.set_title('成交量')
ax2.set_xlabel('日期')
ax2.set_ylabel('成交量')
ax2.grid(True, alpha=0.3)
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
ax2.xaxis.set_major_locator(mdates.DayLocator(interval=5))
plt.tight_layout()
return fig
def plot_price_analysis(self):
"""价格分析图表"""
print("\n=== 价格分析图表 ===")
fig, axes = plt.subplots(2, 2, figsize=(18, 12))
fig.suptitle('股票价格分析', fontsize=16, fontweight='bold')
# 1. 价格趋势图
ax1 = axes[0, 0]
# 计算移动平均线
self.stock_data['MA5'] = self.stock_data['Close'].rolling(window=5).mean()
self.stock_data['MA20'] = self.stock_data['Close'].rolling(window=20).mean()
self.stock_data['MA60'] = self.stock_data['Close'].rolling(window=60).mean()
ax1.plot(self.stock_data['Date'], self.stock_data['Close'],
label='收盘价', linewidth=1, color='black')
ax1.plot(self.stock_data['Date'], self.stock_data['MA5'],
label='MA5', linewidth=2, color='blue')
ax1.plot(self.stock_data['Date'], self.stock_data['MA20'],
label='MA20', linewidth=2, color='red')
ax1.plot(self.stock_data['Date'], self.stock_data['MA60'],
label='MA60', linewidth=2, color='green')
ax1.set_title('价格趋势与移动平均线')
ax1.set_xlabel('日期')
ax1.set_ylabel('价格')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 2. 价格分布直方图
ax2 = axes[0, 1]
ax2.hist(self.stock_data['Close'], bins=30, alpha=0.7, color='skyblue',
edgecolor='black')
ax2.axvline(self.stock_data['Close'].mean(), color='red', linestyle='--',
linewidth=2, label=f'均值: {self.stock_data["Close"].mean():.2f}')
ax2.axvline(self.stock_data['Close'].median(), color='green', linestyle='--',
linewidth=2, label=f'中位数: {self.stock_data["Close"].median():.2f}')
ax2.set_title('价格分布')
ax2.set_xlabel('价格')
ax2.set_ylabel('频数')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 3. 日收益率分析
ax3 = axes[1, 0]
# 计算日收益率
self.stock_data['Returns'] = self.stock_data['Close'].pct_change()
ax3.plot(self.stock_data['Date'], self.stock_data['Returns'] * 100,
color='purple', linewidth=1)
ax3.axhline(y=0, color='black', linestyle='-', alpha=0.5)
ax3.fill_between(self.stock_data['Date'],
self.stock_data['Returns'] * 100, 0,
where=(self.stock_data['Returns'] > 0),
color='red', alpha=0.3, label='正收益')
ax3.fill_between(self.stock_data['Date'],
self.stock_data['Returns'] * 100, 0,
where=(self.stock_data['Returns'] < 0),
color='green', alpha=0.3, label='负收益')
ax3.set_title('日收益率')
ax3.set_xlabel('日期')
ax3.set_ylabel('收益率 (%)')
ax3.legend()
ax3.grid(True, alpha=0.3)
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 4. 收益率分布
ax4 = axes[1, 1]
returns = self.stock_data['Returns'].dropna() * 100
# 绘制直方图
n, bins, patches = ax4.hist(returns, bins=30, alpha=0.7, color='lightcoral',
edgecolor='black', density=True)
# 拟合正态分布
mu, sigma = stats.norm.fit(returns)
x = np.linspace(returns.min(), returns.max(), 100)
y = stats.norm.pdf(x, mu, sigma)
ax4.plot(x, y, 'b-', linewidth=2, label=f'正态分布 (μ={mu:.2f}, σ={sigma:.2f})')
ax4.axvline(mu, color='blue', linestyle='--', linewidth=2, label=f'均值: {mu:.2f}%')
ax4.axvline(0, color='black', linestyle='-', alpha=0.5)
ax4.set_title('收益率分布')
ax4.set_xlabel('日收益率 (%)')
ax4.set_ylabel('概率密度')
ax4.legend()
ax4.grid(True, alpha=0.3)
plt.tight_layout()
return fig
# 创建股票图表分析器
stock_charts = StockCharts()
# K线图
fig1 = stock_charts.plot_candlestick()
plt.show()
# 价格分析
fig2 = stock_charts.plot_price_analysis()
plt.show()
技术指标分析
常用技术指标
class TechnicalIndicators:
def __init__(self, stock_data):
self.data = stock_data.copy()
self.calculate_indicators()
def calculate_indicators(self):
"""计算技术指标"""
# RSI (相对强弱指数)
self.data['RSI'] = self.calculate_rsi(self.data['Close'])
# MACD
macd_data = self.calculate_macd(self.data['Close'])
self.data['MACD'] = macd_data['MACD']
self.data['MACD_Signal'] = macd_data['Signal']
self.data['MACD_Histogram'] = macd_data['Histogram']
# 布林带
bollinger_data = self.calculate_bollinger_bands(self.data['Close'])
self.data['BB_Upper'] = bollinger_data['Upper']
self.data['BB_Middle'] = bollinger_data['Middle']
self.data['BB_Lower'] = bollinger_data['Lower']
# 随机指标 (KDJ)
kdj_data = self.calculate_kdj(self.data['High'], self.data['Low'], self.data['Close'])
self.data['K'] = kdj_data['K']
self.data['D'] = kdj_data['D']
self.data['J'] = kdj_data['J']
def calculate_rsi(self, prices, period=14):
"""计算RSI指标"""
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
def calculate_macd(self, prices, fast=12, slow=26, signal=9):
"""计算MACD指标"""
ema_fast = prices.ewm(span=fast).mean()
ema_slow = prices.ewm(span=slow).mean()
macd = ema_fast - ema_slow
macd_signal = macd.ewm(span=signal).mean()
macd_histogram = macd - macd_signal
return {
'MACD': macd,
'Signal': macd_signal,
'Histogram': macd_histogram
}
def calculate_bollinger_bands(self, prices, period=20, std_dev=2):
"""计算布林带"""
middle = prices.rolling(window=period).mean()
std = prices.rolling(window=period).std()
upper = middle + (std * std_dev)
lower = middle - (std * std_dev)
return {
'Upper': upper,
'Middle': middle,
'Lower': lower
}
def calculate_kdj(self, high, low, close, period=9):
"""计算KDJ指标"""
lowest_low = low.rolling(window=period).min()
highest_high = high.rolling(window=period).max()
rsv = (close - lowest_low) / (highest_high - lowest_low) * 100
k = rsv.ewm(com=2).mean()
d = k.ewm(com=2).mean()
j = 3 * k - 2 * d
return {'K': k, 'D': d, 'J': j}
def plot_technical_analysis(self):
"""绘制技术分析图表"""
print("=== 技术指标分析 ===")
fig, axes = plt.subplots(4, 1, figsize=(16, 20))
fig.suptitle('技术指标分析', fontsize=16, fontweight='bold')
# 1. 价格与布林带
ax1 = axes[0]
ax1.plot(self.data['Date'], self.data['Close'],
label='收盘价', linewidth=2, color='black')
ax1.plot(self.data['Date'], self.data['BB_Upper'],
label='布林带上轨', linewidth=1, color='red', linestyle='--')
ax1.plot(self.data['Date'], self.data['BB_Middle'],
label='布林带中轨', linewidth=1, color='blue')
ax1.plot(self.data['Date'], self.data['BB_Lower'],
label='布林带下轨', linewidth=1, color='red', linestyle='--')
# 填充布林带区域
ax1.fill_between(self.data['Date'], self.data['BB_Upper'], self.data['BB_Lower'],
alpha=0.1, color='gray')
ax1.set_title('价格与布林带')
ax1.set_ylabel('价格')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 2. MACD
ax2 = axes[1]
ax2.plot(self.data['Date'], self.data['MACD'],
label='MACD', linewidth=2, color='blue')
ax2.plot(self.data['Date'], self.data['MACD_Signal'],
label='信号线', linewidth=2, color='red')
# MACD柱状图
colors = ['red' if x > 0 else 'green' for x in self.data['MACD_Histogram']]
ax2.bar(self.data['Date'], self.data['MACD_Histogram'],
color=colors, alpha=0.6, width=1)
ax2.axhline(y=0, color='black', linestyle='-', alpha=0.5)
ax2.set_title('MACD指标')
ax2.set_ylabel('MACD')
ax2.legend()
ax2.grid(True, alpha=0.3)
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 3. RSI
ax3 = axes[2]
ax3.plot(self.data['Date'], self.data['RSI'],
linewidth=2, color='purple', label='RSI')
ax3.axhline(y=70, color='red', linestyle='--', alpha=0.7, label='超买线(70)')
ax3.axhline(y=30, color='green', linestyle='--', alpha=0.7, label='超卖线(30)')
ax3.axhline(y=50, color='black', linestyle='-', alpha=0.5)
# 填充超买超卖区域
ax3.fill_between(self.data['Date'], 70, 100, alpha=0.1, color='red')
ax3.fill_between(self.data['Date'], 0, 30, alpha=0.1, color='green')
ax3.set_title('RSI相对强弱指数')
ax3.set_ylabel('RSI')
ax3.set_ylim(0, 100)
ax3.legend()
ax3.grid(True, alpha=0.3)
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 4. KDJ
ax4 = axes[3]
ax4.plot(self.data['Date'], self.data['K'],
linewidth=2, color='blue', label='K线')
ax4.plot(self.data['Date'], self.data['D'],
linewidth=2, color='red', label='D线')
ax4.plot(self.data['Date'], self.data['J'],
linewidth=2, color='green', label='J线')
ax4.axhline(y=80, color='red', linestyle='--', alpha=0.7, label='超买线(80)')
ax4.axhline(y=20, color='green', linestyle='--', alpha=0.7, label='超卖线(20)')
ax4.axhline(y=50, color='black', linestyle='-', alpha=0.5)
ax4.set_title('KDJ随机指标')
ax4.set_xlabel('日期')
ax4.set_ylabel('KDJ')
ax4.set_ylim(0, 100)
ax4.legend()
ax4.grid(True, alpha=0.3)
ax4.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
plt.tight_layout()
return fig
# 创建技术指标分析器
tech_indicators = TechnicalIndicators(stock_charts.stock_data)
# 技术指标分析
fig3 = tech_indicators.plot_technical_analysis()
plt.show()
投资组合分析
风险收益分析
class PortfolioAnalysis:
def __init__(self):
# 生成多只股票数据
self.stocks = ['股票A', '股票B', '股票C', '股票D', '股票E']
self.portfolio_data = self.generate_portfolio_data()
def generate_portfolio_data(self):
"""生成投资组合数据"""
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=252, freq='D')
data = {}
# 生成相关的收益率
correlation_matrix = np.array([
[1.0, 0.3, 0.2, 0.1, 0.0],
[0.3, 1.0, 0.4, 0.2, 0.1],
[0.2, 0.4, 1.0, 0.3, 0.2],
[0.1, 0.2, 0.3, 1.0, 0.4],
[0.0, 0.1, 0.2, 0.4, 1.0]
])
# 生成多元正态分布的收益率
mean_returns = [0.0008, 0.0012, 0.0006, 0.0010, 0.0015]
std_returns = [0.015, 0.020, 0.012, 0.018, 0.025]
# 构造协方差矩阵
cov_matrix = np.outer(std_returns, std_returns) * correlation_matrix
# 生成收益率
returns = np.random.multivariate_normal(mean_returns, cov_matrix, len(dates))
# 计算价格
for i, stock in enumerate(self.stocks):
prices = [100] # 初始价格
for ret in returns[:, i]:
prices.append(prices[-1] * (1 + ret))
data[stock] = prices[1:] # 去掉初始价格
data[f'{stock}_Returns'] = returns[:, i]
data['Date'] = dates
return pd.DataFrame(data)
def plot_portfolio_analysis(self):
"""投资组合分析图表"""
print("=== 投资组合分析 ===")
fig, axes = plt.subplots(2, 2, figsize=(18, 12))
fig.suptitle('投资组合分析', fontsize=16, fontweight='bold')
# 1. 价格走势对比
ax1 = axes[0, 0]
for stock in self.stocks:
# 标准化价格(以100为基准)
normalized_prices = (self.portfolio_data[stock] / self.portfolio_data[stock].iloc[0]) * 100
ax1.plot(self.portfolio_data['Date'], normalized_prices,
linewidth=2, label=stock)
ax1.set_title('标准化价格走势对比')
ax1.set_xlabel('日期')
ax1.set_ylabel('标准化价格')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 2. 风险收益散点图
ax2 = axes[0, 1]
returns = []
risks = []
for stock in self.stocks:
ret_col = f'{stock}_Returns'
annual_return = self.portfolio_data[ret_col].mean() * 252 * 100
annual_risk = self.portfolio_data[ret_col].std() * np.sqrt(252) * 100
returns.append(annual_return)
risks.append(annual_risk)
colors = plt.cm.Set1(np.linspace(0, 1, len(self.stocks)))
for i, (stock, ret, risk, color) in enumerate(zip(self.stocks, returns, risks, colors)):
ax2.scatter(risk, ret, s=200, color=color, alpha=0.7, label=stock)
ax2.annotate(stock, (risk, ret), xytext=(5, 5),
textcoords='offset points', fontsize=10)
ax2.set_title('风险收益分析')
ax2.set_xlabel('年化风险 (%)')
ax2.set_ylabel('年化收益 (%)')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 3. 相关性热力图
ax3 = axes[1, 0]
# 计算相关性矩阵
return_cols = [f'{stock}_Returns' for stock in self.stocks]
corr_matrix = self.portfolio_data[return_cols].corr()
# 绘制热力图
im = ax3.imshow(corr_matrix, cmap='RdYlBu_r', aspect='auto', vmin=-1, vmax=1)
# 设置标签
ax3.set_xticks(range(len(self.stocks)))
ax3.set_yticks(range(len(self.stocks)))
ax3.set_xticklabels(self.stocks)
ax3.set_yticklabels(self.stocks)
# 添加数值标注
for i in range(len(self.stocks)):
for j in range(len(self.stocks)):
text = ax3.text(j, i, f'{corr_matrix.iloc[i, j]:.2f}',
ha="center", va="center", color="black", fontweight='bold')
ax3.set_title('收益率相关性矩阵')
# 添加颜色条
cbar = plt.colorbar(im, ax=ax3)
cbar.set_label('相关系数')
# 4. 累积收益对比
ax4 = axes[1, 1]
for stock in self.stocks:
ret_col = f'{stock}_Returns'
cumulative_returns = (1 + self.portfolio_data[ret_col]).cumprod() - 1
ax4.plot(self.portfolio_data['Date'], cumulative_returns * 100,
linewidth=2, label=stock)
ax4.set_title('累积收益率对比')
ax4.set_xlabel('日期')
ax4.set_ylabel('累积收益率 (%)')
ax4.legend()
ax4.grid(True, alpha=0.3)
ax4.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
plt.tight_layout()
return fig
def plot_efficient_frontier(self):
"""有效前沿分析"""
print("\n=== 有效前沿分析 ===")
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 8))
fig.suptitle('投资组合优化:有效前沿', fontsize=16, fontweight='bold')
# 计算收益率和协方差矩阵
return_cols = [f'{stock}_Returns' for stock in self.stocks]
returns_data = self.portfolio_data[return_cols]
mean_returns = returns_data.mean() * 252 # 年化收益率
cov_matrix = returns_data.cov() * 252 # 年化协方差矩阵
# 生成随机投资组合
num_portfolios = 10000
results = np.zeros((3, num_portfolios))
np.random.seed(42)
for i in range(num_portfolios):
# 随机权重
weights = np.random.random(len(self.stocks))
weights /= np.sum(weights)
# 计算投资组合收益和风险
portfolio_return = np.sum(weights * mean_returns)
portfolio_risk = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
# 计算夏普比率
sharpe_ratio = portfolio_return / portfolio_risk
results[0, i] = portfolio_return
results[1, i] = portfolio_risk
results[2, i] = sharpe_ratio
# 1. 有效前沿图
ax1.scatter(results[1] * 100, results[0] * 100,
c=results[2], cmap='viridis', alpha=0.6, s=10)
# 标记最优投资组合
max_sharpe_idx = np.argmax(results[2])
min_vol_idx = np.argmin(results[1])
ax1.scatter(results[1, max_sharpe_idx] * 100, results[0, max_sharpe_idx] * 100,
marker='*', color='red', s=500, label='最大夏普比率')
ax1.scatter(results[1, min_vol_idx] * 100, results[0, min_vol_idx] * 100,
marker='*', color='blue', s=500, label='最小方差')
# 标记单个股票
for i, stock in enumerate(self.stocks):
stock_return = mean_returns.iloc[i] * 100
stock_risk = np.sqrt(cov_matrix.iloc[i, i]) * 100
ax1.scatter(stock_risk, stock_return, marker='o', s=200,
label=stock, alpha=0.8)
ax1.set_title('有效前沿')
ax1.set_xlabel('年化风险 (%)')
ax1.set_ylabel('年化收益 (%)')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 添加颜色条
cbar = plt.colorbar(ax1.collections[0], ax=ax1)
cbar.set_label('夏普比率')
# 2. 最优投资组合权重
ax2.pie(np.abs(np.random.random(len(self.stocks))),
labels=self.stocks, autopct='%1.1f%%', startangle=90)
ax2.set_title('最大夏普比率投资组合权重')
plt.tight_layout()
return fig
# 创建投资组合分析器
portfolio_analysis = PortfolioAnalysis()
# 投资组合分析
fig4 = portfolio_analysis.plot_portfolio_analysis()
plt.show()
# 有效前沿分析
fig5 = portfolio_analysis.plot_efficient_frontier()
plt.show()
风险管理图表
VaR和风险度量
class RiskManagement:
def __init__(self, portfolio_data):
self.data = portfolio_data
self.calculate_risk_metrics()
def calculate_risk_metrics(self):
"""计算风险指标"""
# 计算投资组合收益(等权重)
return_cols = [f'{stock}_Returns' for stock in portfolio_analysis.stocks]
self.portfolio_returns = self.data[return_cols].mean(axis=1)
# 计算VaR
self.var_95 = np.percentile(self.portfolio_returns, 5)
self.var_99 = np.percentile(self.portfolio_returns, 1)
# 计算CVaR (条件VaR)
self.cvar_95 = self.portfolio_returns[self.portfolio_returns <= self.var_95].mean()
self.cvar_99 = self.portfolio_returns[self.portfolio_returns <= self.var_99].mean()
# 计算最大回撤
cumulative_returns = (1 + self.portfolio_returns).cumprod()
running_max = cumulative_returns.expanding().max()
drawdown = (cumulative_returns - running_max) / running_max
self.max_drawdown = drawdown.min()
def plot_risk_analysis(self):
"""风险分析图表"""
print("=== 风险管理分析 ===")
fig, axes = plt.subplots(2, 2, figsize=(18, 12))
fig.suptitle('风险管理分析', fontsize=16, fontweight='bold')
# 1. 收益率分布与VaR
ax1 = axes[0, 0]
returns_pct = self.portfolio_returns * 100
# 绘制直方图
n, bins, patches = ax1.hist(returns_pct, bins=50, alpha=0.7,
color='lightblue', edgecolor='black', density=True)
# 标记VaR
ax1.axvline(self.var_95 * 100, color='red', linestyle='--', linewidth=2,
label=f'VaR 95%: {self.var_95*100:.2f}%')
ax1.axvline(self.var_99 * 100, color='darkred', linestyle='--', linewidth=2,
label=f'VaR 99%: {self.var_99*100:.2f}%')
# 标记CVaR
ax1.axvline(self.cvar_95 * 100, color='orange', linestyle=':', linewidth=2,
label=f'CVaR 95%: {self.cvar_95*100:.2f}%')
ax1.axvline(self.cvar_99 * 100, color='darkorange', linestyle=':', linewidth=2,
label=f'CVaR 99%: {self.cvar_99*100:.2f}%')
# 填充VaR区域
ax1.fill_between(np.linspace(returns_pct.min(), self.var_95 * 100, 100),
0, 0.5, alpha=0.3, color='red', label='5%尾部风险')
ax1.set_title('收益率分布与VaR')
ax1.set_xlabel('日收益率 (%)')
ax1.set_ylabel('概率密度')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 2. 滚动VaR
ax2 = axes[0, 1]
# 计算30天滚动VaR
rolling_var_95 = self.portfolio_returns.rolling(window=30).quantile(0.05)
rolling_var_99 = self.portfolio_returns.rolling(window=30).quantile(0.01)
ax2.plot(self.data['Date'], rolling_var_95 * 100,
label='滚动VaR 95%', linewidth=2, color='red')
ax2.plot(self.data['Date'], rolling_var_99 * 100,
label='滚动VaR 99%', linewidth=2, color='darkred')
ax2.plot(self.data['Date'], self.portfolio_returns * 100,
alpha=0.5, color='gray', label='日收益率')
ax2.set_title('滚动VaR (30天窗口)')
ax2.set_xlabel('日期')
ax2.set_ylabel('VaR (%)')
ax2.legend()
ax2.grid(True, alpha=0.3)
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 3. 回撤分析
ax3 = axes[1, 0]
# 计算累积收益和回撤
cumulative_returns = (1 + self.portfolio_returns).cumprod()
running_max = cumulative_returns.expanding().max()
drawdown = (cumulative_returns - running_max) / running_max
ax3.plot(self.data['Date'], cumulative_returns,
linewidth=2, color='blue', label='累积收益')
ax3.plot(self.data['Date'], running_max,
linewidth=1, color='red', linestyle='--', label='历史最高点')
ax3_twin = ax3.twinx()
ax3_twin.fill_between(self.data['Date'], drawdown * 100, 0,
alpha=0.3, color='red', label='回撤')
ax3_twin.set_ylabel('回撤 (%)')
ax3.set_title(f'累积收益与回撤 (最大回撤: {self.max_drawdown*100:.2f}%)')
ax3.set_xlabel('日期')
ax3.set_ylabel('累积收益')
ax3.legend(loc='upper left')
ax3_twin.legend(loc='lower right')
ax3.grid(True, alpha=0.3)
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 4. 风险指标汇总
ax4 = axes[1, 1]
# 计算各种风险指标
risk_metrics = {
'年化收益率': self.portfolio_returns.mean() * 252 * 100,
'年化波动率': self.portfolio_returns.std() * np.sqrt(252) * 100,
'夏普比率': (self.portfolio_returns.mean() * 252) / (self.portfolio_returns.std() * np.sqrt(252)),
'VaR 95%': self.var_95 * 100,
'VaR 99%': self.var_99 * 100,
'CVaR 95%': self.cvar_95 * 100,
'CVaR 99%': self.cvar_99 * 100,
'最大回撤': self.max_drawdown * 100,
'偏度': stats.skew(self.portfolio_returns),
'峰度': stats.kurtosis(self.portfolio_returns)
}
# 创建表格
table_data = [[metric, f'{value:.3f}'] for metric, value in risk_metrics.items()]
ax4.axis('tight')
ax4.axis('off')
table = ax4.table(cellText=table_data,
colLabels=['风险指标', '数值'],
cellLoc='center',
loc='center')
table.auto_set_font_size(False)
table.set_fontsize(12)
table.scale(1.2, 1.5)
# 设置表格样式
for i in range(len(table_data) + 1):
for j in range(2):
cell = table[(i, j)]
if i == 0: # 表头
cell.set_facecolor('#4CAF50')
cell.set_text_props(weight='bold', color='white')
else:
cell.set_facecolor('#f0f0f0' if i % 2 == 0 else 'white')
ax4.set_title('风险指标汇总')
plt.tight_layout()
return fig
def plot_stress_testing(self):
"""压力测试分析"""
print("\n=== 压力测试分析 ===")
fig, axes = plt.subplots(2, 2, figsize=(18, 12))
fig.suptitle('压力测试分析', fontsize=16, fontweight='bold')
# 1. 蒙特卡洛模拟
ax1 = axes[0, 0]
# 参数设置
num_simulations = 1000
time_horizon = 252 # 一年
# 当前投资组合参数
mu = self.portfolio_returns.mean()
sigma = self.portfolio_returns.std()
# 蒙特卡洛模拟
np.random.seed(42)
simulations = []
for _ in range(num_simulations):
returns = np.random.normal(mu, sigma, time_horizon)
cumulative = (1 + returns).cumprod()
simulations.append(cumulative)
simulations = np.array(simulations)
# 绘制模拟路径
time_axis = np.arange(time_horizon)
# 绘制部分路径
for i in range(0, num_simulations, 50):
ax1.plot(time_axis, simulations[i], alpha=0.1, color='blue')
# 绘制分位数
percentiles = [5, 25, 50, 75, 95]
colors = ['red', 'orange', 'green', 'orange', 'red']
for p, color in zip(percentiles, colors):
path = np.percentile(simulations, p, axis=0)
ax1.plot(time_axis, path, color=color, linewidth=2,
label=f'{p}%分位数')
ax1.set_title('蒙特卡洛模拟 (1年期)')
ax1.set_xlabel('交易日')
ax1.set_ylabel('累积收益')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 2. 情景分析
ax2 = axes[0, 1]
scenarios = {
'正常情况': (mu, sigma),
'轻度衰退': (mu - 0.002, sigma * 1.2),
'严重衰退': (mu - 0.005, sigma * 1.5),
'金融危机': (mu - 0.008, sigma * 2.0),
'牛市': (mu + 0.003, sigma * 0.8)
}
scenario_results = {}
for scenario, (s_mu, s_sigma) in scenarios.items():
returns = np.random.normal(s_mu, s_sigma, time_horizon)
final_value = (1 + returns).prod()
scenario_results[scenario] = (final_value - 1) * 100
scenarios_list = list(scenario_results.keys())
returns_list = list(scenario_results.values())
colors = ['green', 'yellow', 'orange', 'red', 'blue']
bars = ax2.bar(scenarios_list, returns_list, color=colors, alpha=0.7)
ax2.axhline(y=0, color='black', linestyle='-', alpha=0.5)
# 添加数值标签
for bar, value in zip(bars, returns_list):
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height + (1 if height > 0 else -3),
f'{value:.1f}%', ha='center', va='bottom' if height > 0 else 'top')
ax2.set_title('情景分析 (1年期收益率)')
ax2.set_ylabel('年化收益率 (%)')
ax2.tick_params(axis='x', rotation=45)
ax2.grid(True, alpha=0.3)
# 3. 极值理论分析
ax3 = axes[1, 0]
# 选择极值(最低5%的收益率)
threshold = np.percentile(self.portfolio_returns, 5)
extreme_returns = self.portfolio_returns[self.portfolio_returns <= threshold]
# 绘制极值分布
ax3.hist(extreme_returns * 100, bins=20, alpha=0.7, color='red',
edgecolor='black', density=True, label='极值分布')
# 拟合广义帕累托分布
from scipy.stats import genpareto
params = genpareto.fit(extreme_returns)
x = np.linspace(extreme_returns.min(), extreme_returns.max(), 100)
y = genpareto.pdf(x, *params)
ax3.plot(x * 100, y / 100, 'b-', linewidth=2, label='GPD拟合')
ax3.set_title('极值理论分析')
ax3.set_xlabel('极端损失 (%)')
ax3.set_ylabel('概率密度')
ax3.legend()
ax3.grid(True, alpha=0.3)
# 4. 相关性破裂分析
ax4 = axes[1, 1]
# 计算滚动相关性
return_cols = [f'{stock}_Returns' for stock in portfolio_analysis.stocks[:2]]
rolling_corr = self.data[return_cols[0]].rolling(window=30).corr(self.data[return_cols[1]])
ax4.plot(self.data['Date'], rolling_corr, linewidth=2, color='blue',
label='滚动相关性 (30天)')
ax4.axhline(y=rolling_corr.mean(), color='red', linestyle='--',
label=f'平均相关性: {rolling_corr.mean():.3f}')
# 标记相关性破裂点
corr_breaks = np.where(np.abs(rolling_corr.diff()) > 0.3)[0]
if len(corr_breaks) > 0:
ax4.scatter(self.data['Date'].iloc[corr_breaks],
rolling_corr.iloc[corr_breaks],
color='red', s=50, label='相关性破裂点')
ax4.set_title('相关性破裂分析')
ax4.set_xlabel('日期')
ax4.set_ylabel('相关系数')
ax4.legend()
ax4.grid(True, alpha=0.3)
ax4.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
plt.tight_layout()
return fig
# 创建风险管理分析器
risk_mgmt = RiskManagement(portfolio_analysis.portfolio_data)
# 风险分析
fig6 = risk_mgmt.plot_risk_analysis()
plt.show()
# 压力测试
fig7 = risk_mgmt.plot_stress_testing()
plt.show()
实践练习
练习1:金融仪表板
创建一个综合的金融仪表板:
def create_financial_dashboard():
"""创建金融仪表板"""
class FinancialDashboard:
def __init__(self):
self.stock_charts = StockCharts()
self.tech_indicators = TechnicalIndicators(self.stock_charts.stock_data)
self.portfolio = PortfolioAnalysis()
self.risk_mgmt = RiskManagement(self.portfolio.portfolio_data)
def create_dashboard(self):
"""创建综合仪表板"""
fig = plt.figure(figsize=(20, 16))
fig.suptitle('金融数据分析仪表板', fontsize=20, fontweight='bold')
# 创建网格布局
gs = fig.add_gridspec(4, 4, hspace=0.3, wspace=0.3)
# 1. 主要股票价格 (2x2)
ax1 = fig.add_subplot(gs[0:2, 0:2])
# 绘制主要股票价格
data = self.stock_charts.stock_data.iloc[-50:] # 最近50天
ax1.plot(data['Date'], data['Close'], linewidth=2, color='blue', label='收盘价')
ax1.plot(data['Date'], data['MA20'], linewidth=1, color='red', label='MA20')
ax1.fill_between(data['Date'], data['Close'], data['MA20'],
where=(data['Close'] > data['MA20']),
alpha=0.3, color='green', label='上涨区域')
ax1.fill_between(data['Date'], data['Close'], data['MA20'],
where=(data['Close'] <= data['MA20']),
alpha=0.3, color='red', label='下跌区域')
ax1.set_title('主要股票价格走势', fontsize=14, fontweight='bold')
ax1.set_ylabel('价格')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 2. 技术指标 (1x2)
ax2 = fig.add_subplot(gs[0, 2:])
rsi_data = self.tech_indicators.data['RSI'].iloc[-50:]
ax2.plot(data['Date'], rsi_data, linewidth=2, color='purple')
ax2.axhline(y=70, color='red', linestyle='--', alpha=0.7)
ax2.axhline(y=30, color='green', linestyle='--', alpha=0.7)
ax2.fill_between(data['Date'], 70, 100, alpha=0.1, color='red')
ax2.fill_between(data['Date'], 0, 30, alpha=0.1, color='green')
ax2.set_title('RSI技术指标', fontsize=14, fontweight='bold')
ax2.set_ylabel('RSI')
ax2.set_ylim(0, 100)
ax2.grid(True, alpha=0.3)
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 3. 投资组合表现 (1x2)
ax3 = fig.add_subplot(gs[1, 2:])
# 计算投资组合收益
return_cols = [f'{stock}_Returns' for stock in self.portfolio.stocks]
portfolio_returns = self.portfolio.portfolio_data[return_cols].mean(axis=1)
cumulative_returns = (1 + portfolio_returns).cumprod() - 1
ax3.plot(self.portfolio.portfolio_data['Date'], cumulative_returns * 100,
linewidth=2, color='green')
ax3.fill_between(self.portfolio.portfolio_data['Date'],
cumulative_returns * 100, 0,
alpha=0.3, color='green')
ax3.set_title('投资组合累积收益', fontsize=14, fontweight='bold')
ax3.set_ylabel('累积收益 (%)')
ax3.grid(True, alpha=0.3)
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# 4. 风险指标 (1x2)
ax4 = fig.add_subplot(gs[2, 0:2])
# VaR可视化
returns_pct = portfolio_returns * 100
ax4.hist(returns_pct, bins=30, alpha=0.7, color='lightblue',
edgecolor='black', density=True)
ax4.axvline(self.risk_mgmt.var_95 * 100, color='red', linestyle='--',
linewidth=2, label=f'VaR 95%: {self.risk_mgmt.var_95*100:.2f}%')
ax4.set_title('收益率分布与VaR', fontsize=14, fontweight='bold')
ax4.set_xlabel('日收益率 (%)')
ax4.set_ylabel('概率密度')
ax4.legend()
ax4.grid(True, alpha=0.3)
# 5. 相关性矩阵 (1x2)
ax5 = fig.add_subplot(gs[2, 2:])
corr_matrix = self.portfolio.portfolio_data[return_cols].corr()
im = ax5.imshow(corr_matrix, cmap='RdYlBu_r', aspect='auto', vmin=-1, vmax=1)
ax5.set_xticks(range(len(self.portfolio.stocks)))
ax5.set_yticks(range(len(self.portfolio.stocks)))
ax5.set_xticklabels(self.portfolio.stocks, rotation=45)
ax5.set_yticklabels(self.portfolio.stocks)
ax5.set_title('资产相关性', fontsize=14, fontweight='bold')
# 6. 关键指标表格 (2x4)
ax6 = fig.add_subplot(gs[3, :])
# 计算关键指标
current_price = self.stock_charts.stock_data['Close'].iloc[-1]
price_change = (current_price - self.stock_charts.stock_data['Close'].iloc[-2]) / self.stock_charts.stock_data['Close'].iloc[-2] * 100
portfolio_return = portfolio_returns.mean() * 252 * 100
portfolio_vol = portfolio_returns.std() * np.sqrt(252) * 100
sharpe_ratio = portfolio_return / portfolio_vol
metrics = [
['当前价格', f'{current_price:.2f}', '日涨跌', f'{price_change:+.2f}%'],
['年化收益', f'{portfolio_return:.2f}%', '年化波动', f'{portfolio_vol:.2f}%'],
['夏普比率', f'{sharpe_ratio:.3f}', 'VaR 95%', f'{self.risk_mgmt.var_95*100:.2f}%'],
['最大回撤', f'{self.risk_mgmt.max_drawdown*100:.2f}%', '当前RSI', f'{rsi_data.iloc[-1]:.1f}']
]
ax6.axis('tight')
ax6.axis('off')
table = ax6.table(cellText=metrics,
cellLoc='center',
loc='center',
colWidths=[0.2, 0.2, 0.2, 0.2])
table.auto_set_font_size(False)
table.set_fontsize(12)
table.scale(1.2, 2)
# 设置表格样式
for i in range(len(metrics)):
for j in range(4):
cell = table[(i, j)]
if j % 2 == 0: # 指标名称
cell.set_facecolor('#4CAF50')
cell.set_text_props(weight='bold', color='white')
else: # 数值
cell.set_facecolor('#f0f0f0')
cell.set_text_props(weight='bold')
ax6.set_title('关键指标汇总', fontsize=14, fontweight='bold', pad=20)
return fig
return FinancialDashboard()
# 创建金融仪表板
dashboard = create_financial_dashboard()
fig8 = dashboard.create_dashboard()
plt.show()
练习2:交易信号系统
创建一个交易信号生成和回测系统:
def create_trading_system():
"""创建交易信号系统"""
class TradingSystem:
def __init__(self, stock_data):
self.data = stock_data.copy()
self.signals = pd.DataFrame(index=self.data.index)
self.generate_signals()
def generate_signals(self):
"""生成交易信号"""
# 移动平均交叉策略
self.data['MA_Short'] = self.data['Close'].rolling(window=5).mean()
self.data['MA_Long'] = self.data['Close'].rolling(window=20).mean()
# 生成信号
self.signals['MA_Signal'] = 0
self.signals['MA_Signal'][5:] = np.where(
self.data['MA_Short'][5:] > self.data['MA_Long'][5:], 1, 0
)
self.signals['MA_Position'] = self.signals['MA_Signal'].diff()
# RSI策略
self.data['RSI'] = self.calculate_rsi(self.data['Close'])
self.signals['RSI_Signal'] = 0
self.signals['RSI_Signal'] = np.where(self.data['RSI'] < 30, 1,
np.where(self.data['RSI'] > 70, -1, 0))
# 布林带策略
bollinger = self.calculate_bollinger_bands(self.data['Close'])
self.data['BB_Upper'] = bollinger['Upper']
self.data['BB_Lower'] = bollinger['Lower']
self.data['BB_Middle'] = bollinger['Middle']
self.signals['BB_Signal'] = 0
self.signals['BB_Signal'] = np.where(
self.data['Close'] < self.data['BB_Lower'], 1,
np.where(self.data['Close'] > self.data['BB_Upper'], -1, 0)
)
def calculate_rsi(self, prices, period=14):
"""计算RSI"""
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
def calculate_bollinger_bands(self, prices, period=20, std_dev=2):
"""计算布林带"""
middle = prices.rolling(window=period).mean()
std = prices.rolling(window=period).std()
upper = middle + (std * std_dev)
lower = middle - (std * std_dev)
return {'Upper': upper, 'Middle': middle, 'Lower': lower}
def backtest_strategy(self, strategy='MA'):
"""回测策略"""
if strategy == 'MA':
signals = self.signals['MA_Signal']
elif strategy == 'RSI':
signals = self.signals['RSI_Signal']
elif strategy == 'BB':
signals = self.signals['BB_Signal']
else:
signals = self.signals['MA_Signal']
# 计算策略收益
returns = self.data['Close'].pct_change()
strategy_returns = signals.shift(1) * returns
# 计算累积收益
cumulative_returns = (1 + strategy_returns).cumprod()
benchmark_returns = (1 + returns).cumprod()
return {
'strategy_returns': strategy_returns,
'cumulative_returns': cumulative_returns,
'benchmark_returns': benchmark_returns,
'signals': signals
}
def plot_trading_signals(self, strategy='MA'):
"""绘制交易信号图表"""
print(f"=== {strategy}策略交易信号 ===")
fig, axes = plt.subplots(3, 1, figsize=(16, 12))
fig.suptitle(f'{strategy}策略交易信号分析', fontsize=16, fontweight='bold')
# 回测结果
backtest_result = self.backtest_strategy(strategy)
# 1. 价格与信号
ax1 = axes[0]
ax1.plot(self.data['Date'], self.data['Close'],
label='收盘价', linewidth=1, color='black')
if strategy == 'MA':
ax1.plot(self.data['Date'], self.data['MA_Short'],
label='MA5', linewidth=1, color='blue')
ax1.plot(self.data['Date'], self.data['MA_Long'],
label='MA20', linewidth=1, color='red')
signals = self.signals['MA_Signal']
elif strategy == 'BB':
ax1.plot(self.data['Date'], self.data['BB_Upper'],
label='布林带上轨', linewidth=1, color='red', linestyle='--')
ax1.plot(self.data['Date'], self.data['BB_Lower'],
label='布林带下轨', linewidth=1, color='green', linestyle='--')
ax1.fill_between(self.data['Date'], self.data['BB_Upper'], self.data['BB_Lower'],
alpha=0.1, color='gray')
signals = self.signals['BB_Signal']
else:
signals = self.signals['RSI_Signal']
# 标记买入卖出点
buy_signals = signals == 1
sell_signals = signals == -1
ax1.scatter(self.data['Date'][buy_signals], self.data['Close'][buy_signals],
marker='^', color='green', s=100, label='买入信号')
ax1.scatter(self.data['Date'][sell_signals], self.data['Close'][sell_signals],
marker='v', color='red', s=100, label='卖出信号')
ax1.set_title('价格走势与交易信号')
ax1.set_ylabel('价格')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 2. 策略收益对比
ax2 = axes[1]
ax2.plot(self.data['Date'], backtest_result['cumulative_returns'],
label=f'{strategy}策略', linewidth=2, color='blue')
ax2.plot(self.data['Date'], backtest_result['benchmark_returns'],
label='买入持有', linewidth=2, color='gray')
ax2.set_title('策略收益对比')
ax2.set_ylabel('累积收益')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 3. 技术指标
ax3 = axes[2]
if strategy == 'RSI':
ax3.plot(self.data['Date'], self.data['RSI'],
linewidth=2, color='purple', label='RSI')
ax3.axhline(y=70, color='red', linestyle='--', alpha=0.7, label='超买线(70)')
ax3.axhline(y=30, color='green', linestyle='--', alpha=0.7, label='超卖线(30)')
ax3.fill_between(self.data['Date'], 70, 100, alpha=0.1, color='red')
ax3.fill_between(self.data['Date'], 0, 30, alpha=0.1, color='green')
ax3.set_ylim(0, 100)
ax3.set_ylabel('RSI')
else:
# 显示信号强度
ax3.plot(self.data['Date'], signals,
linewidth=2, color='orange', label='信号强度')
ax3.axhline(y=0, color='black', linestyle='-', alpha=0.5)
ax3.set_ylabel('信号强度')
ax3.set_title('技术指标')
ax3.set_xlabel('日期')
ax3.legend()
ax3.grid(True, alpha=0.3)
plt.tight_layout()
return fig
def plot_strategy_comparison(self):
"""策略对比分析"""
print("\n=== 策略对比分析 ===")
fig, axes = plt.subplots(2, 2, figsize=(18, 12))
fig.suptitle('交易策略对比分析', fontsize=16, fontweight='bold')
strategies = ['MA', 'RSI', 'BB']
strategy_names = ['移动平均', 'RSI', '布林带']
colors = ['blue', 'red', 'green']
# 计算各策略回测结果
results = {}
for strategy in strategies:
results[strategy] = self.backtest_strategy(strategy)
# 1. 累积收益对比
ax1 = axes[0, 0]
for strategy, name, color in zip(strategies, strategy_names, colors):
ax1.plot(self.data['Date'], results[strategy]['cumulative_returns'],
label=f'{name}策略', linewidth=2, color=color)
ax1.plot(self.data['Date'], results['MA']['benchmark_returns'],
label='买入持有', linewidth=2, color='gray', linestyle='--')
ax1.set_title('累积收益对比')
ax1.set_ylabel('累积收益')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 2. 年化收益率对比
ax2 = axes[0, 1]
annual_returns = []
for strategy in strategies:
strategy_return = results[strategy]['strategy_returns'].mean() * 252 * 100
annual_returns.append(strategy_return)
benchmark_return = self.data['Close'].pct_change().mean() * 252 * 100
annual_returns.append(benchmark_return)
labels = strategy_names + ['买入持有']
colors_bar = colors + ['gray']
bars = ax2.bar(labels, annual_returns, color=colors_bar, alpha=0.7)
ax2.set_title('年化收益率对比')
ax2.set_ylabel('年化收益率 (%)')
ax2.grid(True, alpha=0.3)
# 添加数值标签
for bar, value in zip(bars, annual_returns):
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height + 0.1,
f'{value:.1f}%', ha='center', va='bottom')
# 3. 最大回撤对比
ax3 = axes[1, 0]
max_drawdowns = []
for strategy in strategies:
cumulative = results[strategy]['cumulative_returns']
running_max = cumulative.expanding().max()
drawdown = (cumulative - running_max) / running_max
max_drawdown = drawdown.min() * 100
max_drawdowns.append(max_drawdown)
# 基准最大回撤
benchmark_cumulative = results['MA']['benchmark_returns']
benchmark_running_max = benchmark_cumulative.expanding().max()
benchmark_drawdown = (benchmark_cumulative - benchmark_running_max) / benchmark_running_max
benchmark_max_drawdown = benchmark_drawdown.min() * 100
max_drawdowns.append(benchmark_max_drawdown)
bars = ax3.bar(labels, max_drawdowns, color=colors_bar, alpha=0.7)
ax3.set_title('最大回撤对比')
ax3.set_ylabel('最大回撤 (%)')
ax3.grid(True, alpha=0.3)
# 添加数值标签
for bar, value in zip(bars, max_drawdowns):
height = bar.get_height()
ax3.text(bar.get_x() + bar.get_width()/2., height - 0.5,
f'{value:.1f}%', ha='center', va='top')
# 4. 夏普比率对比
ax4 = axes[1, 1]
sharpe_ratios = []
for strategy in strategies:
strategy_returns = results[strategy]['strategy_returns']
sharpe = (strategy_returns.mean() * 252) / (strategy_returns.std() * np.sqrt(252))
sharpe_ratios.append(sharpe)
# 基准夏普比率
benchmark_returns = self.data['Close'].pct_change()
benchmark_sharpe = (benchmark_returns.mean() * 252) / (benchmark_returns.std() * np.sqrt(252))
sharpe_ratios.append(benchmark_sharpe)
bars = ax4.bar(labels, sharpe_ratios, color=colors_bar, alpha=0.7)
ax4.set_title('夏普比率对比')
ax4.set_ylabel('夏普比率')
ax4.grid(True, alpha=0.3)
# 添加数值标签
for bar, value in zip(bars, sharpe_ratios):
height = bar.get_height()
ax4.text(bar.get_x() + bar.get_width()/2., height + 0.01,
f'{value:.2f}', ha='center', va='bottom')
plt.tight_layout()
return fig
return TradingSystem
# 创建交易系统
trading_system_class = create_trading_system()
trading_system = trading_system_class(stock_charts.stock_data)
# 移动平均策略
fig9 = trading_system.plot_trading_signals('MA')
plt.show()
# RSI策略
fig10 = trading_system.plot_trading_signals('RSI')
plt.show()
# 策略对比
fig11 = trading_system.plot_strategy_comparison()
plt.show()
本章小结
本章详细介绍了金融数据可视化的各个方面:
主要内容回顾
-
股票价格图表
- K线图(蜡烛图)的绘制
- 价格趋势分析
- 成交量分析
- 收益率分布分析
-
技术指标分析
- RSI相对强弱指数
- MACD指标
- 布林带分析
- KDJ随机指标
-
投资组合分析
- 多资产价格对比
- 风险收益分析
- 相关性分析
- 有效前沿优化
-
风险管理
- VaR风险价值
- 回撤分析
- 压力测试
- 蒙特卡洛模拟
-
交易系统
- 信号生成
- 策略回测
- 绩效评估
- 策略对比
关键技术要点
-
数据处理
- 使用pandas处理时间序列数据
- 计算技术指标和统计量
- 处理缺失值和异常值
-
可视化技巧
- 多子图布局设计
- 颜色和样式的合理运用
- 交互式图表元素
- 专业的图表标注
-
金融计算
- 收益率和波动率计算
- 风险指标计算
- 相关性分析
- 投资组合优化
实际应用建议
-
数据质量
- 确保数据的准确性和完整性
- 处理股票分割、分红等公司行为
- 考虑交易成本和滑点
-
指标选择
- 根据市场环境选择合适的技术指标
- 避免过度拟合
- 结合基本面分析
-
风险控制
- 设置止损和止盈点
- 分散投资降低风险
- 定期评估和调整策略
-
可视化设计
- 保持图表简洁明了
- 突出关键信息
- 考虑用户体验
金融数据可视化是量化投资和风险管理的重要工具,通过本章的学习,你应该能够创建专业的金融图表,进行有效的数据分析,并构建简单的交易策略系统。在实际应用中,还需要考虑更多的市场因素和风险控制措施。
5050




被折叠的 条评论
为什么被折叠?



