问题描述
解决方案
这是一个基于 QMT (Quantitative Trading) 平台的 TD 序列 (DeMark Sequential) 策略代码实现。
策略说明
TD 序列(TD Sequential)是由 Tom DeMark 发明的一种反趋势指标,用于识别价格趋势的衰竭点。本代码实现了最核心的 TD Setup (TD 结构) 部分:
- TD 买入结构 (TD Buy Setup 9):连续 9 根 K 线的收盘价都低于 4 根 K 线前的收盘价。通常视为下跌趋势衰竭,是买入信号。
- TD 卖出结构 (TD Sell Setup 9):连续 9 根 K 线的收盘价都高于 4 根 K 线前的收盘价。通常视为上涨趋势衰竭,是卖出信号。
QMT Python 代码实现
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
"""
策略初始化函数
"""
# 1. 设置资金账号 (请替换为您自己的资金账号)
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
ContextInfo.set_account(ContextInfo.account_id)
# 2. 设置股票池 (示例使用 沪深300ETF)
ContextInfo.stock_code = '510300.SH'
ContextInfo.set_universe([ContextInfo.stock_code])
# 3. 策略参数设置
ContextInfo.lookback = 4 # TD策略比较的位移周期 (通常为4)
ContextInfo.setup_len = 9 # TD结构需要的连续周期数 (通常为9)
# 4. 设置运行周期 (例如日线 '1d')
ContextInfo.period = '1d'
def handlebar(ContextInfo):
"""
K线逐根运行函数
"""
# 获取当前正在处理的股票代码
stock_code = ContextInfo.stock_code
# 获取当前K线索引
index = ContextInfo.barpos
# 确保有足够的数据进行计算 (9 + 4 = 13, 预留20根以防万一)
if index < 20:
return
# 1. 获取历史行情数据
# 我们需要获取足够长度的数据来判断当前的TD结构
# count = setup_len + lookback + buffer
data_count = ContextInfo.setup_len + ContextInfo.lookback + 5
market_data = ContextInfo.get_market_data_ex(
['close'],
[stock_code],
period=ContextInfo.period,
count=data_count,
dividend_type='front' # 前复权
)
if stock_code not in market_data:
return
df = market_data[stock_code]
# 再次检查数据长度
if len(df) < (ContextInfo.setup_len + ContextInfo.lookback):
return
# 获取收盘价序列 (转换为numpy数组提高效率)
closes = df['close'].values
# 2. 计算 TD 结构
# 我们需要检查最近的9根K线是否满足条件
is_buy_setup = True
is_sell_setup = True
# 循环检查最近9根K线 (从当前K线往前推)
# 逻辑:检查 Close[i] < Close[i-4] 是否对最近9根K线都成立
for i in range(ContextInfo.setup_len):
# 当前考察的K线索引 (倒数第1根是 -1, 倒数第2根是 -2...)
current_idx = -(i + 1)
# 用于比较的K线索引 (4根K线前)
compare_idx = -(i + 1 + ContextInfo.lookback)
current_close = closes[current_idx]
compare_close = closes[compare_idx]
# 检查买入结构条件: 收盘价 < 4根前的收盘价
if current_close >= compare_close:
is_buy_setup = False
# 检查卖出结构条件: 收盘价 > 4根前的收盘价
if current_close <= compare_close:
is_sell_setup = False
# 3. 交易逻辑
# 获取当前持仓
positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
current_vol = 0
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
current_vol = pos.m_nVolume
break
# 信号触发:TD 买入结构完成 (TD 9) -> 看涨/反弹
if is_buy_setup:
print(f"[{stock_code}] 时间: {df.index[-1]} - 触发 TD买入结构(9) - 准备买入")
# 示例:全仓买入 (目标仓位 100%)
# 注意:实盘请根据风控调整仓位
order_target_percent(stock_code, 1.0, ContextInfo, ContextInfo.account_id)
# 信号触发:TD 卖出结构完成 (TD 9) -> 看跌/回调
elif is_sell_setup:
print(f"[{stock_code}] 时间: {df.index[-1]} - 触发 TD卖出结构(9) - 准备卖出")
# 示例:清仓 (目标仓位 0%)
order_target_percent(stock_code, 0.0, ContextInfo, ContextInfo.account_id)
# 可选:在图表上画出信号 (仅在回测或界面运行时有效)
if is_buy_setup:
ContextInfo.draw_text(True, closes[-1], 'B9')
elif is_sell_setup:
ContextInfo.draw_text(True, closes[-1], 'S9')
代码详解
-
数据获取 (
get_market_data_ex):- 我们获取了
close收盘价。 count设置为setup_len (9) + lookback (4) + 5,确保有足够的数据回溯比较。- 使用
dividend_type='front'进行前复权,保证价格连续性。
- 我们获取了
-
TD 逻辑核心:
- 代码使用了一个
for循环来回溯最近的 9 根 K 线。 - 买入结构: 检查
closes[current] < closes[current - 4]是否连续 9 次成立。 - 卖出结构: 检查
closes[current] > closes[current - 4]是否连续 9 次成立。
- 代码使用了一个
-
交易执行:
- 使用
order_target_percent函数。 - 当 TD Buy Setup 9 完成时,视为底部信号,执行买入(目标仓位设为 1.0,即满仓)。
- 当 TD Sell Setup 9 完成时,视为顶部信号,执行卖出(目标仓位设为 0.0,即清仓)。
- 使用
-
可视化:
- 使用
ContextInfo.draw_text在 K 线图上标记 "B9" (买入9) 和 "S9" (卖出9),方便回测时观察。
- 使用
注意事项
- 账号设置: 请务必将
init函数中的YOUR_ACCOUNT_ID替换为您实际的 QMT 资金账号。 - 周期选择: TD 策略在不同的周期(日线、周线、分钟线)效果不同,请根据需要调整
ContextInfo.period。 - 风险提示: TD 9 只是一个逆势信号,在强劲的单边趋势中(如连续暴跌或暴涨),TD 9 可能会失效(即出现钝化,价格继续沿着原方向运动)。实际交易中通常需要配合止损或其他过滤条件(如 TD 计数 13 或趋势线突破)使用。
Q&A
Q: 如何在回测中查看策略表现?
A: 在 QMT 界面中打开“模型研究” -> “策略编辑器”,将上述代码粘贴进去,设置好回测时间段和基准,点击“回测”即可。
Q: 代码中的 lookback 为什么是 4?
A: 这是 Tom DeMark 在设计 TD Sequential 指标时的标准定义。他认为比较当前价格与 4 根 K 线前的价格能有效过滤掉短期噪音,捕捉市场动能的衰竭。
Q: 这个策略可以用于期货吗?
A: 可以。只需将 ContextInfo.stock_code 修改为期货合约代码(如 IF00.IF),并将 order_target_percent 替换为期货专用的下单函数(如 buy_open, sell_close 等)即可。