Skip to content

TD序列

一、TD序列策略背景

TD序列(迪马克序列)。该技术由托马斯·迪马克(Thomas Demark)于1970年提出,核心是通过K线序列判断市场趋势转折。传统TD序列的核心规则:

  • 买入结构:连续9根K线,每根收盘价均低于前第4根K线收盘价。

  • 卖出结构:连续9根K线,每根收盘价均高于前第4根K线收盘价 。

本策略特点:反传统TD信号操作——出现买入结构时卖出,出现卖出结构时买入,主打“反向交易”理念。

二、反向策略核心逻辑

1. 反向操作理念

  • 传统TD:买入结构做多,卖出结构做空。
  • 本策略
    • 买入结构出现时挂卖出突破单(顺下跌趋势继续做空)。
    • 卖出结构出现时挂买入突破单(顺上涨趋势继续做多)。
    • 核心:“买在别人不敢买的高点,卖在别人不敢空的低点”。

2. 进场三条件(以做多为例,做空对称)

  1. 无多单持仓;
  2. 无多头挂单;
  3. 连续N根K线(N=参数TDSetup)收盘价均高于各自前M根K线(M=参数TDCountdown)收盘价。

3. 挂单与止损规则

  • 进场价:以最后一根满足条件K线的最高点(做空时为最低点)挂突破单(Buy Stop/Sell Stop)。
  • 止损价:最近L根K线(L=参数SlBarCount)的最低点(做多)或最高点(做空)。
  • 止盈:无固定止盈,依赖跟踪止损。

4. 挂单管理

  • 删单条件
    1. 反向信号出现(如多单挂单时出现空单信号);
    2. 进场价因新TD序列信号变化(需重新挂单)。
  • 跟踪止损:持仓后,以最近L根K线高低点动态调整止损,锁定利润。

三、盘面信号与参数设置

1. 信号可视化

  • 蓝色点:TD买入结构(传统做多信号,本策略做空);
  • 红色点:TD卖出结构(传统做空信号,本策略做多)。
  • 示例:连续9根K线收盘价高于前4根(卖出结构),策略挂买入突破单,止损为前10根K线最低点。

2. 参数优化建议

  • TDSetup:连续K线数量(默认9,数值越大信号越严格,交易机会越少);
  • TDCountdown:对比的前序K线数(默认4);
  • SlBarCount:止损/跟踪止损参考的K线数(默认10)。

四、代码实现要点

cpp
#property strict
#include <Util/Util.mqh>
Util util;
// 不可以做参数优化的参数
input group "固定和输入通用部分";
sinput int MagicNum = 12345; // 魔术编号
sinput int SlType = 1;                                        // 止损方式(1:固定金额{100}美金 2:账户{1}% 3:固定{0.1}手 4:子账户总账户{1}%)
sinput int SlParam = 100;                                     // 止损依据(根据上方【止损方式】的不同,所代表的含义也不同)

input group "参数优化";
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 时间周期

input int TDSetup = 9;     // 连续多少根K线
input int TDCountdown = 4; // 比较前面多少根K线
input int SlBarCount = 10; //  跟踪止损K线

int barsTotal;

int initBalance;
double point;


int OnInit()
{
    // 当用户没有打开参数对应的图表周期,自动帮他打开对应周期
    if (MQLInfoInteger(MQL_TESTER))
    {
        ChartSetInteger(0, CHART_SHOW_GRID, false);
        ChartSetInteger(0,CHART_COLOR_BACKGROUND,clrGray);
        ChartSetInteger(0, CHART_COLOR_CHART_UP, clrBlack);
        ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack);
        ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrWhite);
        ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack);
        ChartSetInteger(0, CHART_SHOW_ASK_LINE, true); // 添加显示买价线
    }
    else
    {
        ChartSetSymbolPeriod(0, _Symbol, TimeFrame);
    }
    initBalance = SlParam;
    Comment("MagicNum:", MagicNum, " SlType: ", SlType, " SlParam: ", SlParam);
    return INIT_SUCCEEDED;
}
// 节省内存后 买VPS就可以买小一点
void OnDeinit(const int reason)
{
}
void OnTick()
{

    if (!util.isNewBar(_Symbol, TimeFrame, barsTotal))
        return;
    if (!util.isEnableTimezone())
       return;
    if(!util.isMarketOpen(_Symbol,TimeCurrent())) 
        return;
        
    point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

    util.trailingStopByBar(_Symbol, TimeFrame, MagicNum, SlBarCount);

    int cntBuy, cntSell;
    if (!util.countOpenPositions(MagicNum, cntBuy, cntSell))
        return;
    //存在仓位
    if (cntBuy > 0 || cntSell > 0)
    {
        return;
    }
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

    int signal = checkSignal();

    // 删除挂单
    int countBuyOrders, countSellOrders;
    if (!util.countPendingOrders(MagicNum, countBuyOrders, countSellOrders))
        return;
    if (signal == 1)
    {
        if (countSellOrders > 0)
            util.deleteOrders(MagicNum, ORDER_TYPE_SELL_STOP); // 删除空单挂单
        if (countBuyOrders > 0)
        {
            double et = util.highPrice(_Symbol, TimeFrame, 1);
            util.deleteOrdersByPriceOpen(MagicNum, ORDER_TYPE_BUY_STOP, et); // 删除所有开仓价变化的多单挂单
        }
    }
    else if (signal == -1)
    {
        if (countBuyOrders > 0)
        util.deleteOrders(MagicNum, ORDER_TYPE_BUY_STOP); // 删除多单挂单

        if (countSellOrders > 0)
        {
            double et = util.lowPrice(_Symbol, TimeFrame, 1);
            util.deleteOrdersByPriceOpen(MagicNum, ORDER_TYPE_SELL_STOP, et); // 删除所有开仓价变化的空单挂单
        }
    }
    //更新挂单
    if (!util.countPendingOrders(MagicNum, countBuyOrders, countSellOrders))
        return;
    bool bullSignal = cntBuy == 0 && countBuyOrders == 0 && signal == 1;
    if (bullSignal)
    {
        double et = util.highPrice(_Symbol, TimeFrame, SlBarCount);
        double sl = util.lowPrice(_Symbol, TimeFrame, SlBarCount);

        double slp = SlParam;
        if (SlType == 4)
        {
            slp = util.calculateEachEaBalance(MagicNum, initBalance); // 子账户百分之一
        }
        util.orderSendStop(_Symbol, MagicNum, et, sl, SlType, slp, 0.0, "TD9SO-" + string(MagicNum));
    }
    bool bearSignal = cntSell == 0 && countSellOrders == 0 && signal == -1;
    if (bearSignal)  
    {
        double et = util.lowPrice(_Symbol, TimeFrame, SlBarCount);
        double sl = util.highPrice(_Symbol, TimeFrame, SlBarCount);

        double slp = SlParam;
        if (SlType == 4)
        {
            slp = util.calculateEachEaBalance(MagicNum, initBalance); // 子账户百分之一
        }
        util.orderSendStop(_Symbol, MagicNum, et, sl, SlType, slp, 0.0, "TD9SO-" + string(MagicNum));
    }
}
//
int checkSignal()
{
    int count = 1;
    while (iClose(_Symbol, TimeFrame, count) > iClose(_Symbol, TimeFrame, count + TDCountdown))
    {
        util.setFlag("TD9", 1, count, TimeFrame);
        count++;
        if (count > TDSetup)
            return 1;
    }
    count = 1;
    while (iClose(_Symbol, TimeFrame, count) < iClose(_Symbol, TimeFrame, count + TDCountdown))
    {
        util.setFlag("TD9", -1, count, TimeFrame);
        count++;
        if (count > TDSetup)
            return -1;
    }
    return 0;
}