Esta estratégia é uma conversão em C# do expert do MetaTrader Exp_X2MACandle_MMRec. Ela observa a cor de um candle duplamente suavizado, produzido pelo indicador personalizado X2MA original, para decidir quando abrir ou fechar posições. A versão StockSharp recria o pipeline de suavização dupla e mantém uma camada leve de gestão de dinheiro que reduz o volume de trading após um número configurável de perdas recentes.
O algoritmo processa apenas candles completados. Ele assina um período configurável, aplica duas médias móveis encadeadas aos valores OHLC do candle, deriva uma cor sintética de candle (verde, cinza ou vermelho) e usa transições de cor com um deslocamento de barra selecionável pelo usuário para acionar ações. Operações compradas são abertas quando a cor muda de altista para qualquer outra coisa. Operações vendidas seguem a condição simétrica. As saídas de posição estão alinhadas com as mesmas verificações de cor e podem ser habilitadas ou desabilitadas separadamente para cada lado.
Lógica do indicador
Cada candle é suavizado duas vezes. Ambas as etapas podem usar diferentes métodos e comprimentos.
As opções de suavização mapeiam para indicadores do StockSharp:
Simple → SimpleMovingAverage
Exponential → ExponentialMovingAverage
Smoothed → SmoothedMovingAverage (RMA)
Weighted → WeightedMovingAverage
Jurik → JurikMovingAverage (o parâmetro Phase é respeitado quando disponível).
O corpo do candle sintético é achatado quando a diferença absoluta abertura/fechamento está abaixo de GapPoints * Security.StepPrice.
As cores são atribuídas da seguinte forma: abertura < fechamento → 2 (altista), abertura > fechamento → 0 (baixista), caso contrário → 1 (neutro).
Os sinais são avaliados na barra SignalBar + 1 (duas barras atrás com a configuração padrão) para que as ordens sejam enviadas apenas após um candle completo confirmar a mudança de cor.
Gestão de dinheiro
O expert original reduzia dinamicamente o tamanho da posição após uma série de perdas usando estatísticas históricas de negociações. O StockSharp não expõe o histórico exato do MetaTrader, então o port mantém uma fila interna de negociações fechadas recentes.
O comprimento da fila é controlado por HistoryDepth e o volume cai para ReducedVolume uma vez que LossTrigger ou mais perdas sejam detectadas dentro da janela.
A estratégia registra os resultados das negociações usando preços de fechamento de candles quando uma saída manual é acionada. As ordens stop-loss/take-profit da versão MetaTrader não são recriadas. Você pode adicionar suas próprias regras de proteção através dos gestores de risco do StockSharp se necessário.
Parâmetros
Nome
Descrição
CandleType
Período dos candles usados para suavização e trading.
FirstMethod, FirstLength, FirstPhase
Método de suavização primário, comprimento e fase Jurik.
SecondMethod, SecondLength, SecondPhase
Método de suavização secundário, comprimento e fase Jurik.
GapPoints
Limiar de achatamento do corpo em passos de preço.
SignalBar
Deslocamento (0 = último candle terminado) ao ler os buffers de cor.
AllowLongEntry / AllowShortEntry
Habilitar abertura de posições compradas ou vendidas.
AllowLongExit / AllowShortExit
Habilitar fechamento de posições compradas ou vendidas.
NormalVolume
Tamanho de ordem padrão (lotes, ações, contratos).
ReducedVolume
Tamanho de ordem usado após o número configurado de perdas.
HistoryDepth
Número de negociações recentes inspecionadas para perdas (0 desativa o rastreamento do histórico).
LossTrigger
Contagem de perdas que ativa o volume reduzido (0 desativa o interruptor).
Notas de uso
A estratégia opera em um único instrumento retornado por GetWorkingSecurities().
Sinais e saídas são processados uma vez por candle terminado para evitar ordens duplicadas.
Definir ReducedVolume igual a NormalVolume se quiser desativar a redução de volume mantendo as estatísticas do histórico.
Como o port depende de preços de fechamento de candles para classificar as negociações, o contador de perdas pode diferir ligeiramente do MetaTrader quando ocorrem deslizamentos ou execuções parciais. A documentação deve ajudá-lo a ajustar os parâmetros para obter comportamento similar.
Stops e take-profits da versão MQL não são recriados automaticamente. Usar gestores de risco do StockSharp (StartProtection) se precisar de proteção a nível de plataforma.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ExpX2MaCandleMmRecStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public ExpX2MaCandleMmRecStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 8).SetGreaterThanZero().SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 25).SetGreaterThanZero().SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new WeightedMovingAverage { Length = FastPeriod };
var slow = new WeightedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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.Indicators import WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_x2_ma_candle_mm_rec_strategy(Strategy):
def __init__(self):
super(exp_x2_ma_candle_mm_rec_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 8) \
.SetDisplay("Fast WMA", "Fast WMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 25) \
.SetDisplay("Slow WMA", "Slow WMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(exp_x2_ma_candle_mm_rec_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(exp_x2_ma_candle_mm_rec_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = WeightedMovingAverage()
fast.Length = self.FastPeriod
slow = WeightedMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return exp_x2_ma_candle_mm_rec_strategy()