A estratégia reproduz o comportamento do MetaTrader consultor especialista Stat_Euclidean_Metric.mq4. Ele monitora MACD reversões em um único instrumento e período. Quando a linha MACD forma um ponto de inflexão local, a estratégia abre uma posição imediatamente (modo de treinamento) ou valida a configuração com um classificador k-vizinhos mais próximos (k-NN) que compara a estrutura atual do mercado com vetores de recursos históricos armazenados em arquivos binários.
Lógica de negociação
Assine o tipo de vela configurado e calcule o indicador MACD no preço típico ((High + Low + Close) / 3).
Detecte uma reversão de baixa quando os últimos três valores MACD concluídos satisfizerem MACD[2] <= MACD[1] e MACD[1] > MACD[0].
Detecte uma reversão de alta quando MACD[2] >= MACD[1] e MACD[1] < MACD[0].
Dependendo do modo selecionado:
Modo de treinamento (TrainingMode = true) – abra uma ordem de mercado na direção da reversão após, opcionalmente, fechar a posição atual. Isso imita o comportamento original EA ao coletar novas amostras.
Modo classificador (TrainingMode = false) – calcule cinco proporções de médias móveis simples do preço típico e avalie a probabilidade de sucesso com um modelo k-NN. Faça pedidos somente se a probabilidade ultrapassar os limites configurados.
Aplique o módulo StartProtection integrado para anexar níveis de stop-loss e take-profit nas etapas do instrumento.
Vetor de recursos para classificação
O modelo k-NN usa as seguintes proporções calculadas na vela recém-fechada:
SMA(89) / SMA(144)
SMA(144) / SMA(233)
SMA(21) / SMA(89)
SMA(55) / SMA(89)
SMA(2) / SMA(55)
Cada amostra armazenada nos arquivos do conjunto de dados contém seis valores double: as cinco proporções acima e um rótulo (0 para um resultado desfavorável, 1 para uma negociação bem-sucedida). Durante a avaliação, a estratégia seleciona as amostras NeighborCount mais próximas, calcula a média de seus rótulos e interpreta o resultado como a probabilidade de sucesso.
Arquivos de conjunto de dados
BuyDatasetPath – caminho para o arquivo binário com vetores coletados após negociações de alta.
SellDatasetPath – caminho para o arquivo binário com vetores coletados após negociações de baixa.
Se um caminho for relativo, ele será resolvido em Environment.CurrentDirectory. Os arquivos ausentes são relatados no log e tratados como um conjunto de dados vazio. Esta implementação lê conjuntos de dados, mas não atualiza nem acrescenta novas amostras automaticamente; a exportação de novos vetores deve ser tratada externamente durante a execução no modo de treinamento.
Parâmetros
TrainingMode – alterne entre negociação MACD pura e negociação assistida por classificador.
BuyThreshold / SellThreshold – probabilidade mínima retornada pelo classificador para abrir negociações na direção primária.
AllowInverseEntries – permite negociações contrárias quando a probabilidade é extremamente baixa.
InverseBuyThreshold / InverseSellThreshold – probabilidade máxima que ainda desencadeia uma negociação na direção oposta.
FastLength / SlowLength / SignalLength – MACD EMA comprimentos.
TakeProfitPoints / StopLossPoints – níveis de proteção expressos em etapas do instrumento.
ClosePositionsOnSignal – fecha a posição líquida atual antes de enviar uma nova ordem.
BuyDatasetPath / SellDatasetPath – arquivos binários que armazenam vetores históricos.
NeighborCount – número de vizinhos usados na votação k-NN.
CandleType – série de velas usada para todos os indicadores.
Recomendações de uso
Forneça caminhos absolutos ou relativos ao diretório de trabalho para os arquivos do conjunto de dados antes de ativar o modo classificador.
Colete amostras de alta qualidade executando a estratégia no modo de treinamento em dados históricos e exportando vetores manualmente.
Otimize os limites e a contagem de vizinhos para adaptar o classificador a novos mercados ou instrumentos.
Mantenha o parâmetro Volume do instrumento alinhado ao modelo de risco, pois a estratégia sempre abre Volume + |Position| lotes para reverter a posição líquida quando necessário.
Diferenças da versão MQL4
Os conjuntos de dados do classificador são apenas lidos; o EA original grava novas amostras durante a desinicialização. Aqui o usuário deve atualizar os arquivos manualmente após analisar o histórico de negociações.
Todas as ordens de proteção são anexadas por meio de StockSharp StartProtection em vez de parâmetros manuais OrderSend.
O fechamento de ordem no modo classificador sempre sai de toda a posição quando ClosePositionsOnSignal está habilitado, enquanto o script MQL4 fecha apenas ordens lucrativas antes de receber novos sinais.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD reversal strategy with moving average ratio filter.
/// Enters on MACD histogram reversals filtered by MA trend alignment.
/// </summary>
public class StatEuclideanMetricStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _trendMaLength;
private readonly List<decimal> _macdHistory = new();
public StatEuclideanMetricStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_fastLength = Param(nameof(FastLength), 12)
.SetDisplay("Fast Length", "Fast EMA period for MACD.", "Indicators");
_slowLength = Param(nameof(SlowLength), 26)
.SetDisplay("Slow Length", "Slow EMA period for MACD.", "Indicators");
_trendMaLength = Param(nameof(TrendMaLength), 50)
.SetDisplay("Trend MA Length", "Period for trend filter MA.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public int TrendMaLength
{
get => _trendMaLength.Value;
set => _trendMaLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macdHistory.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var trendMa = new SimpleMovingAverage { Length = TrendMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, trendMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, trendMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue, decimal trendValue)
{
if (candle.State != CandleStates.Finished)
return;
var macdLine = fastValue - slowValue;
_macdHistory.Add(macdLine);
if (_macdHistory.Count > 5)
_macdHistory.RemoveAt(0);
if (_macdHistory.Count < 3)
return;
var close = candle.ClosePrice;
var macd1 = _macdHistory[^1];
var macd2 = _macdHistory[^2];
var macd3 = _macdHistory[^3];
// MACD reversal patterns
var buyReversal = macd3 >= macd2 && macd2 < macd1; // V-shape bottom
var sellReversal = macd3 <= macd2 && macd2 > macd1; // inverted V top
// Exit conditions
if (Position > 0 && sellReversal)
{
SellMarket();
}
else if (Position < 0 && buyReversal)
{
BuyMarket();
}
// Entry conditions with trend filter
if (Position == 0)
{
if (buyReversal && close > trendValue)
{
BuyMarket();
}
else if (sellReversal && close < trendValue)
{
SellMarket();
}
}
}
}
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, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class stat_euclidean_metric_strategy(Strategy):
"""MACD reversal with trend MA filter."""
def __init__(self):
super(stat_euclidean_metric_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12).SetDisplay("Fast Length", "Fast EMA for MACD", "Indicators")
self._slow_length = self.Param("SlowLength", 26).SetDisplay("Slow Length", "Slow EMA for MACD", "Indicators")
self._trend_ma_length = self.Param("TrendMaLength", 50).SetDisplay("Trend MA Length", "Period for trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).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(stat_euclidean_metric_strategy, self).OnReseted()
self._macd_history = []
def OnStarted2(self, time):
super(stat_euclidean_metric_strategy, self).OnStarted2(time)
self._macd_history = []
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_length.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_length.Value
trend_ma = SimpleMovingAverage()
trend_ma.Length = self._trend_ma_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast_ema, slow_ema, trend_ma, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, trend_ma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val, trend_val):
if candle.State != CandleStates.Finished:
return
macd_line = fast_val - slow_val
self._macd_history.append(macd_line)
if len(self._macd_history) > 5:
self._macd_history.pop(0)
if len(self._macd_history) < 3:
return
close = float(candle.ClosePrice)
m1 = self._macd_history[-1]
m2 = self._macd_history[-2]
m3 = self._macd_history[-3]
buy_reversal = m3 >= m2 and m2 < m1
sell_reversal = m3 <= m2 and m2 > m1
# Exits
if self.Position > 0 and sell_reversal:
self.SellMarket()
elif self.Position < 0 and buy_reversal:
self.BuyMarket()
# Entries
if self.Position == 0:
if buy_reversal and close > trend_val:
self.BuyMarket()
elif sell_reversal and close < trend_val:
self.SellMarket()
def CreateClone(self):
return stat_euclidean_metric_strategy()