反转还是假象?华尔街奇才的神秘指标:Python实战神奇九转(含代码)量化小白必看

平时炒股的时候,你是不是也常有这种感觉: 明明趋势挺猛的,突然就反转了;或者跌得心惊胆战,正想割肉,它却莫名反弹了...
这种"踩不准点"的郁闷,我相信绝大多数散户都经历过。 而量化的本质,其实就是想办法用规则把这种模糊感量化出来。
那么,有没有一种方法,能帮我们大概判断------"当前的上涨/下跌,是不是差不多该到头了?" 答案是:有!这就是我们今天要聊的主角------神奇九转(TD Sequential)。
神奇九转是谁提出的?
神奇九转并不是国内股民发明的"土法宝",它的鼻祖是美国投资大师 Tom DeMark 。 Tom DeMark 被称为"华尔街的技术分析奇才",几十年来专注于研究价格节奏、趋势耗竭的量化方法。 他的 TD Sequential 方法体系,后来被交易员们昵称为"神奇九转十三转",其中最有名的就是 "九转"(Setup 阶段)。
简单说,它的逻辑就是:
- 底部九转(Buy Setup) :从某根K线开始,连续9根收盘价都低于各自4根K线前的收盘价,计数从1记到9。第9根出现时,往往被视作"短期下跌动能衰竭、可能反弹"的信号(不是必涨,只是胜率/赔率结构更友好)。
- 顶部九转(Sell Setup) :相反,连续9根收盘价都高于各自4根K线前的收盘价。
是不是有点意思? 在国内,很多技术派交易者都喜欢用"底部九转"来寻找潜在的抄底信号,用"顶部九转"来预警高位风险。
九转的计算逻辑(重点)
以底部九转为例(卖出信号同理取反):
-
比较对象 :第 i 根K线与 i-4 根K线的收盘价比较。
- 若
Close[i] < Close[i-4]
,则底部计数 +1 ;否则计数被打断、重新等待新的起算点。
- 若
-
连续性 :需要连续满足上面的条件,计数才能从 1 → 9。
-
起算/重置:
- 当任一根不满足
C[i] < C[i-4]
,底部计数就归零 ,等待下一次满足时从1重新开始。 - 顶部同理,只是换成
C[i] > C[i-4]
。
- 当任一根不满足
-
完美(Perfection,可选):
- Buy Setup 的"完美9"常见定义:第8或第9根的最低价 < 第6和第7根的最低价。这被认为信号更扎实(但也更少出现)。
-
并行计数 :买、卖 Setup 可以并行计算(互不影响)。
-
边界 :由于要和
i-4
比,前4根无法计数;另外若数据小于 30~60 根,信号不稳,尽量用足够长的历史。
上面的内容大家看了可能比较晕,这里用一个具体的例子来说明,加深下大家的理解:
九转计算逻辑举例
假设我们有一组收盘价序列(按时间顺序):
序号 | 收盘价 |
---|---|
1 | 10.0 |
2 | 10.2 |
3 | 10.5 |
4 | 10.3 |
5 | 10.1 |
6 | 9.9 |
7 | 9.7 |
8 | 9.6 |
9 | 9.5 |
10 | 9.4 |
11 | 9.2 |
12 | 9.0 |
13 | 8.9 |
现在我们来算"底部九转":
- 从第5根开始 ,才能和第1根比较:
- 第5根收盘 10.1 vs 第1根收盘 10.0 → 10.1 > 10.0,不满足条件,所以计数=0。
- 第6根:9.9 vs 第2根 10.2 → 9.9 < 10.2,满足 → 计数=1。
- 第7根:9.7 vs 第3根 10.5 → 9.7 < 10.5,满足 → 计数=2。
- 第8根:9.6 vs 第4根 10.3 → 9.6 < 10.3,满足 → 计数=3。
- 第9根:9.5 vs 第5根 10.1 → 9.5 < 10.1,满足 → 计数=4。
- 第10根:9.4 vs 第6根 9.9 → 9.4 < 9.9,满足 → 计数=5。
- 第11根:9.2 vs 第7根 9.7 → 9.2 < 9.7,满足 → 计数=6。
- 第12根:9.0 vs 第8根 9.6 → 9.0 < 9.6,满足 → 计数=7。
- 第13根:8.9 vs 第9根 9.5 → 8.9 < 9.5,满足 → 计数=8。
👉 如果再往下还有一根满足条件,那就是第9次,计数就能到9,形成底部九转完成。
直觉总结 :九转寻找的是"与4根前相比持续走弱/走强"的节奏,一旦完成9根,短期趋势可能 衰竭,容易出现反向波动(而不是一定反转)。实战里我更倾向把它当作择时辅助而不是单独开仓依据。
Python实现神奇九转
接下来我们要实现的功能有:
- 计算 Buy/Sell Setup 的 1~9 计数(含可选"完美9"判断)。
- 画K线并把 1~9 的数字标注到对应蜡烛上,9 的位置高亮。
- 批量扫描 一篮子股票,找出收盘处于"底部9转"(可选是否要求"完美9")的标的。
数据源:我用 xtquant+miniQMT 获取 A 股日线,绘图用 mplfinance。如果你有其它数据源,只需把取数函数替换掉。
首先是九转的核心算法,传入包含列: ['open','high','low','close'] 且按时间升序排列的DataFrame,就会返回
td_buy_count
: 底部九转计数(0~9)td_sell_count
: 顶部九转计数(0~9)td_buy_9
: 是否在该bar触发底部9td_sell_9
: 是否在该bar触发顶部9td_buy_perfect9
: 是否买setup完美9td_sell_perfect9
: 是否卖setup完美9
python
def compute_td_setup(df: pd.DataFrame):
df = df.copy()
n = len(df)
buy_count = np.zeros(n, dtype=int)
sell_count = np.zeros(n, dtype=int)
td_buy_9 = np.zeros(n, dtype=bool)
td_sell_9 = np.zeros(n, dtype=bool)
td_buy_perfect9 = np.zeros(n, dtype=bool)
td_sell_perfect9 = np.zeros(n, dtype=bool)
close = df['close'].values
high = df['high'].values
low = df['low'].values
for i in range(n):
# 需要与 i-4 比较
if i >= 4:
# 底部九转:收盘 < 4根前收盘
if close[i] < close[i-4]:
buy_count[i] = buy_count[i-1] + 1 if i > 0 else 1
else:
buy_count[i] = 0
# 顶部九转:收盘 > 4根前收盘
if close[i] > close[i-4]:
sell_count[i] = sell_count[i-1] + 1 if i > 0 else 1
else:
sell_count[i] = 0
else:
buy_count[i] = 0
sell_count[i] = 0
# 标记到9
if buy_count[i] == 9:
td_buy_9[i] = True
# 完美9(可选规则):第8或第9根的最低价 < min(第6, 第7根的最低价)
# 注意索引换算:当前i是第9,i-1是第8;i-3第6,i-2第7
if i >= 7: # 至少要有第6、7、8根
cond = (low[i-1] < min(low[i-3], low[i-2])) or (low[i] < min(low[i-3], low[i-2]))
td_buy_perfect9[i] = bool(cond)
if sell_count[i] == 9:
td_sell_9[i] = True
# 卖setup完美9:第8或第9根的最高价 > max(第6, 第7根的最高价)
if i >= 7:
cond = (high[i-1] > max(high[i-3], high[i-2])) or (high[i] > max(high[i-3], high[i-2]))
td_sell_perfect9[i] = bool(cond)
df['td_buy_count'] = buy_count
df['td_sell_count'] = sell_count
df['td_buy_9'] = td_buy_9
df['td_sell_9'] = td_sell_9
df['td_buy_perfect9'] = td_buy_perfect9
df['td_sell_perfect9'] = td_sell_perfect9
return df
接下来是绘图功能
python
def plot_kline_with_td_counts(df, title=""):
"""
在K线上标注 td_buy_count / td_sell_count
"""
df = df.copy()
# 自定义红涨绿跌的颜色风格
my_style = mpf.make_mpf_style(
base_mpf_style='charles', # 继承基础风格
marketcolors=mpf.make_marketcolors(
up='red', # 涨:红色
down='green', # 跌:绿色
edge='inherit', # K线边框颜色(inherit 表示继承 up/down 颜色)
wick='inherit', # 上下影线颜色
volume='inherit' # 成交量颜色
),
rc={'font.family': 'SimHei'}
)
# mplfinance 绘图并返回 fig 和 axes
fig, axes = mpf.plot(
df, type='candle', mav=(5,10,20), volume=True,
style=my_style, title=title, figratio=(16,9), figscale=1.1,
tight_layout=True, returnfig=True
)
ax = axes[0] # 主图
# 用整数索引代替 x 轴
x_vals = np.arange(len(df))
for i, row in enumerate(df.itertuples()):
if row.td_buy_count > 0:
ax.text(
x_vals[i], row.low*0.995, str(int(row.td_buy_count)),
ha='center', va='top', fontsize=8, color='green'
)
if row.td_sell_count > 0:
ax.text(
x_vals[i], row.high*1.005, str(int(row.td_sell_count)),
ha='center', va='bottom', fontsize=8, color='red'
)
plt.show()

接下来我们通过xtdata.get_stock_list_in_sector('沪深A股')
获取全部股票列表,通过一个循环就可以查找出谁目前处于九转的位置了。
怎么用 & 常见坑
- 优先看"位置" :底部9如果叠加在重要支撑/趋势线附近、或大级别均线支撑位,胜率更好;若在单边暴跌途中连环出现多个9,别盲冲(我会用量能/波动过滤)。
- 完美9 :加入"完美9"条件能减少假信号,但会明显减少机会,需要权衡。
- 多周期共振 :日线出现底9,如果60分钟也出现过底9,我会更重视。
- 别把 Setup 当"反转锤" :九转更多是衰减 而非反转本身,实操里最好配合入场/出场的触发(比如突破前高、ATR 回撤止损等)。
- 数据一致性 :是否复权、数据源口径差异,会影响是否"正好等于9"。策略上线前,固定口径并回溯验证。
- 风控优先 :我个人更喜欢把九转作为择时+仓位调整的工具,而不是孤立的买点;止损/仓位管理要先定好再交易 🎯
可进一步改造的方向(给进阶读者)
- 加"倒计时 13" :在 9 之后统计 13 个满足
收盘 <= 前两根最低
的 bar(买方向),形成 TD Sequential 的"倒计时"部分,用于捕捉更完全的耗竭信号。 - 信号打分:把完美9、是否位于布林带下轨、是否缩量、是否在周线支撑等要素打分,筛选"强信号九转"。
- 事件驱动过滤:财报/分红/大宗交易等事件期间的九转,单独建样本组。
- 可视化优化:把 1~9 的数字逐根打印在蜡烛旁(matplotlib annotate),并在第9根背景着色,阅读体验更好 😎
最后我的观点
- 九转是"节奏工具",不是"圣杯"。它在"趋势末端的节奏衰竭"上有独特价值,但必须和结构/量价/风控配合。
- 回测是关键:任何市场、任何品种、任何数据口径,回测一遍再上线,否则就是用感觉在交易。
- 简单能跑,胜过复杂不落地。先把上面这套跑起来,再按你的交易体系去加规则,边测边迭代 🚀