TD序列
一、TD序列策略背景
TD序列(迪马克序列)。该技术由托马斯·迪马克(Thomas Demark)于1970年提出,核心是通过K线序列判断市场趋势转折。传统TD序列的核心规则:
- 买入结构:连续9根K线,每根收盘价均低于前第4根K线收盘价。
- 卖出结构:连续9根K线,每根收盘价均高于前第4根K线收盘价 。
本策略特点:反传统TD信号操作——出现买入结构时卖出,出现卖出结构时买入,主打“反向交易”理念。
二、反向策略核心逻辑
1. 反向操作理念
- 传统TD:买入结构做多,卖出结构做空。
- 本策略:
- 买入结构出现时挂卖出突破单(顺下跌趋势继续做空)。
- 卖出结构出现时挂买入突破单(顺上涨趋势继续做多)。
- 核心:“买在别人不敢买的高点,卖在别人不敢空的低点”。
2. 进场三条件(以做多为例,做空对称)
- 无多单持仓;
- 无多头挂单;
- 连续N根K线(N=参数TDSetup)收盘价均高于各自前M根K线(M=参数TDCountdown)收盘价。
3. 挂单与止损规则
- 进场价:以最后一根满足条件K线的最高点(做空时为最低点)挂突破单(Buy Stop/Sell Stop)。
- 止损价:最近L根K线(L=参数SlBarCount)的最低点(做多)或最高点(做空)。
- 止盈:无固定止盈,依赖跟踪止损。
4. 挂单管理
- 删单条件:
- 反向信号出现(如多单挂单时出现空单信号);
- 进场价因新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;
}