O consultor especialista MetaTrader original "Dynamic Stop Loss" não abre novas negociações por conta própria. Em vez disso, observa as posições de mercado existentes e, assim que uma nova vela aparece, reposiciona o stop-loss protetor para que fique a uma distância fixa atrás do preço mais recente. A porta StockSharp mantém o mesmo comportamento: cada barra completada aciona um recálculo da parada de proteção para qualquer lado que esteja aberto no momento. Se não existir nenhuma posição, a estratégia simplesmente fica inativa até que uma nova posição seja detectada.
Como funciona
A estratégia assina velas definidas pelo parâmetro Candle Type (período padrão de 1 minuto).
Quando uma vela fecha, o preço de fechamento é multiplicado pela distância do ponto selecionado pelo usuário. A distância é convertida de pontos no estilo MetaTrader em um delta de preço absoluto via Security.PriceStep (substituição para Security.Step e depois para 1).
Se uma posição longa estiver aberta, a estratégia cancela qualquer ordem stop existente e coloca um novo stop de venda em Close - Distance.
Se uma posição curta estiver aberta, o stop é movido para Close + Distance usando uma ordem stop de compra.
Quando a posição é fechada (manualmente ou pelo stop fill), a ordem móvel é cancelada para evitar ordens de proteção obsoletas.
Isso produz a mesma distância de parada constantemente ancorada que a versão MQL, o que significa que a parada pode se aproximar e se afastar do mercado à medida que as velas flutuam.
Parâmetros
Nome
Padrão
Descrição
StopLossPoints
800
Distância entre o preço de mercado e o stop de proteção medido em pontos do instrumento. O valor é multiplicado por Security.PriceStep (substituto para Security.Step e depois 1) antes de ser aplicado ao preço de fechamento. Defina como 0 para desativar o gerenciamento de parada.
CandleType
TimeFrameCandle(00:01:00)
Tipo de vela que define quando o stop é recalculado. Escolha um período que corresponda ao gráfico usado em MetaTrader.
Notas de uso
A estratégia espera que as negociações sejam abertas por estratégias externas, operações manuais ou outros componentes. Ele apenas gerencia o stop-loss.
Certifique-se de que os metadados de segurança (PriceStep, Step, volume) sejam preenchidos para que a conversão ponto-preço corresponda ao tamanho do tick do corretor. Instrumentos cotados com pips fracionários devem expor o passo adequado.
Como o stop é recalculado a cada fechamento de vela, ele seguirá o preço mesmo quando o mercado se mover contra a posição. Isso reflete a lógica MetaTrader onde OrderModify sempre usa o último Bid/Ask menos/mais a distância configurada.
As ordens stop criadas sempre substituem as anteriores para manter a plataforma sincronizada com o nível de proteção mais recente.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Dynamic Stop Loss strategy: EMA trend with ATR-based dynamic stop management.
/// Enters on EMA trend direction, exits when price moves against by ATR distance.
/// </summary>
public class DynamicStopLossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _entryPrice;
private decimal _stopPrice;
private bool _prevAboveEma;
private bool _hasPrevSignal;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public DynamicStopLossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for stop distance", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 1.5m)
.SetDisplay("ATR Multiplier", "ATR multiplier for stop distance", "Risk");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 12)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_stopPrice = 0m;
_prevAboveEma = false;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_stopPrice = 0;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var stopDist = atr * AtrMultiplier;
var aboveEma = close > ema;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (Position > 0)
{
var newStop = close - stopDist;
if (newStop > _stopPrice) _stopPrice = newStop;
if (close <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
_candlesSinceTrade = 0;
_prevAboveEma = aboveEma;
_hasPrevSignal = true;
return;
}
}
else if (Position < 0)
{
var newStop = close + stopDist;
if (newStop < _stopPrice || _stopPrice == 0) _stopPrice = newStop;
if (close >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
_candlesSinceTrade = 0;
_prevAboveEma = aboveEma;
_hasPrevSignal = true;
return;
}
}
if (_hasPrevSignal && aboveEma != _prevAboveEma && _candlesSinceTrade >= SignalCooldownCandles)
{
if (aboveEma && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_stopPrice = close - stopDist;
_candlesSinceTrade = 0;
}
else if (!aboveEma && Position >= 0)
{
SellMarket();
_entryPrice = close;
_stopPrice = close + stopDist;
_candlesSinceTrade = 0;
}
}
_prevAboveEma = aboveEma;
_hasPrevSignal = true;
}
}
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 ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class dynamic_stop_loss_strategy(Strategy):
def __init__(self):
super(dynamic_stop_loss_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 100) \
.SetDisplay("EMA Period", "EMA trend period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for stop distance", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 1.5) \
.SetDisplay("ATR Multiplier", "ATR multiplier for stop distance", "Risk")
self._signal_cooldown = self.Param("SignalCooldownCandles", 12) \
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading")
self._ema = None
self._atr = None
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_above_ema = False
self._has_prev_signal = False
self._candles_since_trade = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def atr_multiplier(self):
return self._atr_multiplier.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(dynamic_stop_loss_strategy, self).OnReseted()
self._ema = None
self._atr = None
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_above_ema = False
self._has_prev_signal = False
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(dynamic_stop_loss_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_period
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev_signal = False
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.Bind(self._ema, self._atr, self._process_candle)
subscription.Start()
def _process_candle(self, candle, ema_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed or not self._atr.IsFormed:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
atr_val = float(atr_value)
stop_dist = atr_val * self.atr_multiplier
above_ema = close > ema_val
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
if self.Position > 0:
new_stop = close - stop_dist
if new_stop > self._stop_price:
self._stop_price = new_stop
if close <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._candles_since_trade = 0
self._prev_above_ema = above_ema
self._has_prev_signal = True
return
elif self.Position < 0:
new_stop = close + stop_dist
if new_stop < self._stop_price or self._stop_price == 0.0:
self._stop_price = new_stop
if close >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._candles_since_trade = 0
self._prev_above_ema = above_ema
self._has_prev_signal = True
return
if self._has_prev_signal and above_ema != self._prev_above_ema and self._candles_since_trade >= self.signal_cooldown:
if above_ema and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - stop_dist
self._candles_since_trade = 0
elif not above_ema and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._stop_price = close + stop_dist
self._candles_since_trade = 0
self._prev_above_ema = above_ema
self._has_prev_signal = True
def CreateClone(self):
return dynamic_stop_loss_strategy()