|
|
一、神奇九转概念
神奇九转并不是国内股民发明的“土法宝”,它的鼻祖是美国投资大师 Tom DeMark。 Tom DeMark 被称为“华尔街的技术分析奇才”,几十年来专注于研究价格节奏、趋势耗竭的量化方法。 他的 TD Sequential 方法体系,后来被交易员们昵称为“神奇九转十三转”,其中最有名的就是 “九转”(Setup 阶段)。
简单说,它的逻辑就是:
底部九转(Buy Setup):从某根K线开始,连续9根收盘价都低于各自4根K线前的收盘价,计数从1记到9。第9根出现时,往往被视作“短期下跌动能衰竭、可能反弹”的信号(不是必涨,只是胜率/赔率结构更友好)。
顶部九转(Sell Setup):相反,连续9根收盘价都高于各自4根K线前的收盘价。
二、九转的计算逻辑(重点)
以底部九转为例(卖出信号同理取反):
1.比较对象:第 i 根K线与 i-4 根K线的收盘价比较。
若 Close[i] < Close[i-4],则底部计数 +1;否则计数被打断、重新等待新的起算点。
2.连续性:需要连续满足上面的条件,计数才能从 1 → 9。
3.起算/重置:
当任一根不满足 C[i] < C[i-4],底部计数就归零,等待下一次满足时从1重新开始。
顶部同理,只是换成 C[i] > C[i-4]。
4.完美(Perfection,可选):
Buy Setup 的“完美9”常见定义:第8或第9根的最低价 < 第6和第7根的最低价。这被认为信号更扎实(但也更少出现)。
5.并行计数:买、卖 Setup 可以并行计算(互不影响)。
6.边界:由于要和 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,形成底部九转完成。
三、Python实现神奇九转
from xtquant import xtdata
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import mplfinance as mpf
def get_hq(code, start_date='19900101', period='1d', dividend_type='front_ratio', count=-1):
'''
基于xtquant下载某个股票的历史行情
盘中运行最后一个K里存了最新的行情
period 1d 1w 1mon
dividend_type - 除权方式,用于K线数据复权计算,对tick等其他周期数据无效
none 不复权
front 前复权
back 后复权
front_ratio 等比前复权
back_ratio 等比后复权
'''
xtdata.enable_hello = False
xtdata.download_history_data(stock_code=code, period='1d', incrementally=True)
history_data = xtdata.get_market_data_ex(
['open', 'high', 'low', 'close', 'volume', 'amount', 'preClose', 'suspendFlag'], [code], period=period,
start_time=start_date, count=count, dividend_type=dividend_type)
print(history_data)
df = history_data[code]
df.index = pd.to_datetime(df.index.astype(str), format='%Y%m%d')
df['date'] = df.index
return df
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
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()
if __name__ == "__main__":
df = get_hq('002627.SZ', start_date='20220101', period='1d', dividend_type='front_ratio', count=200)
df = compute_td_setup(df)
result = plot_kline_with_td_counts(df, title="")
print(result)
四、怎么用 & 常见坑
(1)优先看“位置”:底部9如果叠加在重要支撑/趋势线附近、或大级别均线支撑位,胜率更好;若在单边暴跌途中连环出现多个9,别盲冲(我会用量能/波动过滤)。
(2)完美9:加入“完美9”条件能减少假信号,但会明显减少机会,需要权衡。
(3)多周期共振:日线出现底9,如果60分钟也出现过底9,我会更重视。
(4)别把 Setup 当“反转锤”:九转更多是衰减而非反转本身,实操里最好配合入场/出场的触发(比如突破前高、ATR 回撤止损等)。
(5)数据一致性:是否复权、数据源口径差异,会影响是否“正好等于9”。策略上线前,固定口径并回溯验证。
(6)风控优先:我个人更喜欢把九转作为择时+仓位调整的工具,而不是孤立的买点;止损/仓位管理要先定好再交易
五、可进一步改造的方向(给进阶读者)
(1)加“倒计时 13”:在 9 之后统计 13 个满足 收盘 <= 前两根最低 的 bar(买方向),形成 TD Sequential 的“倒计时”部分,用于捕捉更完全的耗竭信号。
(2)信号打分:把完美9、是否位于布林带下轨、是否缩量、是否在周线支撑等要素打分,筛选“强信号九转”。
(3)事件驱动过滤:财报/分红/大宗交易等事件期间的九转,单独建样本组。
(4)可视化优化:把 1~9 的数字逐根打印在蜡烛旁(matplotlib annotate),并在第9根背景着色,阅读体验更好
|
|