Esta estratégia utilitária anexa automaticamente ordens protetoras de stop-loss e take-profit a cada posição aberta no instrumento configurado. Ela espelha o comportamento do expert original do MetaTrader "AutoSet SL TP", monitorando a lista de posições ativas e aplicando as restrições de distância do corretor antes de registrar as ordens protetoras.
A estratégia não abre trades por conta própria. Em vez disso, ela monitora o volume, direção e preço de execução das posições que foram criadas manualmente ou por outras estratégias. Assim que uma posição longa ou curta aparecer, o algoritmo calcula os níveis desejados de stop-loss e take-profit expressos em pips no estilo MetaTrader, ajusta os níveis para cumprir com as restrições de congelamento e parada publicadas pela bolsa, e então envia as ordens protetoras de mercado apropriadas. Quando a posição é totalmente fechada, as ordens protetoras são canceladas automaticamente.
Como funciona
Assina dados de Nível1 para receber os melhores preços bid/ask juntamente com os campos opcionais StopLevel e FreezeLevel fornecidos pelo corretor.
Converte as distâncias configuradas em pips para preços absolutos usando os metadados do símbolo (passo de preço e precisão decimal). Cotações de cinco e três dígitos são automaticamente escalonadas por um fator de dez para corresponder à semântica de pip do MetaTrader.
Em cada atualização de cotação ou notificação de trade pessoal:
Ignora o sinal se não houver posição aberta ou se a direção não corresponder ao filtro configurado (somente compra, somente venda ou ambos).
Calcula a distância mínima permitida entre o preço de mercado e uma ordem protetora. Se o corretor não publicar níveis de congelamento/parada, o algoritmo recorre a três spreads multiplicados por 1.1 para se manter com segurança fora das zonas proibidas.
Determina o preço de stop-loss e take-profit em relação ao ask atual (para longos) ou bid (para curtos) e normaliza o resultado para o passo de preço do instrumento.
Coloca ou re-registra ordens protetoras de stop ou limite com o volume exato da posição. As ordens são substituídas apenas quando o preço alvo ou o volume muda, o que mantém as modificações na bolsa no mínimo.
Se o volume da posição se tornar zero, todas as ordens protetoras pendentes são canceladas. A estratégia também cancela as ordens existentes quando a direção do trade não é mais permitida pelo filtro.
Como o algoritmo depende exclusivamente de fills externos, ele pode ser combinado com negociação discricionária, painéis ou outros sistemas automatizados que gerenciam entradas, enquanto esta estratégia garante um envelope protetor consistente.
Parâmetros
StopLossPips – distância do preço de mercado atual ao stop-loss em pips do MetaTrader. Um valor de 0 desabilita a ordem de stop. Padrão: 50.
TakeProfitPips – distância do preço de mercado atual ao take-profit em pips do MetaTrader. Um valor de 0 desabilita a ordem de take-profit. Padrão: 140.
DirectionFilter – especifica qual direção de posição é gerenciada:
Buy – proteger apenas exposição longa.
Sell – proteger apenas exposição curta.
BuySell – proteger ambos os lados (comportamento padrão no script original).
Notas práticas
As ordens protetoras são sempre criadas com o volume absoluto da posição. Se o corretor impuser tamanhos de lote mínimos ou máximos, a estratégia arredonda o volume para o valor permissível mais próximo antes de colocar as ordens.
O algoritmo usa ReRegisterOrder para ajustar ordens protetoras ativas. Isso mantém os mesmos identificadores de ordem da bolsa sempre que possível e evita cancelamentos desnecessários.
A distância de fallback (spread × 3 × 1.1) evita que o stop ou take-profit viole restrições ocultas da bolsa quando os níveis explícitos de congelamento/parada não são fornecidos.
Como a estratégia não gerencia entradas, ela pode ser iniciada antes ou depois de as posições serem abertas. Qualquer posição qualificada que já exista no momento da inicialização será protegida imediatamente após a primeira atualização de cotação.
Os "pips" do MetaTrader diferem dos passos de preço da bolsa em símbolos com três ou cinco dígitos decimais. A estratégia replica o Expert Advisor original multiplicando o valor do ponto de acordo, garantindo que os números configurados correspondam exatamente às configurações do MT5.
Diferenças em relação ao expert do MetaTrader
Em vez de modificar os atributos de stop e take-profit na posição, o StockSharp gerencia ordens protetoras de stop e limite explícitas. Essa abordagem mantém a lógica completamente transparente dentro do livro de ordens do StockSharp.
A versão do StockSharp usa dados de mercado de Nível1 para reconstruir os níveis de restrição do corretor. Se o provedor expuser nomes de campo diferentes para distâncias de congelamento ou parada, a estratégia os descobre automaticamente por reflexão no enum Level1Fields.
Cada comentário de código e mensagem de log está em inglês para manter a consistência com as diretrizes de codificação, enquanto a documentação é localizada em russo e chinês para os usuários finais.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class AutoSetStopLossTakeProfitStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public AutoSetStopLossTakeProfitStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class auto_set_stop_loss_take_profit_strategy(Strategy):
def __init__(self):
super(auto_set_stop_loss_take_profit_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(auto_set_stop_loss_take_profit_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(auto_set_stop_loss_take_profit_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return auto_set_stop_loss_take_profit_strategy()