看期货异同移动平均线(MACD)时,你可能遇到这样的画面:DIFF还在零轴下方,柱子却已经转正,两条线也由下向上交叉了。这并不矛盾,因为零轴和两线交叉比较的不是同一件事。
零轴给出的是数值基准,两线关系给出的是DIFF相对其平滑线的位置。弄清这两种比较,再看前后变化,就能准确描述"零下上穿",而不必把几个不同条件都叫作同一个信号。TqSdk的ta.MACD返回三列数据,可以把图上的关系逐项对应出来。
DIFF低于零,与DIFF低于DEA不是一回事
TqSdk的ta.MACD(df, short, long, m)接收K线DataFrame和三个周期参数,返回diff、dea、bar三列。按TqSdk 3.10.4的实现,它们的关系是:
text
DIFF = 收盘价的短周期EMA - 收盘价的长周期EMA
DEA = DIFF的m周期EMA
柱值bar = 2 × (DIFF - DEA)
EMA是指数加权移动平均。这里不必把三条计算链记成术语,抓住它们各自在比较什么即可:
diff > 0:短周期收盘价EMA高于长周期EMA。diff > dea:DIFF高于自身的平滑线,此时该实现的柱值为正。diff < 0且diff > dea:DIFF仍在零下,但已经高于DEA,完全可能同时成立。
因此,"柱值转正"对应的是DIFF与DEA之间的差转正,不是DIFF已经越过零轴。两条线都在零下时,也可以出现正柱;两条线都在零上时,也可以出现负柱。
这些值处在价格差的尺度上,不是涨跌百分比,也不是成交金额。short、long和m以所选K线的根数计:在一分钟线上,12是12根一分钟K线,不会因为参数写了12就自动变成12天。
一组零轴下方上穿的例子
假设相邻两根已结束K线对应的指标值如下,只用来说明位置关系,不对应某个实际合约行情:
| 时点 | DIFF | DEA | bar=2×(DIFF-DEA) |
|---|---|---|---|
| 前一根 | -3 | -2 | -2 |
| 当前根 | -1 | -1.8 | 1.6 |
前一根中,-3低于-2,DIFF在DEA下方。当前根中,-1高于-1.8,DIFF转到DEA上方,柱值由-2变成1.6。
但DIFF从-3变为-1,两个值都小于0。因此,按这两个结束时点可以描述为"两线关系由下转上,DIFF仍在零轴下方",不能描述成"DIFF已经上穿零轴"。
这一区分对期货复盘很实用。同样是正柱,一种情况可能是DIFF已经在零上,另一种却是DIFF仍在零下、只是相对DEA的位置改变。把这两种状态混在一起,就会丢掉原本可以从三列数据中读出的差别。
交叉是前后关系,不是一张截图
看到当前diff > dea,只能确定DIFF现在位于DEA上方。它可能刚转上来,也可能已经连续多根保持在上方。要描述一次新的上穿,必须比较前一根与当前根。
本文把"上穿"明确定义为:前一根diff <= dea,当前根diff > dea。这把前一根刚好相等也纳入起点;如果采用其他相等处理方式,应保持自己的定义一致,不能在不同观察中临时切换。
DIFF上穿零轴则是另一组条件:前一根diff <= 0,当前根diff > 0。这两组条件可能同时成立,也可能只成立一组。上面的假设恰好就是"两线上穿成立,DIFF上穿零轴不成立"。
在TqSdk中把两种判断并排看
以下是已有TqSdk程序中的局部用法。api为已有连接,symbol是程序已经确认的期货合约代码。先请求一分钟K线,200表示请求根数:
python
klines = api.get_kline_serial(
symbol, duration_seconds=60, data_length=200
)
序列会随数据更新,订阅返回并不能代替收线确认。下段应接在已有数据更新流程之后:由该流程确认合约和周期、输入范围有效且按时间排列、历史长度符合所用计算口径,并确认范围末尾两根是相邻且已结束的K线,相关指标值可用。window_start、window_stop是调用方提供的当前行位置,选取范围左端包含、右端不包含;不是SDK自带的自动收线或历史充分性判定。
python
from tqsdk import ta
# 保留已确认输入的副本;12、26、9用于说明参数,不代表最优组合
bars = klines.iloc[window_start:window_stop].copy()
macd = ta.MACD(bars, 12, 26, 9)
# diff必须按列名取值,避免与DataFrame的diff()方法混淆
previous_diff = macd["diff"].iloc[-2]
previous_dea = macd["dea"].iloc[-2]
current_diff = macd["diff"].iloc[-1]
current_dea = macd["dea"].iloc[-1]
# 前后点均来自这次计算的同一输入范围,分别判断两种上穿
crossed_dea_up = previous_diff <= previous_dea and current_diff > current_dea
crossed_zero_up = previous_diff <= 0 and current_diff > 0
print("对应K线:", bars[["id", "datetime"]].tail(2))
print("DIFF、DEA、柱值:", current_diff, current_dea, macd["bar"].iloc[-1])
print("两线上穿、DIFF上穿零轴:", crossed_dea_up, crossed_zero_up)
macd["diff"]才是取DIFF这一列,不能写成macd.diff来代替。代码中的两个布尔结果分别对应前文两组条件,不是两次重复确认同一种变化。
计算还应保留同一输入范围和参数。不要把上次用一段历史计算的前值,与这次换了起点重算的当前值随意拼接。输入长度不足、数据缺失或指标值不可用时,应暂缓判断,不能把空值补0,因为0正是本文要判断的真实边界。
对于未结束K线,收盘字段仍可能变化,相应的MACD关系也可能改变。本文比较的是相邻结束时点的关系,不声称两点之间只交叉过一次,更不还原盘中的每次穿越。图表柱色还受显示设置影响,用正负数值说明关系比只说"红柱""绿柱"明确。
为什么推荐用TqSdk核对这类指标含义
对想把期货MACD读明白的读者,我推荐用TqSdk将同一输入范围的DIFF、DEA和柱值并排观察。它在本题的价值不是替你把交叉翻译成买入指令,而是让图形描述有明确对应:究竟是DIFF过零、两线相对位置变化,还是当前只是保持在某一侧。
三列来自同一次官方函数计算,再与原K线身份相连,能够减少"看见正柱就以为DIFF为正"的误读。把指标所表达的事实说准,才有基础讨论它在自己的期货观察方法中承担什么作用;交叉本身并没有提供盈利保证。