Esta estratégia replica o comportamento do especialista MetaTrader Dual StopLoss.mq4. Atua como uma camada de gestão de risco: monitoriza as ordens protetoras de stop-loss associadas às posições abertas e fecha essas posições alguns pontos antes de o stop ser acionado. A saída antecipada foi projetada para evitar derrapagens negativas em movimentos altamente voláteis, ao mesmo tempo que respeita a colocação inicial do stop do trader.
Como funciona
A estratégia assina os dados do Nível 1 para rastrear o melhor lance/venda atual e a distância StopLevel (ou equivalente) publicada pela corretora.
Cada vez que novos preços chegam ou ordens/negociações mudam, ele procura a ordem stop ativa mais próxima que pertence ao título gerenciado.
A distância entre o preço de mercado e esse stop de proteção é comparada com um limite configurável:
pointValue corresponde ao MetaTrader de Point (0,0001 para a maioria dos pares FX, detectado automaticamente nas configurações de segurança).
stopLevelDistance vem de campos de Nível 1 (StopLevel, MinStopPrice, StopPrice ou StopDistance) quando disponíveis, caso contrário, zero.
Quando a distância restante é menor ou igual ao limite, a posição é fechada imediatamente através de uma ordem de mercado.
A lógica cobre posições longas e curtas. Para posições longas, a melhor oferta é comparada com o preço stop de venda; para posições curtas, o melhor preço de venda é comparado com o preço stop de compra. Apenas são consideradas ordens stop e stop-limit no estado ativo.
Parâmetros
Parâmetro
Descrição
QuandoToClosePoints
Distância (em MetaTrader pontos) do nível de stop que deverá desencadear a saída antecipada. Padrão: 10. Defina como zero para contar apenas com a distância mínima do nível de stop da corretora.
Notas e limitações
A estratégia não abre posições por si só; gerencia apenas posições que já existem e possuem ordens de stop de proteção.
Certifique-se de que o conector/corretor subjacente forneça valores de nível de parada por meio de dados de Nível 1 se você quiser levar em conta as distâncias mínimas impostas pelo corretor. Se essa informação estiver faltando, a estratégia ainda funciona usando apenas a distância do ponto configurada.
A chamada StartProtection() ativa os guardas de segurança integrados de StockSharp para que as saídas de emergência permaneçam ativas depois que a estratégia for iniciada.
As paradas são detectadas na coleção Orders da estratégia. Certifique-se de que as paradas de proteção sejam registradas através da mesma instância de estratégia para que apareçam nesta lista.
Quando existem múltiplas ordens de stop para a mesma direção, é utilizada aquela mais próxima do mercado.
Dicas de uso
Anexe a estratégia a uma carteira/título onde as posições são abertas manualmente ou por outro sistema, mas as paradas de proteção são colocadas no mesmo contexto estratégico.
Configure WhenToClosePoints para corresponder à quantidade de amortecimento necessária antes da parada. Este valor é interpretado exatamente como em MetaTrader (pontos, não unidades de preço).
Inicie a estratégia e monitore o log. Quando o preço de mercado se aproximar do stop, a estratégia emitirá uma ordem de mercado para fechar a posição de forma proativa.
Combine este módulo com outras estratégias de entrada ou dimensionamento de posição para criar um fluxo de trabalho de negociação completo.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Dual Stoploss strategy: dual SMA crossover with confirmation.
/// Buys when fast SMA crosses above mid SMA and mid is above slow, sells on opposite.
/// </summary>
public class DualStoplossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _midPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal _prevFast;
private decimal _prevMid;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int MidPeriod { get => _midPeriod.Value; set => _midPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DualStoplossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
_midPeriod = Param(nameof(MidPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Mid SMA", "Mid SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevMid = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new SimpleMovingAverage { Length = FastPeriod };
var mid = new SimpleMovingAverage { Length = MidPeriod };
var slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, mid, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal midValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevFast <= _prevMid && fastValue > midValue && midValue > slowValue && Position <= 0)
BuyMarket();
else if (_prevFast >= _prevMid && fastValue < midValue && midValue < slowValue && Position >= 0)
SellMarket();
}
else
{
if (fastValue > midValue && midValue > slowValue && Position <= 0)
BuyMarket();
else if (fastValue < midValue && midValue < slowValue && Position >= 0)
SellMarket();
}
_prevFast = fastValue;
_prevMid = midValue;
_hasPrev = 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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class dual_stoploss_strategy(Strategy):
"""
Dual Stoploss strategy: triple SMA crossover with confirmation.
Buys when fast SMA crosses above mid SMA and mid is above slow.
Sells on opposite crossover.
"""
def __init__(self):
super(dual_stoploss_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._fast_period = self.Param("FastPeriod", 3) \
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators")
self._mid_period = self.Param("MidPeriod", 10) \
.SetDisplay("Mid SMA", "Mid SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators")
self._prev_fast = 0.0
self._prev_mid = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(dual_stoploss_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_mid = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(dual_stoploss_strategy, self).OnStarted2(time)
self._has_prev = False
fast = SimpleMovingAverage()
fast.Length = self._fast_period.Value
mid = SimpleMovingAverage()
mid.Length = self._mid_period.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, mid, slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, mid)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, mid_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_val)
mid_val = float(mid_val)
slow_val = float(slow_val)
if self._has_prev:
if self._prev_fast <= self._prev_mid and fast_val > mid_val and mid_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_mid and fast_val < mid_val and mid_val < slow_val and self.Position >= 0:
self.SellMarket()
else:
if fast_val > mid_val and mid_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif fast_val < mid_val and mid_val < slow_val and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_mid = mid_val
self._has_prev = True
def CreateClone(self):
return dual_stoploss_strategy()