Esta estratégia é uma conversão em C# do consultor especialista do MetaTrader 5 Exp_TDI-2_ReOpen. Opera usando o indicador Trend Direction Index (TDI-2) e aplica a lógica original de re-entrada em posições. O port em C# usa a API de alto nível do StockSharp e mantém o comportamento central da versão MQL: reage a cruzamentos entre a linha de momentum TDI e a linha de índice TDI, escala em posições lucrativas após um avanço de preço configurável, e gerencia trades com stops protetores opcionais.
Indicadores
Indicador TDI-2 – um indicador personalizado baseado em momentum implementado neste repositório. Constrói duas linhas:
Linha direcional: Período × MomentumSuavizado, onde o momentum é igual ao preço aplicado menos o preço Período barras atrás.
Linha de índice: |Direcional| − (2 × Período × Suavizado(|Momentum|, 2×Período) − |Momentum|).
O indicador suporta os seguintes métodos de suavização: média móvel Simples, Exponencial, Suavizada (RMA) e Linearmente Ponderada.
As opções de preço aplicado suportadas replicam a implementação MQL original, incluindo as fórmulas TrendFollow e Demark.
Lógica de trading
Em cada vela finalizada, a estratégia avalia os valores TDI-2 na barra especificada por Signal Bar (padrão: vela anterior fechada) e uma barra antes.
Quando a linha direcional estava acima da linha de índice e depois cruza abaixo:
Se Allow Long Entries estiver habilitado e não houver posição comprada ativa, a estratégia prepara uma nova entrada comprada.
Se existir uma posição vendida e Allow Short Exits estiver habilitado, fecha a posição vendida.
Quando a linha direcional estava abaixo da linha de índice e depois cruza acima:
Se Allow Short Entries estiver habilitado e não houver posição vendida ativa, a estratégia prepara uma nova entrada vendida.
Se existir uma posição comprada e Allow Long Exits estiver habilitado, fecha a posição comprada.
Lógica de re-entrada (escalonamento):
Enquanto mantém uma posição comprada, a estratégia rastreia o preço de execução do último trade comprado. Se o mercado se mover favoravelmente em Re-entry Step (points) e o número de trades comprados executados ainda estiver abaixo de Max Entries, abre uma ordem comprada adicional com o volume base.
A mesma lógica aplica-se a posições vendidas usando o preço de execução vendido mais recente.
Ao abrir uma posição enquanto existe uma posição contrária, a estratégia envia uma ordem a mercado combinada dimensionada tanto para fechar a exposição contrária quanto para estabelecer a nova posição com o volume base configurado.
Os níveis opcionais de stop-loss e take-profit são ativados através de StartProtection usando o multiplicador PriceStep do instrumento.
Parâmetros
Nome
Descrição
Valores padrão
Money Management
Volume de ordem base.
0.1
Max Entries
Número máximo de entradas por direção (trade inicial + re-entradas).
10
Stop Loss (points)
Distância do stop-loss em pontos do instrumento.
1000
Take Profit (points)
Distância do take-profit em pontos do instrumento.
2000
Slippage (points)
Mantido por compatibilidade; não usado na implementação do StockSharp.
10
Re-entry Step (points)
Movimento mínimo favorável antes de escalar em uma posição existente.
300
Allow Long Entries / Allow Short Entries
Habilitar abertura de posições compradas/vendidas.
true
Allow Long Exits / Allow Short Exits
Habilitar fechamento de posições compradas/vendidas.
true
Candle Type
Série de velas usada para os cálculos.
Velas H4
TDI Smoothing
Método de suavização para o indicador TDI-2.
MA Simples
TDI Period
Período de lookback do momentum.
20
TDI Phase
Reservado para compatibilidade com o input MQL (sem efeito nos modos de suavização suportados).
15
Applied Price
Fonte de preço usada pelo TDI-2.
Close
Signal Bar
Número de velas fechadas a olhar para trás ao avaliar cruzamentos.
1
Notas adicionais
Apenas os métodos de suavização suportados pelos indicadores do StockSharp (SMA, EMA, SMMA, LWMA) são implementados. Outros modos MQL como JJMA ou T3 não estão disponíveis.
O parâmetro TDI Phase é mantido por completude. Não influencia os métodos de suavização suportados e pode ser deixado no seu valor padrão.
O parâmetro Slippage (points) é fornecido por paridade com o consultor especialista original, mas não é usado pela API de alto nível.
Os contadores de re-entrada são reiniciados automaticamente quando a posição líquida retorna a zero.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trend Direction Index re-entry strategy.
/// Trades based on crossings between the TDI momentum line and the TDI index line.
/// </summary>
public class Tdi2ReOpenStrategy : Strategy
{
private readonly StrategyParam<int> _tdiPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal? _lastClose;
private decimal? _directional;
private decimal? _index;
private decimal? _prevDirectional;
private decimal? _prevIndex;
public int TdiPeriod { get => _tdiPeriod.Value; set => _tdiPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Tdi2ReOpenStrategy()
{
_tdiPeriod = Param(nameof(TdiPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("TDI Period", "Momentum lookback period", "Indicator")
.SetOptimize(5, 30, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Data series", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastClose = null;
_directional = null;
_index = null;
_prevDirectional = null;
_prevIndex = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_lastClose = null;
_directional = null;
_index = null;
_prevDirectional = null;
_prevIndex = null;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_lastClose is not decimal lastClose)
{
_lastClose = close;
return;
}
var momentum = close - lastClose;
_lastClose = close;
var alpha = 2m / (TdiPeriod + 1m);
if (_directional is not decimal prevDirectionalLine || _index is not decimal prevIndexLine)
{
_directional = momentum;
_index = momentum;
return;
}
var directional = prevDirectionalLine + alpha * (momentum - prevDirectionalLine);
var index = prevIndexLine + alpha * (directional - prevIndexLine);
if (_prevDirectional is not decimal prevDir || _prevIndex is not decimal prevIdx)
{
_directional = directional;
_index = index;
_prevDirectional = prevDirectionalLine;
_prevIndex = prevIndexLine;
return;
}
var crossUp = prevDir <= prevIdx && directional > index;
var crossDown = prevDir >= prevIdx && directional < index;
if (crossUp && Position <= 0)
BuyMarket();
else if (crossDown && Position >= 0)
SellMarket();
_directional = directional;
_index = index;
_prevDirectional = directional;
_prevIndex = index;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class tdi_2_re_open_strategy(Strategy):
"""TDI momentum/index crossover: custom EMA-smoothed momentum lines."""
def __init__(self):
super(tdi_2_re_open_strategy, self).__init__()
self._tdi_period = self.Param("TdiPeriod", 10).SetGreaterThanZero().SetDisplay("TDI Period", "Momentum lookback period", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(tdi_2_re_open_strategy, self).OnReseted()
self._last_close = 0
self._directional = None
self._index = None
self._prev_dir = None
self._prev_idx = None
def OnStarted2(self, time):
super(tdi_2_re_open_strategy, self).OnStarted2(time)
self._last_close = 0
self._directional = None
self._index = None
self._prev_dir = None
self._prev_idx = None
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
if self._last_close == 0:
self._last_close = close
return
momentum = close - self._last_close
self._last_close = close
alpha = 2.0 / (self._tdi_period.Value + 1.0)
if self._directional is None or self._index is None:
self._directional = momentum
self._index = momentum
return
directional = self._directional + alpha * (momentum - self._directional)
index = self._index + alpha * (directional - self._index)
if self._prev_dir is None or self._prev_idx is None:
self._directional = directional
self._index = index
self._prev_dir = self._directional
self._prev_idx = self._index
return
cross_up = self._prev_dir <= self._prev_idx and directional > index
cross_down = self._prev_dir >= self._prev_idx and directional < index
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._directional = directional
self._index = index
self._prev_dir = directional
self._prev_idx = index
def CreateClone(self):
return tdi_2_re_open_strategy()