本章涵盖的内容(This chapter covers)
- 解读图表中的形态
- 如何使用蜡烛图(K 线)
- 哪些均线有助于评估股票
- 布林带、MACD 与均线带(moving average ribbons)
- 显示一目均衡云(Ichimoku Cloud)
- 使用 Streamlit 进行可视化
想象有人向你展示一个极具吸引力的产品——也许是你从未考虑过、但现在被深深吸引的东西。可能是一件惊艳的艺术品、一处奢华房产、或一辆精致座驾。此时卖家目光灼灼地问你:“你愿意出价多少?”
再假设你从未买过类似的东西。身边没有顾问可问,也无法上网或问 AI 聊天机器人一个“合理价格”。缺乏任何历史数据或参照,你所能做的只有猜。
许多潜在买家会对此感到焦虑:出价过高,可能多年后悔;出价太低,也会有问题——买家可能冒犯卖家,或者在旁人眼中暴露自己不了解市场价值,损伤名声。
历史数据是做出明智决策的坚实基础。比如,若你知道邻近房屋的成交价,至少能对房地产价格做出估算(尽管仍可能因种种原因买得不划算)。数据越多,越容易判断价格是否合理。
技术分析在很大程度上就是对历史价格走势的研究。了解过去的波动是好事;而把历史价格可视化则更好——许多人比起表格数字更偏爱图形。我们先从图表与其解读开始,随后看一些用于投资的特定图形,并学习如何自己做可视化。
10.1 图表(Charts)
在前几章里,我们常用股价随时间绘图。因此,“图表用于表现数据”并非新概念。一旦有了价格与时间,就能画出以 x 轴为时间、y 轴为资产以某种货币计价的数值的图。先看一张无上下文的图(图 10.1)。
图 10.1 一张没有上下文的图表,我们还需要自己解释很多信息
面对一张无上下文的图,我们也能做些假设:若知 x 轴为时间、y 轴为价格,可以看出某资产在图前半段相对稳定,随后发生了某事,价格急挫,期间有些小幅反弹。没有上下文我们只能做基本观察;要理解“发生了什么”,还需要更多细节。让我们加入上下文,看看图 10.2。
图 10.2 解放日(2025 年 4 月 2 日)后标普 500(代码:^GSPC)的价格走势;当天特朗普总统宣布对美国关税策略作出根本性调整
有了上下文,图表就有了意义。我们知道看的是标普 500,且区间很短(几天),尽管标普 500 自 1957 年 3 月 4 日起就有数据。
如果把图中数据放到一个 DataFrame,可得从 4 月 1 日约 5600 美元起步,到收在 5000 美元下方的时间序列。我们不知道有多少数据点,这取决于更新频率——若按分钟更新,数据量将是按小时更新的 60 倍。
标普 500 的价格下跌了 11.15% 。若某人在跟踪标普 500 的指数基金中持有 11,150。对期望资产稳步增值的保守型投资者而言,这张图令人不安。但我们再看一张图(图 10.3)。
图 10.3 GameStop 的极端价格走势常被视作高波动股的案例。作为一家电子游戏零售商,GameStop 的股价因 subreddit r/wallstreetbets 的“被低估”讨论而暴涨,导致做空该股的对冲基金蒙受巨额亏损。
对习惯了 GameStop 这类波动的人来说,标普 500 的变动就没那么“惊心动魄”。毕竟,指数的 11% 回撤并不意味着价值永远消失。除非全球经济发生剧变,指数价格通常会回升并延续增长。然而,把指数与一家处境艰难公司的股价表现相提并论并不恰当。为了理解短期内跌超 10%的意义,我们对照历史上的股灾。表 10.1 显示,特朗普宣布对外来商品大幅加征关税后的损失幅度,与雷曼倒闭后的跌幅相当。
表 10.1 近几十年五次最显著的单日回撤
| 日名 | 日期 | % 变化 |
|---|---|---|
| 黑色星期一 | 1987-10-19 | –24.57 |
| 黑色星期二 | 1987-10-20 | –16.23 |
| 新冠疫情 | 2020-03-12 | –13.93 |
| 雷曼兄弟倒闭 | 2008-11-20 | –12.43 |
| 美国关税公告 | 2025-04-04 | –10.53 |
我们也可以把视角拉远,考察更长时期的意义。图 10.4 给出了自 1996 年 11 月起的标普 500 数据。
图 10.4 标普指数自 1996-11 至 1996-04-04 的走势展示了多个危机:互联网泡沫破裂(2000 年初)、次贷危机(2008)、新冠疫情(2020)、俄乌战争爆发(2022)以及美国宣布关税(2025)
图中能看到多次回撤:互联网泡沫、雷曼破产、新冠、俄乌开战。“解放日”的后效也会在图上长期留痕。不过,即便经历了显著下跌,2025 年 4 月的标普 500 仍高于 2024 年初。
接下来看个股。图 10.5 展示了 NVIDIA 的股价演变。
图 10.5 NVIDIA 五年股价上涨 1787.89% ,在股市历史上相当罕见
个股通常比指数更波动。若有人在 2020 年以相当于 120,堪称投资者的“美梦成真”。
NOTE 2020 年以来,NVIDIA 发生过两次股票拆分。如果你在 2020 年 1 月以 $266 买入 1 股,后来会变成 40 股。
科技公司往往追求高速增长,高度依赖技术领先战胜对手。因此它们通常拥有更高的市盈率(P/E)与更低的分红(大量利润再投入研发)。一旦被对手超越、且自身盈利增长无法持续超越同业,科技股就可能变得风险较高。
图 10.6 概述了 NVIDIA 的营收。随着公司扩大营收,投资者对其持续增长的前景更为乐观。即便今天 NVIDIA 的 P/E 高于部分同行,若考虑未来潜在增长,它仍可能是一个良好资产。
图 10.6 NVIDIA 的年度营收(含 2026、2027 年在 2025 年 1 月披露的估算)
在第 7 章的组合分析中,我们基于历史回报得出:应在组合中给 Pfizer 与 NVIDIA 更高权重。图 10.7 显示了与 NVIDIA 完全不同的另一面。
图 10.7 若在五年前买入辉瑞(Pfizer),其股价没有带来资本增值——这意味着“低买高卖”并不总能奏效
如果 2020 年买入辉瑞,如今账面价值更低。不过辉瑞是一只高分红股票,期间的年度分红能在一定程度上弥补资本增值的缺失。
辉瑞的图表映照着 COVID-19 的历史,这也体现在其盈利上(见图 10.8)。
图 10.8 辉瑞在新冠疫情期间盈利暴增,疫情退去后盈利回落
盈利对任何公司都至关重要,但并非所有行业都能持续提高盈利。辉瑞的投资者需要接受:疫情峰值时期的盈利不可能长期维持。不过也可期待辉瑞继续创造足够营收以支付可观分红并稳步增长。
盈利与股价可能相关:好年景往往反映在股价上——投资者在财报会上看到“炸裂”业绩便买入;若结果令人失望就卖出。技术分析同样关注股价,但侧重点不同:它试图在价格走势中寻找模式,并据此预测股价。这听上去像“迷信”,但如果大家都相信同一种模式,这些模式就会反复出现——有人根据信号行动,随后更多人跟随,趋势于是再度上演。接下来我们更深入地看图表,并定义这些可能影响技术分析师决策的形态。
10.1.1 读图(Reading charts)
让我们来看图 10.9,这些图展示了过去五年里多类资产的价格演变。
图 10.9 四张图(英伟达、比特币、10 年期美元国债收益率,以及美元兑欧元汇率)。每张图都在“讲故事”,我们可以据此推断当时发生了什么。
有些才艺秀会给选手设置挑战。若有一档面向投资者的才艺秀,其中一个挑战可以是:给出不带标签的走势图,让选手猜公司名字——有点像“听前奏猜歌名”。一些投资者对熟悉的图形确实做得到。比如他们看着比特币会这样说:
“看到左边这两个隆起了吗?那是 2021 年。年初顺风顺水,5 月我以为完蛋了,11 月又欢呼起来,直到……你懂的,Luna、FTX,那阵子简直噩梦。”
这可真是一档我愿意看的游戏节目!哪怕我们还没给形态命名,能识别一些模式就是开始——因为你会围绕这些模式自然而然地编织故事:一度扶摇直上、随后坠落、又涅槃重生……许多股价演变的故事都可以被戏剧化。我们来更近地看看。
普适原则?(UNIVERSAL PRINCIPLES?)
价格“应该如何走”是否存在一般性的洞见?不妨试试。一个原则是均值回归:涨多了会跌、跌多了会涨。如果把均值回归当作指导原则,你就可能在大涨后选择卖出,相信回调不可避免。
把动量视为核心的人可能不同意:一只股票涨起来,动量可能继续增强。既然趋势在延续,为什么要卖?虽说动量不会永恒,但他们会尽量骑着趋势走更久。
逆向投资者也可能不同意:“跟着市场做我多半要亏,我宁可去找别人没在做的事。”
投资者的信念各异,甚至相互矛盾。股价上涨时,均值回归者与逆向者可能建议卖出,而动量派会建议持有甚至加仓——不可能人人都正确。
10.1.2 形态(Patterns)
我们来审视资产的价格变化,看看能否识别相似性。此前在比特币图上提到的那两个“隆起”,可以解读为看跌双顶(bearish double top) 。见图 10.10,它用纵轴表示股价、横轴表示时间。
图 10.10 看跌双顶:在一段上升趋势后形成两个峰值,随后下行。若交易者及时识别该形态,可以相应选择卖出或做空/对冲。
起初是上升趋势。交易者兴奋,看到动量,期待更多收益。随着“低买高卖”的收益示范,更多人买入,价格推升。各平台会衡量客户情绪,给出某些资产的“恐惧与贪婪指数”。在这阶段,情绪常表现为极度贪婪。
随后出现转折点(图中标注为Top 1)。原因可能多样,但投资者可能在基本面评估后认为价格偏高,于是开始卖出,情绪转弱。不过仍有相当一部分人看好该资产,于是重新买入、价格再度上行。第二次转折后,价格再次下跌,这回下跌延续。
网上或技术分析资料里可以查到大量图形形态:如头肩顶/头肩底(head-and-shoulders) 、上升楔形(rising wedge) 、扩张三角形(expanding triangle) 、三重顶(triple top) 、旗形/三角旗(pennant/flag)等。基本思路相同:价格上下起伏,交易者试图基于历史经验识别形态并预测后续。每种形态一般都有看涨版和看跌版(见图 10.11)。
图 10.11 看涨双顶与其“孪生兄弟”看跌双顶。查阅其他形态(如头肩、楔形)时,也总能看到看涨/看跌两类版本。
双顶既是反转形态也是延续形态:看跌版以上升开场、以下跌告终;看涨版反之。作为延续形态时,它表现为“原有趋势→短暂相反趋势→回归原趋势”。图 10.12 展示了看涨/看跌旗形(flag)作为延续形态的参考例:先有趋势,再被相反方向短暂打断,最终延续原方向。
图 10.12 看涨与看跌旗形是延续形态的参考:初始短期趋势被逆向走势打断后,又继续沿原方向发展
需要强调的是,图形形态只是模板。“图表低语者”过去也常常犯错;很多人误判过走势,因为没有任何保证价格一定会按模板走。
当资产下跌时,分析师常谈支撑线(support) :指某一价位被认为不易跌破。同时也会谈阻力线(resistance) :指不易被突破的高位。另一个重要术语是突破(breakout) :价格跨越支撑或阻力之时。
对波动更大的资产而言,支撑/阻力尤为关键。写书时,1 枚比特币价格为 72,000 存在支撑——换言之,除非发生外部事件,价格不太可能跌破该位。
尽管支撑与阻力是对数据的解读,但心理作用不容忽视。设想**$100,000** 是阻力位:你看着价格反复靠近 10 万,内心可能开始辩论。你内心的“冒险派”说:“上吧!长期看它总会涨。今天就是那天!”而“保守派”回应:“理性点,六位数很高了。你在 10 万下方买时,别人会卖、价格会回落,你就被套住。等回落再买更好。”冒险派又说:“要是它一举突破 10 万且不回头呢?现在不买就晚了。”——由此可见,了解当下的支撑/阻力对交易者很重要;但长期投资者可能会忽略它们,因为他们相信资产长期增长。
你打算持有的时间越短,图形形态的重要性就越高。短线交易者常加杠杆买入,如果不尽快了结,就要付利息。
总之,图表可以帮助交易者与投资者判断买卖时机;而长线投资者也不会完全无视它们。下面看看如何解读一张图。
10.1.3 解读一张图(Interpreting a chart)
许多金融分析平台(如 TradingView、Finviz、Seeking Alpha)都提供高级图表功能:你可以绘制股价,并在图上叠加显示指标。本章稍后会用 Python 演示其中一些指标的绘制。但要注意,本书涉及的指标(如移动平均、布林带、一目云等)只是高级图表里的少数。
图 10.13 为 Finviz.com 上的 NuScale(SMR) 图表(网址:finviz.com/quote.ashx?…)。截图基于 2025-06-13 的数据,展示了通过可视化我们能多快把握更长时间维度上的价格变化。
技术派投资者会基于此类图表识别看跌/看涨形态。你甚至可以把类似的截图贴给 GenAI 聊天机器人,请它识别形态;它会给出解读,某些情况下还会基于信号(例如 5 月下旬出现“黄金交叉” :SMA50 上穿 SMA200——本章稍后会讨论简单移动平均(SMA) )对可能的价格演变做推测。
一些投资者认为技术分析的有效性未被证明,把它视为一种赌博;另一些人则说,只要胜率达到 51% ,就能开始赚钱。无论你是否想把价格形态用于短线交易,读图对长线投资者同样重要,因为图表会在时间维度上讲述重要故事。
资产具有动量,常反映在图上的形态里。时间尺度也是读图的关键:短期下跌未必让长线投资者担忧,只要长期趋势仍向上;而用杠杆的短线交易者必须更早决策,因为他们要支付资金成本。
技术交易者会开发算法来分析移动平均(MA) 、布林带(Bollinger Bands) 、斐波那契回撤、艾略特波浪(Elliott Waves)等技术指标。许多技术投资者采用均线交叉策略,例如 200 日 SMA vs. 5 日 SMA。我们接下来就看看这些基本原则到底在讲什么。
10.1.4 替代图表类型(Alternative chart types)
虽然我们主要用折线图来可视化资产随时间的价格,但投资者也常使用其他类型的图表。下面来看看其中几种。
TREEMAPS(矩形树图)
矩形树图在 Finviz 等平台上很受欢迎,用来展示价格的涨跌。图 10.14 显示了 Finviz 上的矩形树图。
图 10.14 Finviz 的矩形树图能帮助我们把握价格运动。查看当日图表(finviz.com/map.ashx)获取最新数据,并以交互方式下钻,从配色方案中读出更多信息。
BAR CHART(条形图)
条形图有助于展示某一类别内元素的分布。Finviz 提供了很好的示例(finviz.com/groups.ashx)。在图 10.15 中,我们可以看到单日表现较好的行业板块。Finviz 也提供反映更长区间资产绩效的条形图。
图 10.15 条形图展示各行业在单日内的增长分布。条形图非常适合呈现群组内部的分布情况。
HISTOGRAM(直方图)
直方图以一系列条形展示某数值变量的分布,可用来验证数据是否符合高斯分布。第 7 章中,我们在蒙特卡罗模拟里用直方图展示收益率(见图 10.16),其分布较为均衡地覆盖在高斯曲线附近。
图 10.16 Apple(苹果)一段时期内收益率的直方图,表明其收益率近似正态分布;换言之,无需过度担忧该股票的表现。
SCATTERPLOT(散点图)
第 8 章中,我们使用散点图来绘制公司点位。当你基于多种标准对多只资产分组时,散点图能帮助识别有趣的标的。例如,若希望在风险与收益两个维度上考察多只资产,就可用它们为横纵轴绘制散点图(第 8 章已有演示)。
我们在图 10.17 中再次展示第 8 章那张图,以快速说明散点图的价值。可以看到三个离群点:最左侧的点代表跌幅显著的资产;若鼠标悬停即可知是 Moderna——一家资本密集型企业,同时受到较高联邦利率等因素影响。最上方的点是 Super Micro Computer,这家半导体公司因未能按时披露而陷入风波、股价大跌。最右侧是 Palantir,其高回报也反映在其 P/E(市盈率)上,这一点从相应比率的复核中可见。
图 10.17 按波动率与收益对股票进行分类的散点图
10.2 使用图表解读价格变化(Using charts to interpret price changes)
我们已经了解到,相比直接查看原始数据,图表能更快带来洞见。从某种意义上说,图表是数据分析的捷径。现在,我们将稍微转向把价格波动放入语境来分析——这对某些投资与交易策略至关重要。
本章开头强调过:缺乏额外知识与语境的标价会让人困惑。我们想购买的东西有其内在价值(也称真实价值)。在货币出现之前,商品交换的基本模式就是“换到更划算的价值”。这一普适原则同样适用于证券:我们为获取资产所支付的价格不同,交易就可能是好或坏。
在做基本面研究时(如前几章),我们会审视公司的细节,以判断资产的价值是上升还是下降。有效市场理论(Eugene Fama)认为:除非你掌握内幕、知道别人不知道的事,资产价格大体等于其价值。但也有专家不同意此观点,因为确有投资者长期跑赢市场。
通过分析基本面,你形成心中的估值。你检查资产负债表等文件,根据公司内部变化来假设该资产是低估还是高估。而在图表中的价格走势分析(通常称技术分析)中,你考察价格随时间的变化,关注市场认为公司值多少钱,从而判断趋势,据此安排买卖时点。开始吧。
10.2.1 蜡烛图(Candlesticks)
蜡烛图用于可视化金融市场的价格运动,提供市场情绪的线索,展示在给定时间窗口内的波动。
蜡烛图像时序图,但加入了**开盘价、最高价、最低价、收盘价(OHLC)**等信息。先看蜡烛图的“解剖结构”,理解其工作方式。
蜡烛图的结构(ANATOMY OF A CANDLESTICK CHART)
图 10.18 展示了 NVIDIA 连续三个交易日的蜡烛图(本图每根蜡烛代表一天)。时间尺度可伸缩——同样的图也可以按小时或月份来画。
图 10.18 该蜡烛图覆盖英伟达三个交易日,可据此推断价格运动。第 1 与第 3 天收盘价低于开盘价;且第 3 天的波动更大。
蜡烛图常用不同的配色方案。多数平台用绿/红;在纸媒等场景也常见白/黑。绿色或白色表示在该周期内(此例为单日)收盘价高于开盘价,即看涨;红色或黑色表示收盘低于开盘,即看跌。想看更多示例图,可去 Finviz(finviz.com)或 TradingView(www.tradingview.com),能下钻并放大到具体日期查看细节。研究你熟悉的公司也很有帮助——通常可以看到投资者对 催化剂(见第 11 章)的反应如何映射到股价上。
图 10.19 展示了一根蜡烛的结构。实体高度表示开收盘之差;上下影线(wicks/shadows)表示该周期内达到的最高/最低。上影线从实体顶端延至最高点;下影线从实体底端延至最低点。
图 10.19 看涨(白色)与看跌(黑色)蜡烛的结构
结合前面的图 10.18:1 月 10 日股价开于 140,也曾下探至略高于 136 略下。
若要画出类似图 10.19 的图,需要 Python 的 mplfinance 库来做金融可视化。我们从英伟达获取数据并作图,如清单 10.1。输出与前文图 10.6 一致。
清单 10.1 绘制蜡烛图(Creating candlestick charts)
import mplfinance as mpf
import pandas as pd
import yfinance as yf
ticker = "NVDA"
data = yf.Ticker(ticker).history(start="2025-01-10", end="2025-01-15")
df = pd.DataFrame(data).dropna()
mpf.plot(df, type='candle', style='charles', title='Candlestick Chart')
接下来,我们要在连续多日的基础上观察更复杂的蜡烛形态,以理解如何据此解读价格运动。
蜡烛形态(CANDLESTICK PATTERNS)
蜡烛形态帮助交易者/投资者快速解读价格行为。通过分析多根蜡烛可以识别趋势、反转与延续,例如:
- 长影线:表明对某些价位的试探/拒绝(支撑或压力显著)。
- 小实体:市场犹豫/分歧。
- 大实体:动量强。
常见、用于研判价格的形态(见图 10.20)包括:
- 十字星(Doji) :开收几乎相同,实体很小,提示犹豫。
- 锤子线(Hammer) :小实体 + 长下影,下跌后可能出现的反转信号。
- 吞没形态(Engulfing) :一根大实体完全包住前一根,意味着动量显著转向。
图 10.20 多种蜡烛形态,帮助描述市场行为。除十字星、锤子线、吞没外,还能直观看到波动程度:长实体对应高波动,多根短实体对应平静。
用上图术语,我们可以更精确地描述:
纺锤线(Spinning Top)常指连续两天日内波动大,但收出十字;
流星线(Shooting Star)则是日内波动大、收低于开。
为形态命名有助于与其他投资者讨论趋势。
理解了蜡烛图后,我们可以考察多周期均线来进一步把握股价演变。下面就来讨论。
10.2.2 基于均线的图表(Charts based on averages)
在本章开头,我们强调了历史成交信息的重要性。设想某件商品标价 80,你对它的看法会与通常能卖到 $120 时截然不同。
当我们研究价格经常波动的股票时,必须在较长时间上考察其价格演变。除了高点与低点,一个核心参照就是平均价:给出 1、2、3 三个数,人人都知道平均值是 2。下面看更复杂的情形。
取某只股票 x 天的收盘价,将这些值求和后除以数量即可得到该段的平均值。若随时间滚动观察,就设定一个窗口(比如 20 天)计算平均;当加入一个新值时,同时移除最早的一个值。这就是本章先前提到的简单移动平均(SMA) 。
我们也会思考:所有数据等权是否高效?近期数据是否值得更高权重?因此出现了引入权重的均线公式,比如指数移动平均(EMA) 、加权移动平均(WMA)与Hull 移动平均(HMA) (本章将讨论)。目标是平滑价格数据并更早识别趋势。不同均线针对不同场景。
在清单 10.2 中,我们构造演示数据并分别应用 SMA 与 EMA。EMA对更近的数据权重更高。我们先定义一组随机数据并应用算法。
清单 10.2 计算 SMA 与 EMA(Calculating SMA and EMAs)
import pandas as pd
data = {
"Date": pd.date_range(start="2023-01-01", periods=10),
"Price": [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
}
df = pd.DataFrame(data)
window_size = 3
df['SMA'] = df['Price'].rolling(
window=window_size).mean() #1
df['EMA'] = df['Price'].ewm(
span=window_size, adjust=False).mean() #2
#1 SMA is the unweighted mean of the last N values.
#2 EMA gives more weight to recent values.
从结果看,在最近三天 16、17、18 的情形下,EMA 的均值(17.003906)略高于 SMA 的 17。若单笔不大,这差异似乎不显著;但在大额交易中影响可观。下面用历史股价比较,并加入其余两种均线。
NOTE 为了更好显示,我们会限制小数点后的位数:
pd.options.display.float_format = '{:,.2f}'.format
WMA 同样加权,但不同于 EMA,它按线性方式分配权重,在一定程度上平衡 EMA 与 SMA 的差异。HMA 以 WMA 为基础,通过在计算中引入半窗口与窗口平方根进一步平滑。若对细节感兴趣可查阅数学定义;简述为:
HMA > WMA > EMA > SMA(越靠左越敏感于最新变化)。Python 实现如下:
def weighted_moving_average(prices, window):
weights = np.arange(1, window + 1)
return prices.rolling(window).apply(
lambda x: np.dot(x, weights) / weights.sum(), raw=True)
def hull_moving_average(prices, window):
half_window = int(window / 2)
sqrt_window = int(np.sqrt(window))
wma_half = weighted_moving_average(prices, half_window)
wma_full = weighted_moving_average(prices, window)
hma = weighted_moving_average(2 * wma_half - wma_full, sqrt_window)
return hma
一切就绪后,我们加载 2024 年的 NVIDIA 数据并比较。下面代码将各类收盘价均线添加到 DataFrame:
import yfinance as yf
ticker = "NVDA"
data = yf.Ticker(ticker).history(start="2024-01-01", end="2024-12-31")
averages = pd.DataFrame(data).dropna()
averages.reset_index(inplace=True)
def add_averages(close, window_size):
close['SMA'] = close['Close'].rolling(window=window_size).mean()
close['EMA'] = close['Close'].ewm(span=window_size, adjust=False).mean()
close['WMA'] = weighted_moving_average(close['Close'], window_size)
close['HMA'] = hull_moving_average(close['Close'], window_size)
return close[['Date', 'Close', 'SMA', 'EMA', 'WMA', 'HMA']]
观察数据可见:窗口越大,DataFrame 起始段的 NaN 越多。各均线值略有差异。我们抽取一个日期(2024-02-02)并展示 3 日与 20 日窗口的结果(表 10.2)。
表 10.2 同日的多种均线与不同时间窗口
| 窗口(天) | 收盘价 | SMA | EMA | WMA | HMA |
|---|---|---|---|---|---|
| 3 | 66.14 | 63.55 | 64.29 | 64.33 | 67.96 |
| 20 | 66.14 | 58.33 | 58.83 | 60.60 | 64.19 |
结合 EMA/WMA/HMA 可见:近期数据权重越高,均线值越贴近最后一天的收盘价;而 SMA 更远一些。也就是说,SMA 对价格变化的反应更慢。这未必是缺点:若关注长期趋势,短期尖峰对我们不构成困扰。可归纳如下:
- SMA:最适合长期趋势分析。
- EMA:适用于需要更快反应的短线策略。
- WMA:在 EMA 与 SMA 之间取平衡,但仍强调近期价格。
- HMA:对快速趋势的捕捉最有效。
我们还能提出另一条假设:无论使用哪类均线,都可以采用不同时间窗口以获得不同维度的洞见。下面继续。
移动平均带(MOVING AVERAGE RIBBONS)
移动平均带可视化趋势强度:带窄表示盘整,带宽表示强趋势。清单 10.3 用 6 个窗口(10、20、30、40、50、60)绘制均线带。
清单 10.3 绘制移动平均带(Plotting moving average ribbons)
import yfinance as yf
import matplotlib.pyplot as plt
ticker = "NVDA"
df = yf.Ticker(ticker).history(start="2024-01-1", end="2025-01-1")
df.reset_index(inplace=True)
periods = [10, 20, 30, 40, 50, 60] #1
for period in periods: #1
df[f"SMA_{period}"] = df["Close"].rolling(
window=period).mean() #1
plt.figure(figsize=(12, 6))
plt.plot(df["Date"], df["Close"], label="Price", color="black", linewidth=1.5)
for period in periods:
plt.plot(df["Date"], df[f"SMA_{period}"], label=f"SMA {period"})
plt.title("Moving Average Ribbon")
plt.xlabel("Date")
plt.ylabel("Price")
plt.legend()
plt.grid()
plt.show()
#1 Creates different windows
结果见图 10.21。可见 5 月与10 月的波动较少,表明当时价格更稳定;而带子张开更大的时期,趋势更强。
图 10.21 移动平均带在 2024-05 与 2024-10 收缩,暗示当时市场处于盘整
NOTE 第 11 章将探讨多种基于 SMA 的交易策略,研究最佳窗口设置,并验证诸如 SMA50 vs. SMA200 等常用参数是否最优。
接下来用布林带(Bollinger Bands)来更清晰地刻画超买/超卖。
布林带(BOLLINGER BANDS)
布林带是衡量价格波动性并识别超买/超卖的常用工具。参数如下:
- 中轨(Middle band) :通常为 20 期 SMA,表示所选区间的平均价。默认 20,可调整;过大或过小都会使信号迟钝或过敏。
- 上轨(Upper band) :中轨 + 2×标准差,常视为超买/阻力区域。
- 下轨(Lower band) :中轨 – 2×标准差,常视为超卖/支撑区域。
用 Python 绘制布林带并解读。我们收集某股票的技术数据,按清单 10.4 的参数计算各条带:先加载股票,设定 20 为默认窗口,计算标准差与上下轨,最后绘图。
清单 10.4 绘制布林带(Plotting Bollinger Bands)
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
ticker = "NVDA"
data = yf.download(ticker, start="2024-01-01")
window = 20 #1
std_dev_factor = 2 #1
data['SMA'] = data['Close'].rolling(
window=sma_window_length).mean() #1
rolling_std = data['Close'].rolling(
window=sma_window_length).std() #1
data['rf'] = (rolling_std * std_dev_factor) #1
data['upper'] = data['SMA'] + data['rf'] #1
data['lower'] = data['SMA'] - data['rf'] #1
plt.figure(figsize=(12, 6))
plt.plot(data.index, data['Close'], label="Close Price", color="blue",
linewidth=1)
plt.plot(data.index, data['SMA'], label=f"{window}-Day SMA",
color="orange", linewidth=1.5)
plt.plot(data.index, data['upper'], label="Upper Bollinger Band",
color="green", linestyle="--", linewidth=1)
plt.plot(data.index, data['lower'], label="Lower Bollinger Band",
color="red", linestyle="--", linewidth=1)
plt.title(f"Bollinger Bands for {ticker} (20-day window)", fontsize=14)
plt.xlabel("Date", fontsize=12)
plt.ylabel("Price", fontsize=12)
plt.legend()
plt.grid(alpha=0.3)
plt.show()
#1 Calculates the bands
图 10.22 展示了布林带:你会看到依据上述公式绘出的中轨、上轨、下轨,从而更清晰地识别上/下边界。
图 10.22 英伟达的布林带,为潜在买卖时点提供线索
布林带在高波动期会张开,在低波动期会收缩。约在 2024 年 8 月附近,收盘价波动显著;通常在剧烈波动之后,会经历收缩—盘整阶段,并可能迎来突破。
价格往往在带内运行。我们试图识别偏离时刻,它可能意味着价格动作、趋势延续或反转。在 2024 年 4 月下旬,股价低于 80 并下穿下轨——可能是买入信号;而 2024-06 附近出现卖出信号。若 4 月买入会盈利;但若 6 月未识别这是强上升趋势而卖出,可能会亏损——强趋势下,传统卖出信号可能失效。
第 11 章我们会通过回测验证策略。先看下一个指标。
移动平均收敛/发散(MACD, Moving Average Convergence Divergence)
MACD 是基于动量的技术指标,用来寻找潜在买卖点。它分析两条均线(短期与长期)之间的关系。常见设置如下(通过不同窗口捕捉信号):
- MACD 线:短期 EMA 与长期 EMA 的差。通常为 12 期 EMA – 26 期 EMA。
- 信号线(Signal line) :MACD 线的 9 期 EMA,作为触发买卖信号的参考。
- 柱状图(Histogram) :MACD – 信号线的可视化。当柱状图 > 0,MACD 在信号线上方,表示看涨动量; < 0 表示看跌动量。
看清单 10.5:在 calculate_macd 中,我们基于收盘价计算 EMA_12 与 EMA_26,得到 MACD 及其信号线;随后用 plot_macd 绘图。
清单 10.5 MACD
import pandas as pd
import matplotlib.pyplot as plt
def calculate_macd(data, short_window=12, long_window=26, signal_window=9):
data['EMA_12'] = data['Close'].ewm(
span=short_window, adjust=False).mean() #1
data['EMA_26'] = data['Close'].ewm(
span=long_window, adjust=False).mean() #1
data['MACD'] = data['EMA_12'] - data['EMA_26'] #1
data['Signal_Line'] = data['MACD'].ewm(
span=signal_window, adjust=False).mean() #1
return data
def plot_macd(data):
plt.figure(figsize=(12,6))
plt.plot(data.index, data['MACD'], label='MACD', color='blue')
plt.plot(data.index, data['Signal_Line'],
label='Signal Line', color='red')
plt.bar(data.index, data['MACD'] - data['Signal_Line'],
label='Histogram', color='gray', alpha=0.5)
plt.legend(loc='best')
plt.title('MACD Indicator')
plt.xlabel('Date')
plt.ylabel('MACD Value')
plt.grid()
plt.show()
import yfinance as yf
ticker = "NVDA"
df = yf.Ticker(ticker).history(start="2024-01-01")
df = calculate_macd(df)
plot_macd(df)
#1 Calculates the MACD parameter
得到 NVIDIA 的 MACD 图(见图 10.23),即可检视历史买卖信号。
图 10.23 英伟达收盘价的 MACD。信号线与 MACD 线的交叉会产生交易信号
金叉/死叉判定买卖:当 MACD 线上穿信号线时为看涨信号(如 2024 年 3 月前与5 月再次出现);MACD 线下穿信号线为看跌信号(如 2024 年 4 月附近),可能提示卖出。同时结合零轴穿越:2025 年 3 月的整体情绪较2024 年 3 月更偏看跌。
柱状图的变大意味着当前趋势的动量增强;缩小则暗示动量减弱,可能临近反转。
理解 MACD 对算法交易系统的构建与使用尤为有益。需注意:MACD 并非万能,在盘整/震荡市中会产生虚假信号。因此最好结合其他指标或基本面进行确认。
在第 11 章,我们还将回测这类策略,并评估若按此买卖会赚或亏多少。接下来继续下一个技术指标。
10.2.3 一目均衡表(Ichimoku Cloud)
一目均衡表(Ichimoku Cloud,亦称 Ichimoku)是一种技术分析图表技术,在一张图内同时体现趋势、动量、支撑与阻力,从而对价格行为给出更全面的视角。与 SMA 或蜡烛图不同,Ichimoku 提供了多维度的市场解读方法。
NOTE 一目均衡表的关键元素常使用日文术语。为清晰与简洁起见,本书省略了这些术语;你可以在 Investopedia 等网站查询。
虽然初看略显复杂,但把它拆解为组件就容易理解了。Ichimoku 包含五个关键要素:
- 转换线(Conversion line) ——短期均线,对价格变化反应较快
- 基准线(Baseline) ——中期趋势线,用于度量动量与趋势方向
- 领先跨度 A(Leading span A) ——形成云层的第一条边界,常作为动态支撑/阻力
- 领先跨度 B(Leading span B) ——形成云层的第二条边界
- 滞后跨度(Lagging span) ——把当前价格与过去价格对比,用于趋势确认
均线的时间窗要相应调整:转换线采用较短窗口(如 9 日),基准线采用较长窗口。领先跨度 A 与 B 由上述两线(以及更长窗口)推导而来,代码中会演示。
下面用 Python 与 Matplotlib 实现 Ichimoku(清单 10.6)。我们重新加载 NVIDIA 数据,计算各项参数并绘图。
清单 10.6 创建一目均衡表(Creating an Ichimoku)
import yfinance as yf
import matplotlib.pyplot as plt
ticker = "NVDA"
data = yf.Ticker(ticker).history(start="2024-01-10", end="2025-01-15")
df = pd.DataFrame(data).dropna()
df.reset_index(inplace=True)
df["ConversationLine"] = (df["Close"].rolling(
window=9).max() + df["Close"].rolling(
window=9).min()) / 2 #1
df["BaseLine"] = (df["Close"].rolling(
window=26).max() + df["Close"].rolling(
window=26).min()) / 2 #1
df["SpanA"] = ((df["ConversationLine"]
+ df["BaseLine"]) / 2).shift(26) #1
df["SpanB"] = ((df["Close"].rolling(
window=52).max() + df["Close"].rolling(
window=52).min()) / 2).shift(26) #1
df["LaggingSpan"] = df["Close"].shift(-26) #1
plt.figure(figsize=(12, 6))
plt.plot(df.index, df["Close"], label="Close Price", color="black")
plt.plot(df.index, df["ConversationLine"], label="ConversationLine",
color="red")
plt.plot(df.index, df["BaseLine"], label="BaseLine", color="blue")
plt.fill_between(df.index, df["SpanA"], df["SpanB"],
where=df["SpanA"] >= df["SpanA"], color="lightgreen", alpha=0.5)
plt.fill_between(df.index, df["SpanA"], df["SpanB"],
where=df["SpanA"] < df["SpanB"], color="lightcoral", alpha=0.5)
plt.plot(df.index, df["LaggingSpan"], label="LaggingSpan", color="purple", linestyle="dotted")
plt.title("Ichimoku Cloud")
plt.legend()
plt.show()
#1 Calculates the parameter for the Ichimoku
来看图 10.24 的结果。可以看到总体上呈偏多动能,这从大量绿色(灰度版为浅灰)云层即可观察到。
图 10.24 英伟达收盘价的一目均衡图,显示多次偏多时段
Span A 与 Span B 之间的区域构成云层:当 Span A 在 Span B 之上时,云为绿色(灰度为浅灰),属看涨信号;反之 Span B 在 Span A 之上,云为红色(灰度为深灰),属看跌信号。
云层在上升趋势中常充当支撑,在下降趋势中常充当阻力。据此我们可识别趋势:
- 价格在云上方:市场处于上行趋势
- 价格在云下方:市场处于下行趋势
- 价格位于云中:市场处于盘整/不确定阶段
也可据此寻找买卖信号:
- 看涨(买入) :当转换线自下而上穿越基准线,且价格位于云上方
- 看跌(卖出) :当转换线自上而下跌破基准线,且价格位于云下方
云层越厚,代表支撑/阻力越强,价格越难穿越。
10.3 使用 Streamlit 进行可视化(Visualization with Streamlit)
在一些场景中,把结果在线可视化会更有帮助。你们当中有人熟悉先进的 Web 技术,也有人不想投入到复杂度更高的方案里。我们需要一种简单直接的可视化技术。
一种快速生成 Web 界面的方法是使用 Streamlit(Python 包)。Streamlit 允许我们定义交互控件,执行 Python 脚本后会生成本地网页。注意:下面示例使用普通 Python 文件(后缀 .py),而非本书其余部分常用的数据科学 Notebook。
清单 10.7 给出了一个绘制收益率的简单例子。我们预设了多个 ticker,并定义起止日期的输入控件。代码会检查是否选择了单只或多只股票;若有数据,则计算收益并绘制折线图。
清单 10.7 用 Streamlit 展示收益率(Displaying returns with Streamlit)
import streamlit as st
import pandas as pd
import yfinance as yf
st.title("Finance Dashboard")
tickers = ('TSLA', 'AAPL', 'MSFT', 'ETH-USD', 'BTC-USD')
dropdown = st.multiselect("Select Ticker", tickers)
start = st.date_input('Start Date', pd.to_datetime('2024-01-01'))
end = st.date_input('End Date', pd.to_datetime('today'))
def relativeret(df):
rel = df.pct_change()
cumret = (1+rel).cumprod() -1
cumret = cumret.fillna(0)
return cumret
if len (dropdown) > 0:
df = relativeret(yf.download(dropdown, start=start, end=end))['Close']
st.header(f"Returns of {dropdown}")
st.line_chart(df)
在命令行里运行 streamlit run <pythonfile> 启动网站;按命令行提示在浏览器中打开,即可看到与图 10.25 类似的图表。
图 10.25 在 Streamlit 渲染的网页上展示 Apple 与 Tesla 的收益曲线
理解了 Streamlit 的基本原理后,我们就能绘制均线带、布林带、MACD 与蜡烛图。在清单 10.8 的代码中,我们用单选(selectbox)替代前例的多选,因为每次只看一家公司。清单中省略了布林带、MACD、均线带的逻辑——它们与前述清单一致,已封装到方法里。完整代码可在本书的 Manning 下载页获取。对于蜡烛图,我们演示了如何用 mplfinance 在 Streamlit 中展示:需传入 returnfig=True 以确保正确渲染。要显示某个图表,我们把方法作为参数传给通用绘图函数并调用。
清单 10.8 Streamlit 技术分析(Streamlit Technical Analysis)
import streamlit as st
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import mplfinance as mpf
st.title("Technical Analysis")
ptype = ('NONE', 'MA', 'BOLL', 'MACD', 'CANDLE', 'CLOUD')
tickers = ('TSLA', 'AAPL', 'MSFT', 'ETH-USD', 'BTC-USD')
dropdown = st.selectbox("Select Ticker", tickers)
plot = st.selectbox("Select Plot", ptype)
start = st.date_input('Start Date', pd.to_datetime('2024-01-01'))
end = st.date_input('End Date', pd.to_datetime('today'))
def bollinger(data):
pass
def macd(data):
pass
def ma(data):
pass
def cloud(data):
pass
def candle(data):
fig, ax = mpf.plot(data, type='candle',
style='charles',figsize=(15,10), title='Candlestick Chart', returnfig=True)
return fig
def plot_ta(title: str, method): #1
st.header(title) #1
plt = method(df) #1
st.pyplot(plt) #1
if len (dropdown) > 0 and plot != 'NONE':
df = yf.Ticker(dropdown).history(start=start, end=end)
match(plot):
case 'BOLL':
plot_ta("Bollinger bands", bollinger)
case 'MA':
plot_ta("Moving Average Ribbons", ma)
case 'MACD':
plot_ta("MACD", macd)
case 'CANDLE':
plot_ta("CANDLE", candle)
case 'CLOUD':
plot_ta("CLOUD", cloud)
#1 Method to plot the chart
图 10.26 展示了使用布林带时的参考结果。若你运行本书 GitHub 仓库提供的 Streamlit 应用,也可以绘制其他图表。
图 10.26 使用 Streamlit 显示布林带的网页示例
小结:即便你不是那种依靠图形形态来判定买卖点的技术派交易者,图表仍能帮助你更好地理解股价如何演化。你可以用图表识别组合中的潜在薄弱点,再结合对该股票投资论证的重新检视,形成更有依据的决策。
总结
-
每张图都有自己的故事。若你绘制某项资产的股价走势,你也会看到“催化剂”(显著推动价格变化的事件)的影响。
-
需要把价格波动放到背景中解读。对不同资产类别,人们对波动性的预期高低不同。
-
你可以在图表中寻找形态来预测未来走势。
-
形态(如“双顶”)是参考范例。它们能揭示趋势,但识别并不容易,因为它们只是历史模板。
-
图表形态大致分为两类:反转与延续。前者先沿趋势运行后反向,后者在短暂整理后继续原趋势。
-
覆盖整个股市的树状图(Treemap) (如 Finviz 推广的)可对市场全体成分给出一览式概览。
-
柱状图用于展示某个群组内的分布;例如按权重可视化 ETF 内部资产。
-
直方图非常适合绘制日收益率分布;若呈高斯分布,通常说明“一切正常”。
-
散点图可用于在一组资产中识别特定目标。
-
蜡烛图能展示在一个蜡烛周期内的市场趋势与波动。
-
蜡烛实体代表开盘价与收盘价之差;上下影线标示当日最高与最低价。
-
通过多日蜡烛可以识别并命名形态与趋势。常见形态包括:
- 十字星(Doji) :开收几乎相同,显示犹豫;
- 锤子线(Hammer) :下跌后可能反转;
- 吞没形态(Engulfing) :动量出现显著转变。
-
计算均线的方法很多;有些会对数据点赋予不同权重:
- 用简单移动平均(SMA)看长期趋势;
- 用指数移动平均(EMA)捕捉动量与最新变化;
- 用加权移动平均(WMA)获取更敏捷的信号;
- 用Hull 移动平均(HMA)在低延迟与平滑度间取得平衡。
-
**移动平均带(MA ribbons)**是在收盘价上叠加多条不同窗口的均线。
-
当均线带收拢时,通常意味着股价处于盘整。
-
使用**布林带(Bollinger Bands)**分析波动与极值;价格触及上/下轨时更易识别。
-
MACD(移动平均收敛/发散)衡量两条均线的关系;用于判断动量与金叉/死叉。
-
交易者关注此类图表中的均线交叉,将其作为买卖信号。
-
Streamlit让 Python 程序员无需掌握前端开发,也能把分析可视化到网页上。