Esta estratégia é uma porta C# do consultor especialista MetaTrader 10p3v003 (10point3.mq4). Ele combina um gatilho cruzado MACD com um mecanismo de grade martingale. A lógica original foi replicada usando StockSharp de alto nível API com os seguintes comportamentos principais:
MACD lógica de sinal – Uma direção de negociação é determinada quando a linha principal MACD cruza a linha de sinal na barra deslocada (SignalShift). As entradas longas exigem que o valor do sinal anterior esteja abaixo de -TradingRangePips, o valor atual de MACD fique abaixo de zero e vice-versa para as posições curtas. Os sinais podem opcionalmente ser invertidos através de ReverseSignal.
Camadas de grade – Após a abertura da primeira posição, entradas adicionais na mesma direção só serão permitidas quando o preço se mover em relação ao último preenchimento em pelo menos GridStepPips. Cada nova perna multiplica o volume por LotMultiplier (ou por 1.5 se MaxTrades > 12), imitando a escala martingale de MQL4.
Proteção contra riscos – A etapa mais recente é fechada e nenhuma entrada adicional é adicionada quando OrdersToProtect ou mais negociações estão ativas e o lucro flutuante excede o limite monetário. O limite é baseado na porcentagem de risco configurada (gestão de dinheiro habilitada) ou na heurística de tamanho do contrato (gestão de dinheiro desabilitada).
Saídas por perna – Cada perna rastreia seu próprio take-profit, stop-loss virtual e trailing stop. A distância de parada corresponde à fórmula original: InitialStopPips + (MaxTrades - existingOrders) * GridStepPips. O trailing é ativado somente após o preço se mover em TrailingStopPips + GridStepPips a favor da posição e fecha a perna quando o preço retrocede em TrailingStopPips.
Filtro de sessão – Quando UseTimeFilter está ativado, nenhuma nova grade é iniciada enquanto o tempo da vela estiver estritamente entre StopHour e StartHour, reproduzindo a proteção de "fuso horário de perigo" do script.
Todas as conversões de dinheiro usam os metadados PriceStep/StepPrice do título. Se a exchange não expor o tamanho do contrato, um valor substituto de 100000 será aplicado, o que corresponde à suposição original do Forex.
Parâmetros
Nome
Descrição
CandleType
Assinatura Candle usada para processamento de MACD (padrão: período de 30 minutos).
Volume
Tamanho base do lote para a primeira ordem de grade.
TakeProfitPips
Distância em pips para o take-profit de cada perna (0 desabilita).
InitialStopPips
Distância de parada base em pips. A parada real aumenta com o número de slots livres na grade.
TrailingStopPips
Distância de trailing stop em pips aplicada após a perna ser suficientemente lucrativa (0 desabilita).
MaxTrades
Número máximo de entradas simultâneas de martingale.
LotMultiplier
Multiplicador aplicado ao volume de cada perna de grade adicional (substituído por 1.5 quando MaxTrades > 12).
GridStepPips
Movimento adverso mínimo de preço (em pips) necessário antes de abrir a próxima entrada na grade.
OrdersToProtect
Número mínimo de pernas ativas antes que a proteção de lucro flutuante possa fechar a última negociação.
UseMoneyManagement
Permite o cálculo dinâmico de lotes com base no patrimônio da conta.
AccountType
Seleciona a fórmula de risco: 0 – Padrão (patrimônio líquido / 10.000); 1 – Normal (patrimônio líquido/100.000); 2 – Nano (patrimônio líquido/1.000).
RiskPercent
Percentagem de capital próprio utilizado quando a gestão de dinheiro está ativada.
ReverseSignal
Inverte sinais MACD longos/curtos.
FastEmaLength, SlowEmaLength, SignalLength
MACD períodos (26/12/9 por padrão).
SignalShift
Número de barras fechadas usadas para a verificação de cruzamento (padrão: 1).
TradingRangePips
MACD banda de sinal (em pips) que deve ser violada antes que um cruzamento seja aceito.
UseTimeFilter
Ativa a proteção de sessão com base em StopHour/StartHour.
StopHour, StartHour
Faixa exclusiva que bloqueia a criação de uma nova grade quando UseTimeFilter for verdadeiro.
Notas de gerenciamento de dinheiro
Quando UseMoneyManagement está desativado, o lote base (Volume) é usado diretamente. Caso contrário, o EA calcula o tamanho do lote a partir do patrimônio atual usando as mesmas fórmulas do EA original:
Tipo de conta 0: Ceil(risk% * equity / 10,000) / 10
Tipo de conta 1: risk% * equity / 100,000
Tipo de conta 2: risk% * equity / 1,000
Os volumes são normalizados com Security.VolumeStep e, em seguida, limitados por Security.MinVolume/MaxVolume.
Fluxo de trabalho de execução
Assine o fluxo de velas configurado e alimente o indicador MACD por meio de BindEx.
Em cada vela finalizada, atualize a lógica de trailing/stop para as pernas ativas.
Quando as regras de cruzamento MACD forem acionadas, certifique-se de que o filtro da sessão permita a negociação, que a direção da grade corresponda à posição existente e que o preço tenha se movido GridStepPips em relação ao último preenchimento.
Calcule o volume da próxima perna usando o multiplicador de martingale e envie uma ordem de mercado.
Monitore o lucro flutuante; assim que o limite de proteção for atingido, feche a perna mais recente e faça uma pausa até a próxima vela.
Notas de conversão
Todos os comentários foram reescritos em inglês conforme necessário.
É usado StockSharp API de alto nível (velas + BindEx). O acesso direto ao valor do indicador é evitado.
Os cálculos de lucro flutuante baseiam-se em PriceStep/StepPrice. Para instrumentos exóticos certifique-se de que estes campos estejam preenchidos.
A estratégia mantém o estado por trecho internamente para emular o gerenciamento de pedidos MQL4, porque StockSharp agrega posições por padrão.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// 10point3 MACD Grid: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class TenPointThreeMacdGridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public TenPointThreeMacdGridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 12)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 26)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI 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 FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class ten_point_three_macd_grid_strategy(Strategy):
"""EMA crossover with RSI filter and ATR stops."""
def __init__(self):
super(ten_point_three_macd_grid_strategy, self).__init__()
self._fast_ema = self.Param("FastEmaLength", 12).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_ema = self.Param("SlowEmaLength", 26).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetDisplay("RSI Length", "RSI period", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(ten_point_three_macd_grid_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
def OnStarted2(self, time):
super(ten_point_three_macd_grid_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
fast = ExponentialMovingAverage()
fast.Length = self._fast_ema.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_ema.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, rsi, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._prev_fast == 0 or self._prev_slow == 0 or atr_val <= 0:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fast_val < slow_val and self._prev_fast >= self._prev_slow) or close <= self._entry_price - atr_val * 2:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if (fast_val > slow_val and self._prev_fast <= self._prev_slow) or close >= self._entry_price + atr_val * 2:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
if fast_val > slow_val and self._prev_fast <= self._prev_slow and rsi_val > 50:
self._entry_price = close
self.BuyMarket()
elif fast_val < slow_val and self._prev_fast >= self._prev_slow and rsi_val < 50:
self._entry_price = close
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return ten_point_three_macd_grid_strategy()