Skip to content

用Python画K线图

K线图(Candlestick Chart)是股票技术分析中最基础也最重要的图表类型。每一根K线记录了一个交易周期(如一天)内的四个关键价格:开盘价(Open)最高价(High)最低价(Low)收盘价(Close),合称 OHLC 数据。

通过 Python,我们可以用代码自动绘制专业的K线图,而无需依赖券商软件。这不仅灵活性更高,还能将K线图嵌入到量化策略的回测报告和研究成果中。

环境准备

在开始之前,请确保安装了以下核心库:

bash
pip install mplfinance matplotlib pandas akshare

各库的作用说明:

库名作用
mplfinance专门绘制金融图表(K线、成交量等)的 matplotlib 扩展
matplotlibPython 最基础的绘图库,用于高级自定义
pandas数据处理框架,几乎所有金融数据都以 DataFrame 形式操作
akshare免费 A 股数据获取库,作为本文的数据源

获取股票数据

我们以获取 A 股数据为例,使用 akshare 获取日线数据:

python
import akshare as ak
import pandas as pd

# 获取贵州茅台(600519)近120个交易日的日K数据
df = ak.stock_zh_a_hist(
    symbol="600519",
    period="daily",
    start_date="20240101",
    end_date="20241231",
    adjust="qfq"  # 前复权,保证K线连续性
)

# akshare 返回的列名为中文,需要重命名为 mplfinance 要求的格式
df = df.rename(columns={
    "日期": "Date",
    "开盘": "Open",
    "收盘": "Close",
    "最高": "High",
    "最低": "Low",
    "成交量": "Volume",
    "成交额": "Turnover"
})

# 将 Date 列设为索引,并转换为日期类型
df["Date"] = pd.to_datetime(df["Date"])
df = df.set_index("Date")

# 只保留需要的列
df = df[["Open", "High", "Low", "Close", "Volume"]]

print(df.head())

前复权(qfq):以最新价格为基准,向前调整历史价格。复权后的K线能真实反映持股期间的收益情况,不会有因除权除息导致的跳空缺口。

使用 mplfinance 画基础K线图

mplfinance 是 matplotlib 专门为金融数据设计的扩展包,原名 mpl_finance,后独立维护。它能用极少的代码画出专业级K线图。

最简K线图

python
import mplfinance as mpf

# 一行代码画出K线图
mpf.plot(df, type="candle", title="贵州茅台日K线")

type 参数支持的图表类型:

  • "candle" — K线图(蜡烛图)
  • "ohlc" — OHLC 线条图
  • "line" — 折线图(通常只显示收盘价)

添加成交量柱状图

成交量是K线分析的重要辅助信息。通过 volume 参数可以在K线下方叠加成交量柱:

python
mpf.plot(
    df,
    type="candle",
    volume=True,           # 显示成交量
    title="贵州茅台 - K线与成交量",
    ylabel="价格(元)",
    ylabel_lower="成交量"
)

自定义颜色和样式

A股的习惯配色与美股不同:A股中红色代表上涨(收盘价 > 开盘价),绿色代表下跌(收盘价 < 开盘价),而美股恰好相反。mplfinance 默认使用美股配色,我们需要自定义。

使用 style 预设

mplfinance 内置了多种样式,常用的有:

python
# 经典黑白背景
mpf.plot(df, type="candle", style="classic")

# 查尔斯·道风格(黑白K线)
mpf.plot(df, type="candle", style="charles")

# 白色背景蓝色主题
mpf.plot(df, type="candle", style="yahoo")

自定义 A 股风格(红涨绿跌)

python
# 定义自定义样式:红涨绿跌,白色背景
my_style = mpf.make_mpf_style(
    base_mpf_style="yahoo",
    marketcolors=mpf.make_marketcolors(
        up="red",        # 上涨:红色
        down="green",    # 下跌:绿色
        edge="inherit",  # K线边框颜色跟随涨跌
        wick="inherit",  # 影线颜色跟随涨跌
        volume="inherit" # 成交量柱颜色跟随涨跌
    ),
    gridstyle=":",
    gridcolor="#e0e0e0"
)

mpf.plot(
    df,
    type="candle",
    volume=True,
    style=my_style,
    title="贵州茅台 - A股风格K线图"
)

这样输出的K线图就完全符合A股投资者的视觉习惯了。

叠加移动均线

移动均线(Moving Average,简称均线或 MA)是最常用的技术指标之一。它通过计算一定周期内的平均收盘价来平滑价格波动,帮助判断趋势方向。

mplfinance 中添加均线非常简单,只需通过 mav 参数指定均线的周期:

python
mpf.plot(
    df,
    type="candle",
    volume=True,
    style=my_style,
    mav=(5, 10, 20),  # 添加5日、10日、20日均线
    title="贵州茅台 - K线 + 均线 + 成交量"
)

mav 参数接受一个元组,每个元素代表一条均线的计算周期。mplfinance 会自动计算并在图上绘制,还会在左上角显示图例。

手动计算均线

有时我们需要手动计算均线(比如要用在策略中),可以用 pandas 的滚动计算:

python
# 计算5日、10日、20日简单移动均线
df["MA5"] = df["Close"].rolling(window=5).mean()
df["MA10"] = df["Close"].rolling(window=10).mean()
df["MA20"] = df["Close"].rolling(window=20).mean()

print(df[["Close", "MA5", "MA10", "MA20"]].tail(10))

简单移动均线(SMA):将最近 N 个交易日的收盘价求算术平均。例如 MA5 = (C1 + C2 + C3 + C4 + C5) / 5。

matplotlib 高级用法:多子图布局

mplfinance 适合快速出图,但当我们需要更灵活的布局(比如多个子图、标注买卖点)时,需要回到 matplotlib 的底层 API。

下面展示如何用 matplotlib 手动绘制包含K线、成交量、MACD 三个子图的高级图表:

准备K线绘制数据

python
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.patches import Rectangle
import numpy as np

# 为了在 matplotlib 中绘制K线,我们需要将日期转为数字
dates = mdates.date2num(df.index.to_pydatetime())
opens = df["Open"].values
highs = df["High"].values
lows = df["Low"].values
closes = df["Close"].values
volumes = df["Volume"].values

手动绘制K线图

python
fig, axes = plt.subplots(
    3, 1,
    figsize=(14, 10),
    height_ratios=[3, 1, 1],  # K线区:成交量:MACD = 3:1:1
    sharex=True  # 共享X轴
)
ax_kline = axes[0]
ax_vol = axes[1]
ax_macd = axes[2]

# 设置中文字体
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

# 绘制K线(矩形实体 + 影线)
bar_width = 0.6
for i in range(len(dates)):
    color = "red" if closes[i] >= opens[i] else "green"

    # 影线(最高到最低的竖线)
    ax_kline.plot(
        [dates[i], dates[i]],
        [lows[i], highs[i]],
        color=color,
        linewidth=0.8
    )

    # 实体(开盘到收盘的矩形)
    body_bottom = min(opens[i], closes[i])
    body_height = abs(closes[i] - opens[i])
    rect = Rectangle(
        (dates[i] - bar_width / 2, body_bottom),
        bar_width, body_height,
        facecolor=color,
        edgecolor=color
    )
    ax_kline.add_patch(rect)

# 叠加均线
ma5 = df["Close"].rolling(5).mean()
ma20 = df["Close"].rolling(20).mean()
ax_kline.plot(dates, ma5, color="blue", linewidth=1, label="MA5")
ax_kline.plot(dates, ma20, color="orange", linewidth=1, label="MA20")

ax_kline.set_title("贵州茅台 - 多子图K线分析", fontsize=14)
ax_kline.legend(loc="upper left")
ax_kline.grid(True, alpha=0.3)

# 绘制成交量柱
for i in range(len(dates)):
    color = "red" if closes[i] >= opens[i] else "green"
    ax_vol.bar(dates[i], volumes[i], width=bar_width, color=color, alpha=0.7)

ax_vol.set_ylabel("成交量")
ax_vol.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

标注买卖点

在实际策略回测中,我们需要在K线图上标注买入和卖出的信号位置。matplotlib 提供了 annotate 方法来实现这一功能:

python
# 模拟买卖信号(这里用均线金叉/死叉作为示例)
signals = pd.DataFrame(index=df.index)
signals["MA5"] = df["Close"].rolling(5).mean()
signals["MA20"] = df["Close"].rolling(20).mean()

# 金叉买入信号:MA5 从下方穿越 MA20
signals["buy"] = (
    (signals["MA5"].shift(1) < signals["MA20"].shift(1)) &
    (signals["MA5"] > signals["MA20"])
)

# 死叉卖出信号:MA5 从上方穿越 MA20
signals["sell"] = (
    (signals["MA5"].shift(1) > signals["MA20"].shift(1)) &
    (signals["MA5"] < signals["MA20"])
)

# 在K线图上标注买卖点
buy_dates = signals[signals["buy"]].index
sell_dates = signals[signals["sell"]].index

for bd in buy_dates:
    idx = df.index.get_loc(bd)
    ax_kline.annotate(
        "买",
        xy=(dates[idx], lows[idx]),
        xytext=(dates[idx], lows[idx] * 0.96),
        fontsize=10,
        fontweight="bold",
        color="red",
        arrowprops=dict(arrowstyle="->", color="red", lw=1.5),
        ha="center"
    )

for sd in sell_dates:
    idx = df.index.get_loc(sd)
    ax_kline.annotate(
        "卖",
        xy=(dates[idx], highs[idx]),
        xytext=(dates[idx], highs[idx] * 1.04),
        fontsize=10,
        fontweight="bold",
        color="green",
        arrowprops=dict(arrowstyle="->", color="green", lw=1.5),
        ha="center"
    )

金叉:短期均线从下方穿越长期均线,通常被视为买入信号。死叉:短期均线从上方穿越长期均线,通常被视为卖出信号。

添加 MACD 子图

在第三个子图中绘制 MACD 指标(关于 MACD 的详细计算,请参考下一篇《用Python计算技术指标》):

python
# 计算 MACD
ema12 = df["Close"].ewm(span=12, adjust=False).mean()
ema26 = df["Close"].ewm(span=26, adjust=False).mean()
dif = ema12 - ema26
dea = dif.ewm(span=9, adjust=False).mean()
macd_bar = (dif - dea) * 2

# 绘制 MACD 柱状图
for i in range(len(dates)):
    color = "red" if macd_bar.iloc[i] >= 0 else "green"
    ax_macd.bar(dates[i], macd_bar.iloc[i], width=bar_width, color=color, alpha=0.7)

ax_macd.plot(dates, dif, color="blue", linewidth=1, label="DIF")
ax_macd.plot(dates, dea, color="orange", linewidth=1, label="DEA")
ax_macd.axhline(y=0, color="gray", linestyle="--", linewidth=0.5)
ax_macd.set_ylabel("MACD")
ax_macd.legend(loc="upper left")
ax_macd.grid(True, alpha=0.3)

# 格式化X轴日期显示
ax_macd.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m"))
ax_macd.xaxis.set_major_locator(mdates.MonthLocator())
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

保存图表到文件

在自动化分析流程中,我们通常需要将图表保存为图片文件,而不是弹窗显示:

python
# mplfinance 保存图片
mpf.plot(
    df,
    type="candle",
    volume=True,
    style=my_style,
    mav=(5, 20),
    title="贵州茅台",
    savefig="kline_maotai.png",
    figsize=(14, 8)
)

# matplotlib 保存图片
fig.savefig("kline_analysis.png", dpi=150, bbox_inches="tight")

支持的格式包括 PNG、JPG、SVG、PDF 等。dpi 参数控制图片分辨率(默认 100,印刷建议 300),bbox_inches="tight" 可以避免图表边缘被裁切。

常见问题与解决方案

中文字体显示为方块

matplotlib 默认不支持中文,需要手动设置字体:

python
# Windows 系统
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 黑体

# macOS 系统
# plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]

# Linux 系统(需要先安装字体)
# plt.rcParams["font.sans-serif"] = ["WenQuanYi Micro Hei"]

plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题

图表显示空白窗口

在脚本中使用 matplotlib 时,确保 plt.show() 在所有绑制代码之后调用。如果在 Jupyter Notebook 中使用,可以在开头加上魔法命令:

python
%matplotlib inline  # 在Notebook中直接显示图片

日期轴过于密集

当数据量较大时,X 轴的日期标签可能重叠。可以通过以下方式调整:

python
# 方法1:自动旋转日期标签
fig.autofmt_xdate()

# 方法2:手动设置日期间隔
ax.xaxis.set_major_locator(mdates.WeekdayLocator(interval=2))  # 每2周显示一个标签

小结

本文介绍了用 Python 绘制K线图的两种主要方式:

  1. mplfinance 快速绑制:适合快速出图,几行代码即可得到带均线和成交量的专业K线图。
  2. matplotlib 手动绘制:适合高级场景,可以自由控制子图布局、标注买卖信号、叠加自定义指标。

掌握K线图绘制是量化分析可视化的第一步。在后续的文章中,我们将学习如何计算技术指标、构建交易策略,并将这些可视化技巧整合到策略回测的完整流程中。

示例:K线图示例

11.210.910.610.410.101-0201-0301-0401-0501-0801-0901-1001-1101-1201-15

红色蜡烛表示上涨(收盘>开盘),绿色蜡烛表示下跌(收盘<开盘)

免责声明:本文所有代码仅用于技术教学目的,文中涉及的股票代码仅为数据示例,不构成任何投资建议。投资有风险,入市需谨慎。

仅供学习交流,不构成任何投资建议