3、时间序列可视化:用Matplotlib画价格、成交量和均线

做量化交易,第一件事是什么?

我个人习惯——先把数据画出来看看。你想想看,一堆数字摆在那,你很难看出门道。但一旦变成图表,趋势、异常、规律,全都一目了然。这就是可视化的力量。

3.1 为什么可视化这么重要?

我在项目中遇到过不少新手,上来就搞复杂模型,结果数据本身就有问题——缺失值、异常跳空、甚至日期顺序都是乱的。可视化就是最好的数据质量检查工具。

具体来说,可视化能帮你做三件事:

  • 发现数据质量问题:一眼看出缺失、异常、跳空
  • 识别趋势和模式:上升、下降、震荡、季节性
  • 验证交易策略:均线金叉死叉、支撑阻力,画出来才直观

核心原则:先画图,再建模。这是时间序列分析的铁律。

3.2 准备工作:安装和导入库

我们先搭好环境。Matplotlib是Python最经典的绘图库,配合Pandas用起来很顺手。

# 安装(如果还没装)
# pip install matplotlib pandas

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# 设置中文字体(避免乱码)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

print("环境准备完成")

避坑指南:我曾经在Mac上折腾了半天中文字体,最后发现用'SimHei'不行,换成'Arial Unicode MS'才搞定。Windows用户一般没问题,Linux用户可能需要安装中文字体包。

3.3 准备示例数据

为了演示,我们生成一段模拟的股票价格数据。真实项目中你会从数据库或API获取,这里先造个假数据方便理解。

import numpy as np

# 生成250个交易日(约1年)的数据
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=250, freq='B')
price = 100 + np.cumsum(np.random.randn(250) * 0.5)
volume = np.random.randint(1000000, 5000000, size=250)

df = pd.DataFrame({
    'date': dates,
    'close': price,
    'volume': volume
})
df.set_index('date', inplace=True)

print(df.head())

3.4 绘制价格走势图

最基本的折线图,看价格随时间的变化。

fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(df.index, df['close'], color='#2196F3', linewidth=1.5)

ax.set_title('股票价格走势图', fontsize=16, fontweight='bold')
ax.set_xlabel('日期')
ax.set_ylabel('收盘价(元)')
ax.grid(True, alpha=0.3)

# 格式化x轴日期
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
ax.xaxis.set_major_locator(mdates.MonthLocator())
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

嗯,这里要注意:日期轴的格式化很关键。如果不处理,x轴标签会挤成一团,根本看不清。我习惯用mdates.DateFormatter来控制显示格式。

3.5 绘制成交量图

成交量通常用柱状图表示,红色涨、绿色跌。这个配色是A股的习惯,美股正好相反。

# 模拟涨跌颜色
df['change'] = df['close'].diff()
colors = ['#4CAF50' if c >= 0 else '#F44336' for c in df['change']]

fig, ax = plt.subplots(figsize=(12, 4))
ax.bar(df.index, df['volume'], color=colors, alpha=0.7, width=0.8)

ax.set_title('成交量图', fontsize=14, fontweight='bold')
ax.set_xlabel('日期')
ax.set_ylabel('成交量(手)')
ax.grid(True, alpha=0.3)

ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
ax.xaxis.set_major_locator(mdates.MonthLocator())
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

注意:第一天的change是NaN,因为没有前一天的数据。我一般会手动把第一天的颜色设为红色(涨),或者干脆跳过。这个小细节容易忽略,但画出来会报错。

3.6 叠加移动平均线

移动平均线是择时最基础的工具。5日均线代表短期趋势,20日均线代表中期趋势,60日均线代表长期趋势。

# 计算移动平均线
df['MA5'] = df['close'].rolling(window=5).mean()
df['MA20'] = df['close'].rolling(window=20).mean()
df['MA60'] = df['close'].rolling(window=60).mean()

fig, ax = plt.subplots(figsize=(12, 6))

# 画价格
ax.plot(df.index, df['close'], color='#333333', linewidth=1, alpha=0.7, label='收盘价')

# 画均线
ax.plot(df.index, df['MA5'], color='#FF9800', linewidth=1.5, label='MA5')
ax.plot(df.index, df['MA20'], color='#2196F3', linewidth=1.5, label='MA20')
ax.plot(df.index, df['MA60'], color='#9C27B0', linewidth=1.5, label='MA60')

ax.set_title('价格与移动平均线', fontsize=16, fontweight='bold')
ax.set_xlabel('日期')
ax.set_ylabel('价格(元)')
ax.legend(loc='best')
ax.grid(True, alpha=0.3)

ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
ax.xaxis.set_major_locator(mdates.MonthLocator())
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

个人经验:均线刚计算时,前N天是NaN值(比如MA5前4天没数据)。画图时Matplotlib会自动跳过这些点,但如果你要做策略回测,一定要处理好这些缺失值。我曾经因为没处理前几天的NaN,导致回测结果多算了几个点的收益——嗯,白高兴一场。

3.7 组合图:价格+成交量+均线

实战中,我们通常把价格和成交量放在同一张图里,上下排列,方便对照分析。

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 8), 
                                gridspec_kw={'height_ratios': [3, 1]})

# 上图:价格和均线
ax1.plot(df.index, df['close'], color='#333333', linewidth=1, alpha=0.7, label='收盘价')
ax1.plot(df.index, df['MA5'], color='#FF9800', linewidth=1.5, label='MA5')
ax1.plot(df.index, df['MA20'], color='#2196F3', linewidth=1.5, label='MA20')
ax1.plot(df.index, df['MA60'], color='#9C27B0', linewidth=1.5, label='MA60')
ax1.set_title('价格走势与成交量分析', fontsize=16, fontweight='bold')
ax1.set_ylabel('价格(元)')
ax1.legend(loc='upper left')
ax1.grid(True, alpha=0.3)

# 下图:成交量
colors = ['#4CAF50' if c >= 0 else '#F44336' for c in df['change']]
ax2.bar(df.index, df['volume'], color=colors, alpha=0.7, width=0.8)
ax2.set_ylabel('成交量(手)')
ax2.set_xlabel('日期')
ax2.grid(True, alpha=0.3)

# 统一x轴格式
for ax in [ax1, ax2]:
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
    ax.xaxis.set_major_locator(mdates.MonthLocator())
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

3.8 核心知识体系

我把这一章的核心逻辑画成了结构图,方便你理解各个知识点之间的关系。

时间序列可视化核心知识体系 可视化三要素 价格走势图 成交量图 移动平均线 折线图 日期格式化 柱状图 涨跌颜色 MA5/MA20/MA60 滚动窗口 组合图:价格 + 成交量 + 均线

3.9 常见问题与避坑

问题 原因 解决方案
中文显示为方框 系统缺少中文字体 设置plt.rcParams['font.sans-serif']为系统可用字体
日期轴标签重叠 数据点太多,标签太密 使用mdates.MonthLocator()控制刻度间隔,旋转标签
均线前段缺失 滚动窗口导致NaN dropna()处理,或从有数据的位置开始画
成交量颜色不对 第一天change为NaN 手动设置第一天的颜色,或用fillna(0)

重要提醒:可视化不是终点,而是起点。画完图之后,你要问自己三个问题:

  1. 数据有没有明显的异常点?
  2. 趋势是否清晰?有没有潜在的转折信号?
  3. 均线之间的关系如何?金叉还是死叉?

这三个问题想清楚了,再往下做模型才有意义。

好了,这一章的内容就到这里。可视化是基本功,但也是容易被忽视的一环。我个人觉得,把图画好,你的量化分析就成功了一半。剩下的,就是不断练习,直到形成肌肉记忆。

蓝海数据掘金营,专注资料整理