数据可视化是金融分析中的重要环节。好的图表不仅能直观展示数据,还能帮助发现隐藏的规律和异常。
常用可视化库对比
| 库 | 优势 | 适用场景 |
|---|---|---|
| Matplotlib | 高度可定制、静态图表 | 报告、论文 |
| Plotly | 交互式、美观 | Web展示、探索性分析 |
| mplfinance | 专门的金融图表库 | K线图、技术分析 |
基础设置
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import mplfinance as mpf
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 设置样式
plt.style.use('seaborn-v0_8-darkgrid')
1. K线图(蜡烛图)
使用 mplfinance
def plot_candlestick_mpf(data, title="K线图"):
ma_lines = mpf.make_addplot(data[['MA5', 'MA20']],
panel=0,
width=0.8)
mpf.plot(
data,
type='candle',
style='charles',
title=title,
ylabel='价格',
volume=True,
addplot=ma_lines,
figsize=(12, 8),
savefig='kline.png'
)
使用 Plotly(交互式)
def plot_candlestick_plotly(data, title="交互式K线图"):
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
vertical_spacing=0.03,
row_heights=[0.7, 0.3],
subplot_titles=(title, '成交量')
)
fig.add_trace(
go.Candlestick(
x=data.index,
open=data['open'],
high=data['high'],
low=data['low'],
close=data['close'],
name='K线'
),
row=1, col=1
)
fig.add_trace(
go.Scatter(
x=data.index,
y=data['MA5'],
name='MA5',
line=dict(color='orange', width=1)
),
row=1, col=1
)
fig.add_trace(
go.Scatter(
x=data.index,
y=data['MA20'],
name='MA20',
line=dict(color='blue', width=1)
),
row=1, col=1
)
colors = ['red' if row['close'] > row['open'] else 'green'
for _, row in data.iterrows()]
fig.add_trace(
go.Bar(
x=data.index,
y=data['volume'],
name='成交量',
marker_color=colors
),
row=2, col=1
)
fig.update_layout(
height=800,
xaxis_rangeslider_visible=False,
hovermode='x unified'
)
fig.show()
2. 技术指标图表
MACD 指标
def plot_macd(data):
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8),
sharex=True,
gridspec_kw={'height_ratios': [2, 1]})
ax1.plot(data.index, data['close'], label='收盘价', linewidth=1.5)
ax1.set_ylabel('价格')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax2.plot(data.index, data['MACD'], label='MACD', linewidth=1.5)
ax2.plot(data.index, data['Signal'], label='Signal', linewidth=1.5)
colors = ['red' if val > 0 else 'green' for val in data['MACD_Hist']]
ax2.bar(data.index, data['MACD_Hist'],
label='MACD Histogram', color=colors, alpha=0.3)
ax2.set_xlabel('日期')
ax2.set_ylabel('MACD')
ax2.legend()
ax2.grid(True, alpha=0.3)
ax2.axhline(y=0, color='black', linestyle='--', linewidth=0.5)
plt.tight_layout()
plt.show()
布林带
def plot_bollinger_bands(data):
plt.figure(figsize=(12, 6))
plt.plot(data.index, data['close'], label='收盘价',
color='black', linewidth=1.5)
plt.plot(data.index, data['BB_upper'], label='上轨',
color='red', linestyle='--', linewidth=1)
plt.plot(data.index, data['BB_middle'], label='中轨',
color='blue', linestyle='--', linewidth=1)
plt.plot(data.index, data['BB_lower'], label='下轨',
color='green', linestyle='--', linewidth=1)
plt.fill_between(data.index, data['BB_upper'], data['BB_lower'],
alpha=0.1, color='gray')
plt.xlabel('日期')
plt.ylabel('价格')
plt.title('布林带指标')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
3. 收益分析图表
收益分布直方图
def plot_returns_distribution(returns):
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
ax1.hist(returns, bins=50, alpha=0.7, color='steelblue', edgecolor='black')
ax1.axvline(returns.mean(), color='red', linestyle='--',
label=f'均值: {returns.mean():.4f}')
ax1.axvline(returns.median(), color='green', linestyle='--',
label=f'中位数: {returns.median():.4f}')
ax1.set_xlabel('收益率')
ax1.set_ylabel('频数')
ax1.set_title('收益率分布')
ax1.legend()
ax1.grid(True, alpha=0.3)
from scipy import stats
stats.probplot(returns, dist="norm", plot=ax2)
ax2.set_title('Q-Q 图')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
累计收益曲线
def plot_cumulative_returns(strategy_returns, benchmark_returns=None):
plt.figure(figsize=(12, 6))
cumulative_strategy = (1 + strategy_returns).cumprod()
plt.plot(cumulative_strategy.index, cumulative_strategy,
label='策略收益', linewidth=2)
if benchmark_returns is not None:
cumulative_benchmark = (1 + benchmark_returns).cumprod()
plt.plot(cumulative_benchmark.index, cumulative_benchmark,
label='基准收益', linewidth=2, alpha=0.7)
plt.xlabel('日期')
plt.ylabel('累计收益(倍数)')
plt.title('策略累计收益曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
回撤曲线
def plot_drawdown(returns):
cumulative = (1 + returns).cumprod()
running_max = cumulative.expanding().max()
drawdown = (cumulative - running_max) / running_max
plt.figure(figsize=(12, 6))
plt.fill_between(drawdown.index, drawdown, 0,
alpha=0.3, color='red', label='回撤')
plt.plot(drawdown.index, drawdown, color='darkred', linewidth=1.5)
max_dd_date = drawdown.idxmin()
max_dd_value = drawdown.min()
plt.plot(max_dd_date, max_dd_value, 'ro', markersize=10)
plt.annotate(f'最大回撤: {max_dd_value:.2%}',
xy=(max_dd_date, max_dd_value),
xytext=(10, 20), textcoords='offset points',
bbox=dict(boxstyle='round', fc='yellow', alpha=0.5),
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
plt.xlabel('日期')
plt.ylabel('回撤')
plt.title('策略回撤曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
4. 相关性分析
相关性热力图
def plot_correlation_heatmap(data):
import seaborn as sns
corr_matrix = data.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix,
annot=True,
cmap='coolwarm',
center=0,
square=True,
linewidths=0.5,
cbar_kws={"shrink": 0.8})
plt.title('资产相关性热力图')
plt.tight_layout()
plt.show()
5. 综合仪表盘
def create_dashboard(data, returns):
fig = plt.figure(figsize=(16, 10))
gs = fig.add_gridspec(3, 2, hspace=0.3, wspace=0.3)
ax1 = fig.add_subplot(gs[0, :])
ax1.plot(data.index, data['close'])
ax1.set_title('价格走势', fontsize=14, fontweight='bold')
ax1.set_ylabel('价格')
ax1.grid(True, alpha=0.3)
ax2 = fig.add_subplot(gs[1, 0])
ax2.bar(data.index, data['volume'], alpha=0.5)
ax2.set_title('成交量', fontsize=12, fontweight='bold')
ax2.set_ylabel('成交量')
ax2.grid(True, alpha=0.3)
ax3 = fig.add_subplot(gs[1, 1])
ax3.hist(returns, bins=30, alpha=0.7, edgecolor='black')
ax3.set_title('收益率分布', fontsize=12, fontweight='bold')
ax3.set_xlabel('收益率')
ax3.grid(True, alpha=0.3)
ax4 = fig.add_subplot(gs[2, 0])
cumulative = (1 + returns).cumprod()
ax4.plot(cumulative.index, cumulative)
ax4.set_title('累计收益', fontsize=12, fontweight='bold')
ax4.set_ylabel('累计收益(倍)')
ax4.grid(True, alpha=0.3)
ax5 = fig.add_subplot(gs[2, 1])
running_max = cumulative.expanding().max()
drawdown = (cumulative - running_max) / running_max
ax5.fill_between(drawdown.index, drawdown, 0, alpha=0.3, color='red')
ax5.set_title('回撤曲线', fontsize=12, fontweight='bold')
ax5.set_ylabel('回撤')
ax5.grid(True, alpha=0.3)
plt.show()
可视化最佳实践
-
选择合适的图表类型
- 趋势分析:折线图
- 分布分析:直方图、箱线图
- 比较分析:柱状图
- 相关性:散点图、热力图
-
注意颜色搭配
- 使用色盲友好的配色方案
- 红涨绿跌符合国内习惯
-
添加必要的标注
- 关键点位标注
- 统计指标显示
- 图例说明清晰
-
保持简洁
- 避免图表过于复杂
- 突出重点信息
- 适当留白
总结
掌握金融数据可视化能力可以:
- 快速发现数据中的模式和异常
- 更好地展示分析结果
- 辅助决策制定
建议根据不同场景选择合适的可视化工具和图表类型。
下一篇将介绍如何使用 Dash 创建交互式金融分析应用。