两行代码画折线,五行代码画 K 线。附完整源码,复制即用。
数据拉下来了,数据库也建好了。今天让数据「看得见」。
效果预览:收盘价图 → 量价双面板 → 专业 K 线图 + 均线叠加,三步走。
环境准备
bash
pip install matplotlib mplfinance akshare pandas
⚠️ 服务器环境必须先设置 Agg 后端 ,否则会报错 no display name and no $DISPLAY environment variable:
python
import matplotlib
matplotlib.use('Agg') # ← 必须在 import pyplot 之前
1. 收盘价折线图
python
import matplotlib.pyplot as plt
import akshare as ak
import pandas as pd
# 拉数据
df = ak.stock_zh_a_hist(symbol='000001', period='daily')
df['trade_date'] = pd.to_datetime(df['日期'])
df = df.sort_values('trade_date')
# 只取最近半年
recent = df[df['trade_date'] > '2025-11-01']
# 画图
plt.figure(figsize=(12, 5))
plt.plot(recent['trade_date'], recent['收盘'],
color='#1f77b4', linewidth=1.5)
plt.title('平安银行 (000001) --- 收盘价走势', fontsize=14)
plt.xlabel('日期')
plt.ylabel('价格 (元)')
plt.grid(alpha=0.3, linestyle='--')
plt.tight_layout()
plt.savefig('000001_price.png', dpi=150, bbox_inches='tight')
plt.close()
🔑 关键参数:
bbox_inches='tight'→ 自动裁掉多余白边dpi=150→ 屏幕画质和文件大小的平衡点- 画完记得
plt.close()释放内存
2. 价格 + 成交量双面板
成交量是价格的「燃料」。缩量上涨可能是假突破,放量下跌才是真恐慌。
python
fig, (ax1, ax2) = plt.subplots(
2, 1, figsize=(12, 8),
sharex=True, # 共享 X 轴
gridspec_kw={'height_ratios': [3, 1]} # 3:1 比例
)
# 上图:收盘价
ax1.plot(recent['trade_date'], recent['收盘'],
color='#1f77b4', linewidth=1.5, label='Close')
ax1.set_title('000001 平安银行 --- 价格与成交量', fontsize=14)
ax1.set_ylabel('收盘价 (¥)')
ax1.legend(loc='upper left')
ax1.grid(alpha=0.3)
# 下图:成交量 --- 红涨绿跌
colors = [
'#e74c3c' if recent['涨跌幅'].iloc[i] >= 0
else '#2ecc71' for i in range(len(recent))
]
ax2.bar(recent['trade_date'], recent['成交量'],
color=colors, alpha=0.7, width=0.8)
ax2.set_ylabel('成交量 (股)')
ax2.grid(alpha=0.3)
plt.xticks(rotation=30)
plt.tight_layout()
plt.savefig('000001_volume.png', dpi=150, bbox_inches='tight')
plt.close()
📌 A 股习惯:红涨绿跌,别跟美股搞混了。
3. mplfinance 专业 K 线图
手写 K 线太繁琐,直接用 mplfinance ------ 金融图表专用库,一行代码出图。
数据格式转换
mplfinance 要求列名严格为 Date, Open, High, Low, Close, Volume:
python
import mplfinance as mpf
# 准备数据
df_kline = recent[['trade_date', '开盘', '最高', '最低', '收盘', '成交量']].copy()
df_kline.columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']
df_kline.set_index('Date', inplace=True)
# ⚠️ 确保数值类型正确
for col in ['Open', 'High', 'Low', 'Close', 'Volume']:
df_kline[col] = pd.to_numeric(df_kline[col], errors='coerce')
出图
python
mpf.plot(
df_kline,
type='candle', # K线模式
volume=True, # 带成交量
title='000001 平安银行 --- 日线 K 线图',
style='charles', # 配色方案
figsize=(14, 8),
savefig='000001_kline.png'
)
这张图已经达到专业交易软件的水准了。
4. 叠加均线
python
# 方式一:mav 参数(最简单)
mpf.plot(
df_kline,
type='candle',
volume=True,
mav=(5, 20, 60), # 自动计算并叠加
title='000001 --- K线 + MA5/MA20/MA60',
style='charles',
figsize=(14, 8),
savefig='000001_ma.png'
)
方式二:addplot 自定义
需要更精细的控制(颜色、线宽、EMA)时用 addplot:
python
# 计算指标
df_kline['MA5'] = df_kline['Close'].rolling(5).mean()
df_kline['MA20'] = df_kline['Close'].rolling(20).mean()
df_kline['EMA12'] = df_kline['Close'].ewm(span=12, adjust=False).mean()
# 叠加
apds = [
mpf.make_addplot(df_kline['MA5'], color='orange', width=1.0),
mpf.make_addplot(df_kline['MA20'], color='blue', width=1.5),
mpf.make_addplot(df_kline['EMA12'], color='cyan', width=1.0,
linestyle='--'),
]
mpf.plot(df_kline, type='candle', volume=True,
addplot=apds, style='charles',
figsize=(16, 9), savefig='000001_full.png')
5. 内置配色方案速查
| style | 效果 |
|---|---|
charles |
白底 + 红涨绿跌(A 股首选) |
binance |
深色背景(暗色主题) |
yahoo |
仿 Yahoo Finance |
blueskies |
蓝色调清新风 |
nightclouds |
深色 + 柔和色 |
default |
极简默认 |
自定义配色:
python
custom = mpf.make_mpf_style(
base_mpf_style='charles',
marketcolors=mpf.make_marketcolors(
up='red', down='green',
edge='inherit', wick='inherit', volume='inherit'
),
gridcolor='#e0e0e0',
gridstyle='--',
)
6. 完整工具脚本
python
#!/usr/bin/env python3
"""plot_kline.py --- 一键生成三张图:收盘价 / 量价 / K线+均线"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import mplfinance as mpf
import akshare as ak
import pandas as pd
from datetime import timedelta
import os
def plot_all(symbol: str, days: int = 180, out_dir: str = './charts'):
os.makedirs(out_dir, exist_ok=True)
# 1. 拉数据
df = ak.stock_zh_a_hist(symbol=symbol, period='daily')
df['trade_date'] = pd.to_datetime(df['日期'])
df = df.sort_values('trade_date')
cutoff = df['trade_date'].max() - timedelta(days=days)
recent = df[df['trade_date'] >= cutoff].copy()
# 2. 收盘价图
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(recent['trade_date'], recent['收盘'], color='#1f77b4', lw=1.5)
ax.set_title(f'{symbol} 收盘价', fontsize=14)
ax.set_ylabel('¥'); ax.grid(alpha=0.3)
plt.xticks(rotation=30); plt.tight_layout()
plt.savefig(f'{out_dir}/{symbol}_price.png', dpi=150); plt.close()
# 3. 量价图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8),
sharex=True, gridspec_kw={'height_ratios': [3, 1]})
ax1.plot(recent['trade_date'], recent['收盘'], color='#1f77b4', lw=1.5)
ax1.set_title(f'{symbol} 量价', fontsize=14)
ax1.set_ylabel('¥'); ax1.grid(alpha=0.3)
colors = ['#e74c3c' if recent['涨跌幅'].iloc[i] >= 0
else '#2ecc71' for i in range(len(recent))]
ax2.bar(recent['trade_date'], recent['成交量'], color=colors, alpha=0.7)
ax2.set_ylabel('Vol'); ax2.grid(alpha=0.3)
plt.xticks(rotation=30); plt.tight_layout()
plt.savefig(f'{out_dir}/{symbol}_volume.png', dpi=150); plt.close()
# 4. K线 + 均线
kline = recent[['trade_date','开盘','最高','最低','收盘','成交量']].copy()
kline.columns = ['Date','Open','High','Low','Close','Volume']
for c in ['Open','High','Low','Close','Volume']:
kline[c] = pd.to_numeric(kline[c], errors='coerce')
kline.set_index('Date', inplace=True)
mpf.plot(kline, type='candle', volume=True, mav=(5,20,60),
title=f'{symbol} K线+MA', style='charles',
figsize=(14,8), savefig=f'{out_dir}/{symbol}_kline.png')
print(f'✅ {symbol}: 3 张图已保存到 {out_dir}/')
if __name__ == '__main__':
plot_all('000001', days=180)
plot_all('600519', days=180)
bash
python plot_kline.py
# ✅ 000001: 3 张图已保存到 ./charts/
# ✅ 600519: 3 张图已保存到 ./charts/
常见踩坑
| 问题 | 原因 | 解决 |
|---|---|---|
no display name |
没设 Agg 后端 | 加 matplotlib.use('Agg') |
| 中文乱码 | 服务器缺中文字体 | apt install fonts-wqy-microhei |
| K 线只有框没颜色 | mplfinance 版本太旧 | pip install --upgrade mplfinance |
| 成交量数值过大显示科学计数 | 没设置 ticklabel_format |
ax.ticklabel_format(style='plain', axis='y') |