A estratégia Escape é uma versão StockSharp do MetaTrader 4 consultor especialista escape.mq4. O robô original negocia um gráfico de cinco minutos e reage a oportunidades de reversão à média: compra quando o preço cai abaixo de uma média móvel curta e vende quando o preço sobe acima de outra média rápida. Cada posição é protegida por um take-profit e stop-loss de distância fixa expressos em MetaTrader pontos. A implementação C# mantém a mesma lógica minimalista enquanto expõe todas as distâncias ajustáveis como parâmetros de estratégia.
Lógica de negociação
Inicialização
Assine a série configurável CandleType (velas de cinco minutos por padrão).
Crie dois indicadores SimpleMovingAverage com comprimentos 5 e 4 que são alimentados com preços de abertura de velas.
Calcule o equivalente MetaTrader Point de Security.PriceStep; esse valor é reutilizado para converter distâncias estilo pip em preços absolutos.
Processamento por vela
Somente velas finalizadas são processadas via SubscribeCandles(...).WhenCandlesFinished(ProcessCandle).
A estratégia primeiro verifica se uma posição existente atingiu seu stop-loss ou take-profit, comparando a máxima/mínima da vela com os níveis de saída registrados. Quando um nível é ultrapassado, a posição é fechada com uma ordem de mercado e ordens de saída duplicadas são evitadas através de sinalizações internas.
Se a conta for plana, os valores anteriores dos dois SMAs estiverem disponíveis, a negociação for permitida e o portfólio tiver capital suficiente (Portfolio.CurrentValue >= MinimumMarginPerLot * TradeVolume), a estratégia avalia as entradas:
Entrada longa – o fechamento atual está abaixo dos 5 períodos anteriores SMA de aberturas.
Entrada curta – o fechamento atual está acima dos 4 períodos anteriores SMA de aberturas.
Quando um sinal é acionado, os níveis de stop-loss e take-profit são calculados a partir do fechamento da vela usando as distâncias dos pontos configuradas e armazenados para monitoramento posterior.
Gerenciamento de riscos
TradeVolume define o tamanho do lote de cada ordem de mercado.
MinimumMarginPerLot aproxima a verificação AccountFreeMargin de MetaTrader. Se o valor do portfólio disponível for muito pequeno, a entrada será ignorada e uma mensagem de diagnóstico será registrada.
Parâmetros
Nome
Padrão
Descrição
LongTakeProfitPoints
10
Distância de take-profit para posições longas em MetaTrader pontos. Defina como 0 para desativar o alvo.
ShortTakeProfitPoints
10
Distância de take-profit para posições curtas em MetaTrader pontos. Defina como 0 para desativar o alvo.
LongStopLossPoints
1000
Distância de stop-loss para posições longas em MetaTrader pontos. Defina como 0 para desativar a parada de proteção.
ShortStopLossPoints
1000
Distância de stop-loss para posições curtas em MetaTrader pontos. Defina como 0 para desativar a parada de proteção.
TradeVolume
0.2
Tamanho do lote utilizado no envio de ordens de mercado.
MinimumMarginPerLot
500
Requisito aproximado de capital por lote antes de abrir uma nova negociação.
CandleType
Período de cinco minutos
Série de velas que impulsiona atualizações de indicadores e geração de sinais.
Notas de implementação
Os indicadores são atualizados manualmente dentro de ProcessCandle com preços de abertura de velas para que os valores armazenados sempre representem a barra anterior (espelhando os argumentos shift=1 usados em iMA).
Os níveis de saída são rastreados em campos decimais em vez de criar coleções adicionais, atendendo às diretrizes API de alto nível.
Stops e metas são avaliados em relação aos extremos das velas; como apenas dados de OHLC estão disponíveis, a verificação de stop é realizada antes do take-profit para emular a prioridade do pedido de MetaTrader o mais próximo possível.
A estratégia reúne velas com médias móveis e negociações próprias quando uma área do gráfico está disponível, simplificando a validação visual.
Diferenças em relação à versão MetaTrader
MetaTrader anexa ordens de stop-loss e take-profit diretamente aos tickets. A porta StockSharp os reproduz monitorando os máximos e mínimos das velas e enviando saídas de mercado; a ordem de execução intrabarra não pode ser garantida se ambos os níveis forem tocados na mesma barra.
Os preços de entrada são derivados do fechamento da vela que acionou o sinal, em vez do lance/venda exato usado por MetaTrader, portanto, o tratamento de deslizamento e spread deve ser configurado no nível do conector.
A guarda AccountFreeMargin() é aproximada por meio de Portfolio.CurrentValue. Usuários com modelos de margem mais detalhados podem estender HasSufficientMargin se necessário.
Configurações cosméticas MQL, como cores, sons e deslizamento, são omitidas; a versão StockSharp concentra-se na lógica comercial principal.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Escape Mean Reversion: SMA crossover with ATR stops.
/// </summary>
public class EscapeMeanReversionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevClose;
private decimal _entryPrice;
public EscapeMeanReversionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_smaLength = Param(nameof(SmaLength), 5)
.SetDisplay("SMA Length", "SMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_entryPrice = 0;
var sma = new SimpleMovingAverage { Length = SmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_prevClose == 0 || atrVal <= 0)
{
_prevClose = close;
return;
}
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 1.5m || close > smaVal)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 1.5m || close < smaVal)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close < smaVal && _prevClose >= smaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close > smaVal && _prevClose <= smaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevClose = close;
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class escape_mean_reversion_strategy(Strategy):
"""
Escape Mean Reversion: SMA crossover with ATR stops.
Buys when price crosses below SMA, sells when price crosses above SMA.
Exits at ATR-based TP/SL levels.
"""
def __init__(self):
super(escape_mean_reversion_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._sma_length = self.Param("SmaLength", 5) \
.SetDisplay("SMA Length", "SMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._prev_close = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(escape_mean_reversion_strategy, self).OnReseted()
self._prev_close = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(escape_mean_reversion_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._entry_price = 0.0
sma = SimpleMovingAverage()
sma.Length = self._sma_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma_val = float(sma_val)
atr_val = float(atr_val)
if self._prev_close == 0.0 or atr_val <= 0:
self._prev_close = close
return
if self.Position > 0:
if (close >= self._entry_price + atr_val * 2.0 or
close <= self._entry_price - atr_val * 1.5 or
close > sma_val):
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (close <= self._entry_price - atr_val * 2.0 or
close >= self._entry_price + atr_val * 1.5 or
close < sma_val):
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close < sma_val and self._prev_close >= sma_val:
self._entry_price = close
self.BuyMarket()
elif close > sma_val and self._prev_close <= sma_val:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def CreateClone(self):
return escape_mean_reversion_strategy()