写在前面
一个具体的场景。
你打开行情软件,切到「沪铜主连」,日线。MACD 刚刚金叉,柱子由绿转红。你下单——买入 cu2601。
这一步里藏着一个几乎没人注意的错位:
你判断用的那条线,和你下单买的那份合约,不是同一个东西。
主连是把一份份到期的合约首尾接起来拼出来的一条曲线,它自己不是合约,你下不了单——这件事之前专门讲过。但当时讲的是"接缝处那个跳空是假的"。
MACD 的麻烦比那个深一层。因为它是一个有记忆的指标。
EMA 的每一个值都压着它之前所有值的影子。所以接缝造成的那一下扰动,不会在换月当天结束——它会在那条线的记忆里住下来,住很久。
你看的那根 MACD,有一段记忆属于一份你从来没有持有过的合约。
住多久、有多深、会不会真的改掉一个金叉——这三个数就是本篇的正文。
先说清这篇不做什么:不做"金叉死叉的胜率是多少"的回测。那需要完整的回测框架、成本假设和样本外检验,是另一个栏目的活。本篇要说的是更前置的一件事——在你回测它之前,先确认你手里那条线是干净的。
一、定义:它在回答什么问题
MACD(Moving Average Convergence / Divergence),Gerald Appel 在 1970 年代提出。名字直译过来是「移动平均的收敛与发散」——名字里就写着答案。
它由三个部件组成:
| 部件 | 算什么 | 中文常见叫法 |
|---|---|---|
| DIF | 快均线 − 慢均线 | 差离值、快线 |
| DEA | DIF 自己的平滑 | 讯号线、慢线 |
| 柱 | DIF − DEA(国内普遍再乘 2) | 柱状图、能量柱 |
一句话定义:
MACD 测的是"快慢两条均线之间的距离,正在变大还是变小"。
距离变大,说明快线在加速甩开慢线,趋势在加速。 距离变小,说明快线在往回靠,趋势在减速。
请把这两个词咬住:加速、减速。
⚠️ MACD 测的是趋势的加速度,不是趋势的方向。
这是它的头号误用来源:把"减速"读成了"要反转"。
你可以想成划船:桨划得慢下来,船不会立刻倒退——它还带着惯性往前漂。
"桨慢了"和"船退了"是两件事,而 MACD 只看得见桨。它只知道推力在减弱,不知道船接下来往哪儿去。
二、公式:为什么是"差的差"
(EMA 就是加权更偏向近期的那种均线。它和 SMA、和 Wilder 平滑的差别,在 ATR 那一篇里已经拆过,这里不重复。)
值得说的是这个结构:它做了两次减法,而每一次减法都丢掉一层信息,换来一层灵敏。
| 第几层 | 做了什么 | 丢掉了什么 |
|---|---|---|
| 价格 → 均线 | 平滑 | 丢掉日内噪音 |
| 均线 → DIF | 快慢相减 | 丢掉价格的绝对水平 |
| DIF → 柱 | 再减一次 | 丢掉速度本身,只剩"速度在变吗" |
第二行是关键:做完这一步,MACD 已经不知道价格是 3000 还是 30000 了。
它只知道两条均线拉开了多少。这也解释了为什么不同品种的 DIF 数值不能横向比大小——和 ATR 的绝对值不能跨品种比是同一个道理。
MACD 离价格隔着两层。两层之外的事,它一概不知道。
12、26、9 是哪来的?
没有公开的推导。 Appel 本人没有给出数学论证,后来也没有人补上。
流传最广的说法是"12 ≈ 两周、26 ≈ 一个月、9 ≈ 一周半"。这个口诀你可以自己验一下:按一周五个交易日算,12 天是 2.4 周、26 天是 5.2 周,哪个都对不齐。
它要能对齐,得假设一周有六个交易日——而美股结束周六交易,比 MACD 被提出来还早了二十多年。这更像是后人补上去的记忆口诀,不是当初的设计依据。
真正该记住的是另一件事:
这三个数,没有一个是为国内期货定的,更没有一个是为你做的那个品种定的。
那该不该改?
这里和 ATR 不一样。ATR 那篇说过"别在参数上花太多时间",因为 ATR 测的是波动率,而波动率本身有很强的持续性,12 和 20 算出来趋势一致。
MACD 没有这个性质。 它测的是差值,而差值会把参数的变化放大——12/26 改成 10/22,交叉点的位置可能整片挪动。
但也正因为它敏感,调参数是最容易滑进"拟合"的地方:你在历史图上把参数拧到金叉刚好都对上,这件事永远做得到,而它什么也证明不了。一个在历史上调出来的参数,和一个能赚钱的参数,中间隔着的东西比这个参数本身大得多。
我的建议:先用默认值,把下面三个坑填了再谈调参。绝大多数人的 MACD 出问题,不是因为参数不对,是因为喂给它的价格序列不对。
三、三个实现细节坑
① 柱状图,国内乘 2,国外不乘
国内绝大多数行情软件画的柱是 2 × (DIF − DEA),而国外的主流实现普遍不乘这个 2。
同一天、同一个品种,两边的柱子高度差整整一倍。
这不影响交叉的位置(乘个正数不改变正负号),但只要你用到柱子的高度——"柱子缩短了三分之一""柱高突破前高"——就必须先问清楚对方用的是哪种画法。
② 价格口径:收盘价还是结算价
这是期货独有的一个坑,股票上根本不存在。
期货每天有两个"收官价格":收盘价是最后一笔成交,结算价是全天成交量加权均价,两者的定义与差别在结算那一篇里讲过。而不同数据源、不同软件默认给你哪一个,并不统一。
按四象限那篇定下的规矩:口径要按"你在测什么"选。MACD 测的是价格序列自身的走势形态,该用收盘价——结算价是一串日均价,拿它算出来的序列会比真实价格平滑,交叉点会跟着挪。
差多少?我把两种口径各跑了一遍,数字在第五节末尾。
③ EMA 从哪一根开始算
EMA 是递推的,第一个值怎么定,后面全跟着变。不同库的默认行为不一样(有的从第一根开始递推,有的先取一段简单平均当种子)。
差异在开头几十根 K 线上明显,之后收敛——这一条和 ATR 那篇的实现细节是同一回事。
但请注意它在期货上会变得更要命,原因见第五节 ④:期货的单一合约,热起来的那段日子本身就没有多长,收敛所需的那段预热,要从这段日子里扣。
四、它能干什么(两个用法,都很克制)
用法一:当减速探测器,不当方向指示器
柱子由长变短,说明趋势在减速。这是 MACD 唯一真正擅长的事。
它的正确用途是提醒你去检查——你手上那个顺势仓位,推动它的那股劲儿正在退。至于要不要减仓、要不要出,那是仓位管理的事,不是 MACD 的事。
它不告诉你反手。 减速之后可能是横盘、可能是再加速、也可能是反转,三者在柱子上早期长得一模一样。
用法二:当过滤器,不当信号
比如:只在 DIF 位于零轴上方时考虑多头方向的入场,零轴下方只考虑空头方向。
注意这里 MACD 的角色——它没有给你入场点,它只是把一半的方向划掉了。入场点得由别的东西给(价格结构、突破、事件)。
这两个用法,没有一个会产生入场点。 如果你的系统里 MACD 是那个"扣扳机"的东西,那你用错它了。
五、实测:期货上多出来的三处麻烦
怎么测的
取成交额前 8 的品种——沪银、沪金、原油、棕榈油、天然橡胶、沪铜、锡、焦煤。品种池是事前定死的(直接沿用上一篇涨跌停实测排好的成交额名次),不因为"某个品种接缝大"而临时加人。
- 样本:699 份单一合约 / 12792 个合约日 / 331 次主力换月,2020 年 1 月起
- 两条线:A = 未调整的主连(该跳就跳,你在软件里看到的那条);B = 把每次接缝的价差减掉之后的主连
- 价格用收盘价,不用结算价
⚠️ 先说清这不是什么:这里没有回测、没有成本假设、没有仓位规则。下面所有数字都不回答"MACD 赚不赚钱",只回答"你手里那条线干不干净"。
① 接缝在 MACD 里住多久:这一条不用测
先说一个我原本准备实测、结果发现根本不需要测的事。
A 和 B 只差一个"从换月那天起整体平移一段"的动作。而 EMA 是线性的,于是两条线的 DIF 之差,恰好等于接缝自己的 MACD——里面一点价格信息都没有,只剩 12 和 26 这两个参数。
所以"污染多久散掉"这个问题,推导就能答尽:
| 换月之后 | DIF 上还残留着接缝价差的…… |
|---|---|
| 第 1 个交易日 | 8.0% |
| 第 8 个交易日 | 27.8%(峰值) |
| 第 15 个交易日 | 22.3% |
| 第 28 个交易日 | 才降到 10% 以下 |
| 第 38 个交易日 | 才降到 5% 以下 |
注意峰值不在换月当天,在换月之后第 8 个交易日——快慢两条 EMA 消化这个跳的速度不一样,差距要过一周多才拉到最大。
这张表对所有品种、所有行情都成立,因为它压根不含行情。我拿真实数据里那些"距上一次换月超过 60 个交易日"的孤立接缝验了一遍,实测第 8 日 27.7%,理论 27.8%——逐点重合。
换月这件事,在价格上是一天,在 MACD 上是两个月。
② 接缝有多大:这个才要测,而且分两种品种
| 品种 | 接缝价差中位 | 主力任期中位 |
|---|---|---|
| 焦煤 | 7.22% | 80 个交易日 |
| 棕榈油 | 4.32% | 81 |
| 天然橡胶 | 1.50% | 82 |
| 原油 | 0.71% | 20 |
| 沪银 | 0.34% | 46 |
| 沪金 | 0.27% | 42 |
| 锡 | 0.16% | 20 |
| 沪铜 | 0.13% | 21 |
这张表分成了泾渭分明的两组,而且恰好是两条不同的中毒路径:
- 焦煤、棕榈油、橡胶:主力月只有那么两三个(1/5/9 这一类),一换就跨四个月,接缝大——焦煤中位数就 7.22%,样本里最大的一次是 2022 年 12 月从 jm2301 换到 jm2305,−18.98%。好处是八十来个交易日才换一次,够散干净
- 沪铜、锡、原油:月月都有合约,接缝很小(铜的中位数只有 0.13%)。但它们每二十来个交易日就换一次——而上面那张表说,散干净要 38 个交易日
第二种情况值得单独说一句。全样本的主力换月间隔中位数是 22 个交易日,73.1% 的间隔短于 40 个交易日。
对换月频繁的品种,上一次接缝还没散干净,下一次已经来了。 那条 DIF 从来没有回到过干净状态。
③ 那到底有没有影响:两个数
前面两条都是"输入"。真正要回答的是:这堆污染,改掉我看到的东西了吗?
第一个数,污染相对 DIF 自身有多大(不是相对价格,是相对这条线本身的幅度):
| 中位数 | 7.7% |
| 超过 10% 的交易日 | 44.7% |
| 超过 25% 的交易日 | 26.7% |
| 超过 50% 的交易日 | 15.8% |
| 超过 100% 的交易日 | 8.7% |
最后一行的意思是:有 8.7% 的交易日,接缝带来的偏移比 DIF 本身还大。
第二个数,也是最终答案——信号真的被改掉了多少:
| 品种 | 脏线信号 | 净线信号 | 对不上 | 错配率 |
|---|---|---|---|---|
| 沪银 | 108 | 110 | 6 | 2.8% |
| 沪金 | 126 | 126 | 8 | 3.2% |
| 沪铜 | 120 | 114 | 16 | 6.8% |
| 天然橡胶 | 112 | 124 | 18 | 7.6% |
| 锡 | 130 | 136 | 24 | 9.0% |
| 原油 | 121 | 127 | 26 | 10.5% |
| 棕榈油 | 116 | 118 | 36 | 15.4% |
| 焦煤 | 117 | 115 | 50 | 21.6% |
| 合计 | 950 | 970 | 184 | 9.6% |
"对不上"分两种:接缝凭空造出来的信号(真实价格里没有),和被接缝抹掉的信号(真实价格里有、主连上没有)。两种各占一半。
每十个金叉死叉里,大约有一个是接缝的产物,或者被接缝吃掉了。
注意错配率并不是简单跟着接缝大小走。 焦煤接缝最大(7.22%)、错配率也最高(21.6%),这符合直觉;但沪铜接缝只有 0.13%、错配率仍有 6.8%,锡 0.16% 对 9.0%——因为它们换得太勤,小接缝乘上次数,一样能改掉信号。
大接缝一次到位,小接缝细水长流,两条路通向同一个地方。
④ 那换成单一合约呢:躲开接缝,撞上寿命
既然主连脏,直接在你要交易的那份合约上算 MACD——这是对的做法,但它有自己的代价。
MACD 要 26 + 9 = 35 个交易日才算预热完(在此之前 DEA 还没稳定)。而:
- 主力任期中位数:22 个交易日
- 合约活跃期中位数:37 个交易日(活跃期 = 持仓量达到它自己峰值一半以上的那段日子)
- 42.9% 的合约,活跃期短于 35 个交易日
| 品种 | 活跃期中位 | vs 35 天预热期 |
|---|---|---|
| 锡 | 26 | 不够 |
| 原油 | 28 | 不够 |
| 沪铜 | 39 | 刚够 |
| 沪银 | 49 | 够 |
| 沪金 | 58 | 够 |
| 棕榈油 | 78 | 够 |
| 焦煤 | 79 | 够 |
| 天然橡胶 | 82 | 够 |
(样本 289 份合约,两头被样本期截断的已全部剔除——不剔的话活跃期会被系统性算短。)
你当然可以从这份合约挂牌第一天就开始算 MACD,那样预热绰绰有余。但挂牌初期它是一份没什么人交易的冷清合约,那段价格本身就是噪音,临近到期又会迅速塌掉。你拿噪音去喂一个有记忆的指标,它会老老实实把噪音记住。
于是就有了这个两难:
主连不干净,单合约不够长。在期货上,你没有一条既真实又足够长的价格序列可以拿来算 MACD。
这不是让你别用,是让你知道自己站在哪一边。 我的选择是:在单一合约上算,接受前 35 个交易日的信号不看;如果非要用主连,就先把接缝减掉(代码见第七节)。
⑤ 滞后的量级:同样的迟到,期货上要乘杠杆
MACD 滞后是常识,不需要证明,但量级值得标定一次。
在干净序列上找出所有事后确认的局部低点(前后各 10 个交易日最低),看第一个金叉什么时候来(748 个样本):
| 金叉比低点晚 | 5 个交易日(中位数) |
| 信号发出时,价格已经走了 | 3.94%(中位数) |
| 折成账户读数(5 倍真实杠杆) | 19.7% |
⚠️ 局部低点是事后定义的,你在当下不知道那是低点。这组数只用来度量"滞后有多大",不构成任何可交易的规则。
最后一行才是期货独有的那部分。3.94% 在一个不带杠杆的账户上是 3.94%;而账户的波动约等于价格波动乘以你的真实杠杆,5 倍下就是接近两成。
滞后本身股票也有。期货多出来的是那个乘号。
附带:换个价格口径,信号又变一批
同一条干净主连,价格从收盘价换成结算价,其余一个字不改:
970 个信号 vs 920 个信号,对不上 130 个,错配率 6.9%。
比接缝那 9.6% 小一些,但同样是白送的误差——而它只取决于你的数据源默认给了你哪一列。
这批数据打几折
- 它不回答 MACD 赚不赚钱。 没有成本、没有仓位、没有出场规则,本篇一次回测都没做。"信号被改掉了 9.6%"和"这个策略行不行"是两个问题
- 主力判定用了前视信息(防抖要看后面三天),实盘做不到。这里只拿它把接缝位置定准,不是可交易规则
- 只有 8 个品种,而且是成交额最靠前的一档。 冷清品种的接缝只会更难看,分母小、一个大户挪仓就能翻倍
- 日线答不了日内。 60 分钟、15 分钟的 MACD 会怎样,这批数据一个字都说不了
- "净主连"只是一种拼接法(等差价差调整)。哪种拼接更好是另一个题目,本篇没碰——但用任何一种,都比不减强
六、失效场景(请比第四节读得更用力)
① 震荡市里,它会一直给信号,而且每个都讲得通
这是老手都见过的:横盘里 MACD 反复金叉死叉,来回打脸。我不打算用数据去"证明"一件从业者天天看见的事,只提醒一句它为什么危险——
不是因为信号多,是因为每一个信号事后都能讲出一套道理。用四象限那篇里的话说,讲得通,正是这类读法最危险的地方。
② 背离:只能事后确认
"价格创新低但 MACD 没创新低,是底背离"——这句话本身没错,错的是它的可用性。
在一个底背离成为底背离之前,它和"下跌中继"长得完全一样。你只有等到价格真的转上去,才能回头说"那是背离"。
一个只能在事后被认出来的形态,不能拿来在事前做决策。
③ 它的参数不是为你定的(承接第二节)
12/26/9 来自 1970 年代的美股。你做的可能是一个 2023 年才上市、日内波动是美股几倍的品种。这三个数没有任何理由恰好适合它。
④ 信号需要成交才能兑现
MACD 死叉让你出场,但如果价格封在跌停板上,你出不去。这一条在涨跌停那篇里已经讲透,ATR 那篇也从止损角度说过,这里只挂链,不重复。
指标画在屏幕上,成交发生在盘口上。这两件事之间没有必然联系。
⑤ 它不回答仓位——而在期货上,这一条能要命
MACD 就算全对,也只告诉你方向和时机,一个字都没说该开多少手。
而期货是带杠杆的。同样一次判断失误,在不同仓位下是完全不同的两个结果。第五节 ⑤ 那组数字讲的正是这件事:同样的滞后,在股票上是几个点,在期货上要乘以你的真实杠杆。
⑥ 就算某个信号真有统计优势,那也不等于能交易
概率不是收益、没扣手续费、没算滑点——这一整套账在四象限那篇里算过一遍,对 MACD 同样适用,不再重述。
七、代码
老规矩,计算逻辑写成纯函数,不绑定任何数据源。有一列按时间升序的收盘价就能跑。
import pandas as pd
def macd(close: pd.Series, fast: int = 12, slow: int = 26, signal: int = 9,
hist_x2: bool = True) -> pd.DataFrame:
"""DIF / DEA / 柱。
hist_x2: 柱要不要乘 2。国内软件普遍乘,国外普遍不乘 —— 见第三节 ①。
它不改变交叉位置,但只要你用到柱高就必须对齐口径。
"""
dif = (close.ewm(span=fast, adjust=False).mean()
- close.ewm(span=slow, adjust=False).mean())
dea = dif.ewm(span=signal, adjust=False).mean()
hist = (dif - dea) * (2 if hist_x2 else 1)
return pd.DataFrame({"dif": dif, "dea": dea, "hist": hist})
def crosses(m: pd.DataFrame) -> pd.Series:
"""+1 金叉、−1 死叉、0 没事。"""
d = m["dif"] - m["dea"]
return ((d > 0) & (d.shift(1) <= 0)).astype(int) \
- ((d < 0) & (d.shift(1) >= 0)).astype(int)
下面这个才是本篇最该带走的函数——把接缝抹掉:
def splice_adjust(px: pd.Series, switch_days, gaps) -> pd.Series:
"""等差价差调整:把换月造成的那一跳从序列里减掉。
switch_days : 每次换月生效的那一天(新主力的第一天)
gaps : 对应的价差 = 切换前一日「新主力收盘 − 旧主力收盘」
⚠️ 这只是**一种**拼接法。哪种更好是另一个题目,本篇不碰。
但用任何一种,都比直接拿未调整的主连算 MACD 强。
"""
adj = pd.Series(0.0, index=px.index)
for day, g in zip(switch_days, gaps):
adj.loc[day:] += g # 从换月那天起,之后全部平移
return px - adj
⚠️ 拿不到各合约明细、算不出 gap 怎么办? 那就别在主连上算 MACD,直接在你正要交易的那份合约上算——代价是第五节 ④ 那个预热问题,但至少序列是真的。 两害相权,请选真的那个。
八、三个你该自己跑一遍的实验
实验一:把两条 DIF 画在一起
同一个品种,一条用未调整主连、一条用价差调整后的主连,把两条 DIF 画在一张图上,在横轴上标出换月日。
你会看到:每个换月日之后,两条线分开,然后用一两个月慢慢合拢。这张图是本篇最值得自己做一遍的东西——做完你很难再直接相信软件里那根主连 MACD。
实验二:数一数信号差几个
把两条线上的金叉死叉各列一张表,对一遍。
你要看的:有几个信号只出现在未调整的那条线上(接缝造出来的),有几个只出现在调整后的那条线上(被接缝抹掉的)。
实验三:把收盘价换成结算价,再跑一遍
一个字都不用改逻辑,只换价格列。
你要看的:交叉点挪了几天、总数差了几个。这个实验和四象限那篇的实验三是同一个动作——同一份数据、同一段代码,换一个口径就换一批结论,这件事值得你亲手撞一次。
九、三句话结论
1. MACD 测的是加速度,不是方向。 柱子变短说明推力在退,不说明要掉头。把"减速"读成"反转",是它最贵的一种误用。
2. 在期货上,先确认序列,再谈信号。 主连有接缝、单合约有寿命——两条路都不干净,你至少要知道自己走的是哪一条、脏在哪儿。
3. 它不扣扳机。 MACD 最多帮你划掉一半方向、提醒你趋势在减速。入场、止损、仓位,一个都不归它管——而在期货上,最后那个才是决定你能不能活下来的。
下一篇:《一个品种一张图 · 多晶硅》——光伏这条产业链上,中美两边各自在做什么,以及它落到那张合约上隔着几道工序。
⚠️ 风险提示 本文仅为期货知识分享与研究交流,不构成任何投资建议。文中所有指标参数、算例与代码均为原理演示,不构成任何交易参数或交易信号推荐,历史数据与统计结果不代表未来表现。期货交易具有高杠杆特性,可能导致本金全部损失甚至产生穿仓负债。文中涉及的合约规则、涨跌停等信息以各期货交易所官方最新公告为准。请通过正规期货公司开户,理性参与、独立决策、风险自担。
