
如果你搜索过“股票走势图的代码”大概率会看到两种情况要么是一段几百行的“收费源码”复制下来根本跑不通要么是“import mplfinance as mpf然后 mpf.plot(df)”这样一行结束你连日期轴为什么显示成一串数字都没弄明白。我的建议是别急着找现成代码先花点时间把 K 线图的底层逻辑搞明白。你会发现走势图根本不需要背代码它长得就像你自己搭出来的积木。这篇文章是“一步一步教你写股票走势图”系列的开篇。整个系列的目标是带大家从零开始用 Python 在自己电脑上写出一张能看、能改、能继续加指标的 K 线图。而这一篇先把最重要的事讲清楚K 线到底是什么画图前该选什么工具以及一个最小可运行示例是怎么写出来的。看完之后哪怕你现在还不懂 matplotlib 的各种细节也能跟着跑出第一张图并且知道下一次打开计算机时应该往哪个方向使劲。1. 为什么要自己画K线而不是继续用行情软件1.1 行情软件很好但它替你做完了所有事打开任何一个炒股软件输入股票代码K 线图、均线、MACD、成交量全都有了界面漂亮响应也快。那为什么还要自己画我当年也有这个疑问直到开始做量化策略回测时才想明白行情软件是“展示工具”不是“分析工具”。它把一切都封装好了你看得到结果却拿不到中间过程。举几个实际场景你就明白了。第一策略回测。你写了一个“双均线交叉买入”的策略跑完回测之后想把买卖点标注在 K 线图上方便检查信号是否合理。行情软件能让你手动画趋势线但不可能按照你策略的每一笔交易自动标点。这时候必须自己控制绘图代码。第二批量出图。假设你要对沪深 300 里每一只股票生成一张包含最新行情和周线的图片用于晨会报告。人工一只一只截图显然不现实写脚本批量出图才是正路。第三样式与交互的定制。你可能想在一张图里同时展示价格、成交量、自定义指标还想在超过某个阈值时自动高亮。行情软件能部分实现但跨工具的自动化就很难受。所以自己画 K 线图核心目的不是“替代行情软件”而是“拥有对图表的完全控制权”。这篇文章就是为此服务的先把地基打好后面才能盖楼。1.2 这个系列解决什么问题哪些人适合跟着学这个系列定位于“从零开始但不止步于跑通代码”。第一篇文章先把概念和最小实现讲清楚后续会逐步加入均线、成交量、常见技术指标、真实数据接入、交互式图表等内容。适合跟着学的有三类人刚接触 Python 的财经专业学生。你可能已经会写 pandas 数据处理但对可视化还不太熟想用代码把课本上那些 K 线形态真正画出来。做量化策略的爱好者或初级工程师。你已经有了策略想法但每次想看信号图都得借用别人的库一旦想改细节就束手无策。需要批量生成行情图做报告的分析师。你不需要多花哨的交互但要快速、准确地出图还要能控制配色和标注。如果你只是想打开炒股软件随便看一眼那确实不需要跟着写代码。但如果你希望图表能为你自己的分析逻辑服务这个系列值得你花时间跟一遍。我把整个系列的内容地图先放在这里方便你判断后面是否值得继续看第一部分本篇K 线基础认知、绘图工具选型、最小可运行代码第二部分加入均线MA5/MA10/MA20学会在同一张图上叠加折线第三部分成交量副图掌握多子图布局与坐标轴对齐第四部分MACD、KDJ 等指标的计算与绘制第五部分真实数据源接入解决复权、停牌、数据顺序等脏数据问题第六部分交互式图表用 plotly/pyecharts 生成可以放进网页的走势图这样设计的原因很简单K 线图是外壳数据是内核指标和分析逻辑才是灵魂。只教画图不教数据等于白学。2. 动手前必须搞懂的东西K线的构成与信息密度2.1 一根K线上有四个价格OHLCK 线也叫蜡烛图起源于日本江户时代的米市后来被引入证券市场成了最主流的价格可视化方式。它看起来只是几根细细的蜡烛但每一根蜡烛都浓缩了四个关键价格业内一般简写为 OHLC。缩写英文全称中文含义OOpen开盘价HHigh最高价LLow最低价CClose收盘价举一个具体的例子。某只股票某一天开盘价是 10.00 元盘中最高冲到 10.50 元盘中最低跌到 9.80 元收盘价是 10.30 元这组信息画成 K 线时会在一个竖直方向上形成两个部分连接开盘价和收盘价的矩形叫“实体”从实体上下两端延伸出去的细线叫“影线”。上面的影线连接最高价下面的影线连接最低价。所以你看K 线不是简简单单一根线它是一种用极低成本打包四个价格信息的方式。和人聊天时你说“今天涨了”这只是收盘价对比前天的一个结论但如果你说“今天开盘 10 块最高 10.5最低 9.8收在 10.3”对方就能感受到今天在盘面上发生了什么冲高回落下方也有支撑波动明显。这也是为什么几乎所有行情软件都默认用 K 线图而不是折线图。折线图能展示收盘价变化但丢掉了开盘、最高、最低这三个同样重要的信息。我们后面写代码时也会刻意保留这些信息。2.2 阳线、阴线、影线K线在表达市场情绪在 A 股市场我们习惯用红绿区分涨跌收盘价高于开盘价叫阳线实体通常显示为红色收盘价低于开盘价叫阴线实体通常显示为绿色。但在欧美市场这个颜色习惯刚好反过来收盘价高于开盘价反而用绿色。这个差异别小看很多人第一次用国外开源库画 A 股 K 线时都会被颜色搞懵。除了颜色影线的含义也需要理解。上影线长说明盘中曾经冲得很高但后来价格被打下来了上方存在抛压。下影线长说明盘中曾经跌得很深但后来被买盘抬起来了下方存在承接。实体大说明开盘和收盘之间差距大多空双方中有一方占据明显优势。实体小甚至接近十字星说明开盘和收盘几乎一样多空力量暂时均衡。需要注意的是这些形态信息只是“市场情绪的概率表达”不是确定的涨跌预言。上影线长并不代表第二天一定跌它只是提示你“这个价位有压力”。K 线形态学最大的价值是提供一种观察市场的语言而不是给你必胜的入场信号。2.3 时间周期与K线组合日K、周K、分钟K的差异行情软件里你可以自由切换日 K、周 K、月 K、60 分钟 K、5 分钟 K。它们的本质逻辑是一样的把对应周期内的 OHLC 数据抽出来画成一根 K 线。很多人有一个误区以为周 K 是把这一周的五根日 K 简单拼起来。其实不是。周 K 的生成规则是开盘价取这周一的第一笔开盘价收盘价取这周五的最后一笔收盘价最高价取这周内所有交易日的最高价最低价取这周内所有交易日的最低价。月 K、年 K 同理。后面我们写代码时会专门用 pandas 的 resample 功能来实现日 K 到周 K 的聚合。现在只需要记住这个原则不同周期的 K 线描述的是不同粒度上的价格博弈。日 K 的形态更敏感周 K 更平滑长周期的 K 线包含的信息量更大但反馈更慢。这一篇先不深入讨论 K 线组合形态比如“红三兵”“黄昏之星”这些那是后面结合具体代码再展开的事。但有了 OHLC 和实体影线的概念你已经能读懂任何一张 K 线图了。3. 画K线之前的技术选型四种常用方案怎么挑3.1 主流绘图库横向对比用 Python 画 K 线绕不开几个主流选择。我把它们的核心特点整理成一张表方便你根据自己的需求对号入座。方案上手难度交互性二次开发自由度典型场景mplfinance较低无中等快速出图、检查数据matplotlib 手动绘制中等无高深度定制、嵌入回测系统plotly中等强较高网页交互式图表pyecharts较低强中等中文文档友好、生成 HTML 图表mplfinance 是 matplotlib 生态下的专用 K 线图库底层是 matplotlib但封装了“画蜡烛”“加均线”“画成交量副图”这些高频操作。如果你只是想快速看到一只股票的 K 线它确实是最省事的。plotly 的优点是交互性鼠标悬停能看到每根 K 线的 OHLC 详情还可以缩放、拖拽非常适合做网页端数据分析工具。缺点是它和常见指标计算库的整合需要自己写更多代码渲染速度在大数据量下也相对慢一些。pyecharts 生成的图表非常漂亮而且中文资料多但如果你需要把 K 线图嵌入到自己的桌面端分析流程里或者和 matplotlib 的绘图体系打通它就没那么顺手了。3.2 我为什么选择先用matplotlib打底这个系列里我会用 matplotlib 手动绘制 K 线作为主线而不是直接推荐 mplfinance。原因是第一原理透明。用 vlines 画影线、用矩形画实体亲手搭一根蜡烛出来之后你对 K 线的理解会完全不同。第二自由度高。后续要叠加自己的策略买卖点、自定义指标、特殊标记时matplotlib 这种基础库能给你最大的操作空间。第三回测系统集成方便。很多本地量化框架默认就依赖 matplotlib学会手动绘图后进入回测系统基本没有学习成本。当然这不代表我们要排斥 mplfinance。在后面的实战篇里我会把它当作“快速验证工具”来讲两者搭配使用才是最高效的。3.3 环境准备安装什么数据从哪来在开始写代码之前你需要先安装基础依赖。打开终端或命令行执行pip install pandas matplotlibpandas 负责数据处理matplotlib 负责绘图。后面如果要用 mplfinance 做对比再单独安装pip install mplfinance关于数据来源我强烈建议第一阶段不要碰真实数据接口。很多新手一上来就想拉某只股票的历史行情结果被 token 配置、接口限流、数据格式清洗搞得头大反而忽略了绘图本身。更好的做法是先用一段随机生成的假数据把 K 线图的绘制逻辑跑通。等后面专门讲数据源时再接入真实行情。4. 第一个能跑的K线图最小示例拆解4.1 准备一份“假数据”避开数据源烦恼为了演示 K 线绘制逻辑我们先构造 20 个交易日的假数据。这里我特意用随机游走的方式生成收盘价再围绕收盘价生成开盘价、最高价和最低价保证数据看起来像那么回事。import numpy as np import pandas as pd import matplotlib.pyplot as plt # 固定随机种子保证每次运行结果一致 np.random.seed(42) n 20 dates pd.bdate_range(2024-01-02, periodsn) # 随机游走生成收盘价 close np.cumsum(np.random.randn(n)) 100 # 开盘价在收盘价附近波动 open_ close np.random.randn(n) * 0.5 # 最高价 max(开盘, 收盘) high np.maximum(open_, close) np.abs(np.random.randn(n)) * 0.8 # 最低价 min(开盘, 收盘) low np.minimum(open_, close) - np.abs(np.random.randn(n)) * 0.8 df pd.DataFrame({ Open: open_, High: high, Low: low, Close: close, }, indexdates) print(df.head())这里有一个细节high必须大于等于open和close的较大值low必须小于等于两者的较小值。否则画出来的 K 线实体就会跑到影子外面去不符合真实行情的数据结构。用np.maximum和np.minimum处理一下能避免这种逻辑错误。4.2 核心绘图代码逐行解释拿到 DataFrame 之后就可以画图了。手动绘制 K 线本质只有两步用竖线画最高价到最低价的影线再用矩形画开盘价到收盘价的实体。我用vlines画影线用bar画实体示例代码如下fig, ax plt.subplots(figsize(12, 6)) for idx, date in enumerate(df.index): o df.loc[date, Open] h df.loc[date, High] l df.loc[date, Low] c df.loc[date, Close] # A股习惯红涨绿跌 color red if c o else green # 1. 画最高价到最低价的影线 ax.vlines(idx, l, h, colorcolor, linewidth1) # 2. 画开盘价到收盘价的实体 if c o: # 阳线实体底部是开盘价高度是收盘价-开盘价 ax.bar(idx, heightc - o, bottomo, width0.6, colorcolor, alpha0.85) else: # 阴线实体底部是收盘价高度是开盘价-收盘价 ax.bar(idx, heighto - c, bottomc, width0.6, colorcolor, alpha0.85) # 用整数位置作为刻度把日期字符串作为标签 ax.set_xticks(range(len(df))) ax.set_xticklabels(df.index.strftime(%Y-%m-%d), rotation45, haright) ax.set_title(Demo K-Line Chart) ax.grid(axisy, linestyle--, alpha0.5) plt.tight_layout() plt.show()我逐行讲一下为什么这么写。第一个关键点是for idx, date in enumerate(df.index)。这里我故意用整数idx作为 x 轴位置而不是直接把日期传给 matplotlib。原因后面会细讲简单说就是整数位置能让你精确控制 K 线的宽度、间距而且和后续叠加均线、指标时更容易对齐。第二个关键点是阳线和阴线实体高度的处理。ax.bar(idx, heightc-o, bottomo)表示以idx为中心在 y 轴从bottom开始向上画一个高度为height的柱子。阳线时c o实体从开盘价向上延伸到收盘价所以高度是c - o底部是o。阴线时如果直接写成height c - o高度就是负数虽然 bar 也能向下画但容易和后面的逻辑混淆。所以我统一用正数高度并把底部设成收盘价c这样实体就是从收盘价向上延伸到开盘价。第三个关键点是width0.6。它控制实体柱子的横向宽度。如果宽度设为 1相邻两根 K 线会紧紧贴在一起看起来像一条实心带没有间隙。设成 0.6 后每根 K 线之间会留出一点空隙视觉上更接近行情软件的效果。第四个关键点是刻度标签。因为 x 轴用的是整数位置所以必须手动把 0、1、2 这些数字替换成真实的日期。df.index.strftime(%Y-%m-%d)会把日期格式化成“2024-01-02”这样的字符串配合rotation45防止标签重叠。4.3 跑起来之后先做这3个小优化上面的代码能跑但默认字体可能显示不了中文图表标题如果包含中文会变成乱码。建议在文件开头加上这两行plt.rcParams[font.sans-serif] [SimHei] # 或用你系统里的中文字体 plt.rcParams[axes.unicode_minus] False # 解决负号显示成方块的问题然后可以调整图片尺寸和清晰度fig, ax plt.subplots(figsize(12, 6), dpi100)最后是加一个副标题把数据范围标注清楚方便日后复盘ax.set_title(fK线图 {df.index[0].date()} 至 {df.index[-1].date()})别小看这几个小优化。实际工作中一张没有标题、没有日期范围的 K 线图事后翻看时很难快速定位是哪只股票、哪段时间的数据。5. 第一次画K线最容易踩的坑日期轴与数据顺序5.1 日期轴为什么会变成一串数字很多人在网上搜“股票走势图代码”拿到的版本可能是直接plt.plot(df.index, df[Close])这样画的。跑完后发现 x 轴刻度变成了 18000 多这种“天文数字”完全看不懂。这是 matplotlib 处理日期的经典问题。matplotlib 内部会把 datetime 对象转换成一个浮点数单位是“天”基准是 1970-01-01。直接传给绘图函数时它默认不会把它格式化成年月日所以显示出一串数字。我在这篇文章的示例里绕开了这个坑x 轴统一用整数idx表示“第几个交易日”刻度标签再用日期字符串替换。这样做的另一个好处是K 线图中每个交易日的间距完全一致不会因为周末或节假日导致某些位置被拉宽。如果你坚持要用真实日期作为 x 轴数据也不是不行但需要额外引入matplotlib.dates里的DateFormatter和定位器代码会复杂不少。我的建议是做本地分析时优先用“整数位置 日期标签”的方式简单、可控。5.2 非交易日留白两种模式的选择继续说日期轴的问题。股票市场每周只有周一至周五交易周六日是不产生 K 线的。如果某个周一直接跟着上一个周五中间没有交易数据绘图时该怎么办这里存在两种模式。模式 A连续模式。把每个交易日都当作 x 轴上的等距位置忽略周末和节假日。上面示例代码用的就是这种模式。它的优点是画出来的 K 线疏密均匀适用于策略回测和指标计算因为策略关心的是“第几个交易日”而不是“日历上的星期几”。模式 B自然日模式。x 轴按照真实日期排列周六日和节假日会留下空白。这种模式更接近行情软件里的视觉效果适合人工看盘因为你能直接感受到周与周之间的时间间隙。mplfinance 默认使用的是连续模式这也是它能用一行代码画出漂亮 K 线的原因之一。如果想用 matplotlib 实现自然日模式需要把 x 轴数据改成mdates.date2num(df.index)并且 bar 的宽度也要改成以“天”为单位的数值同时配置DateFormatter。这个内容比较复杂我把它放到后面讲交互式图表时再展开。对于本篇文章的初学者先掌握连续模式就够了。5.3 数据顺序最新的K线跑到左边去了这是我最开始自学画 K 线时踩过最大的坑没有之一。很多免费数据接口或者 CSV 文件数据排列是“最新日期在第一行历史日期在后续行”。如果你拿到手之后不检查直接for idx, date in enumerate(df.index)循环绘图画出来的 K 线会是最新的在最左边时间越久越靠右整个走势完全镜像反了。更危险的是这种错误有时候一眼看不出来因为单看 K 线的涨跌形态可能只是觉得“怪怪的”但说不清哪里不对。等到你用这个数据去算均线、算 MACD才发现指标全乱套。所以拿到任何数据集的第一件事永远是看头尾print(df.head()) print(df.tail())然后确认日期顺序是升序还是降序。如果降序执行df.sort_index(ascendingTrue, inplaceTrue)如果是标准 DataFrame 但索引不是时间类型先转换一下df.index pd.to_datetime(df.index)养成这个习惯之后很多后续问题都能提前规避。5.4 红绿不分家颜色习惯带来的小困惑再回到颜色问题。国内行情软件普遍是红涨绿跌但 matplotlib、mplfinance 这类国外开源项目默认往往是绿涨红跌。用 mplfinance 画 A 股 K 线时如果你不设置参数第一次看到的颜色会非常别扭。解决办法是显式指定mpf.plot(df, typecandle, upred, downgreen)但在我们手动绘图的示例里颜色只需要用一行逻辑控制color red if c o else green这里有一个容易忽略的边界情况当收盘价等于开盘价时实体高度为 0几乎看不见K 线会变成一根十字线。我习惯把它归为阳线处理也就是c o实体颜色为红色。这只是一种约定不会对数据产生实际影响但需要在团队协作时保持一致。5.5 别忘了复权跳空缺口是真是假还有一个新手最容易忽略但影响巨大的问题复权。某只股票今天除权除息比如 10 送 10股价第二天从 20 元直接变成 10 元。如果你用“不复权”的数据画 K 线会看到一个巨大的向下跳空缺口看起来像暴跌一样。但实际上股东的总资产并没有变化。真实行情数据一般有三种状态状态含义不复权真实成交价格不处理分红送股前复权以最新价格为准调整历史价格后复权以上市首日为准调整后续价格做技术分析时大多数人使用前复权数据因为这样历史价格会被连续化K 线形态更平滑。但在做除权事件统计时又需要用不复权数据。这个知识点在数据接入篇会重点讲。现在你只需要知道同一个股票不同复权方式画出来的 K 线图形状可能完全不同。如果发现自己的图和历史行情软件对不上先检查数据源默认是哪种复权方式。6. 后续系列的内容地图从单根K线到完整走势图6.1 概述篇之后第二篇会做什么这一篇只写了 20 根假数据 K 线还远远不够。第二篇文章我会在这个基础上加入最常用的移动平均线MA5、MA10、MA20并解决一个核心问题如何在 K 线图上叠加折线时保证两条曲线的坐标轴完全对齐。为什么特意强调对齐因为 K 线用的是整数位置作为 x 轴而均线计算后对应的也是同一组交易日序列。只要把整数索引沿用到均线数据上叠加就变得非常简单。如果一开始就混用真实日期和各种复杂定位后续加指标会越写越乱。第二篇还会处理一个细节MA5 是从第 5 个交易日才开始有值前 4 天本质上是 NaN。直接绘图时matplotlib 遇到 NaN 会产生断线怎么处理这个断点也是很多教程没有讲清楚的地方。6.2 再往后成交量、MACD与交互式图表等均线这块稳定之后系列会进入副图阶段。第一步是成交量图这个相对简单本质就是在主图下方再用一个子图画一组和 K 线颜色一致的柱子。但要注意成交量的涨跌颜色应该和当天 K 线保持一致否则容易产生视觉误导。然后是 MACD 指标。MACD 的计算涉及 EMA 指数移动平均、快线慢线、柱状图和高低点标记需要用到 pandas 的ewm方法。这里面的难点不只是计算而是指标数据画在副图后如何让主图 K 线和副图指标的日期刻度一一对应。再往后就是让图表动起来。计划用 plotly 做一版交互式 K 线图鼠标悬停可以看到当天的 OHLC 和成交量还可以拖动滚动条查看不同时间段。这不仅是装点门面在做数据探索时确实能提高效率。6.3 一个立刻能做的小练习把日K改成周K最后给你留一个练习这也是我自己当年学画 K 线时觉得收获最大的练习把日 K 数据聚合成周 K 数据然后用同一个绘图函数画出来。思路很简单。数据已经有了关键是使用 pandas 的resampleweekly df.resample(W).agg({ Open: first, High: max, Low: min, Close: last })这里每一列聚合方式都不一样W表示按周聚合。得到周 K 数据后直接复用前面写好的绘图逻辑就能画出一张周 K 线图。动手做这个练习时你会直观感受到几点。第一周 K 的数量比日 K 少很多图变得更简洁。第二周 K 的实体往往比日 K 大因为一周的价格波动范围通常大于单日。第三万一你的resample结果不对大概率是日期索引类型问题这时候回头检查一下df.index是不是 datetime 类型。我个人的经验是第一次手绘 K 线时数据顺序就是反的我盯着“从右往左不断上涨”的形态看了半天还以为这是某种镜像指标。后来养成了拿到任何数据集先打印 head 和 tail 的习惯才彻底告别这类低级错误。画 K 线这件事技术点并不难难的是把数据本身搞干净。这个系列后面会反复强调同一句话先确认数据的顺序、频率和复权状态再谈画图。如果你跟着这篇文章跑通了最小示例你已经超过了绝大多数在收藏夹里吃灰的“K 线图代码学习者”。接下来要做的不是急着找新教程而是把上面那个周 K 练习做出来。改一改聚合周期改一改颜色把这段代码变成你自己的工具。下一篇文章我们直接在它的基础上加均线。