Conversão do consultor especialista MetaTrader 5 "Crossing Moving Average (barabashkakvn's edition)" do fonte MQL/21515.
Implementa a lógica sobre a API de alto nível do StockSharp com assinaturas de velas e vinculação de indicadores.
Projetado para instrumentos onde o momentum e os cruzamentos de médias móveis capturam reversões de tendência.
Este pacote contém apenas a versão em C#. Uma tradução para Python é intencionalmente omitida conforme solicitado.
Ideia Central
A estratégia monitora duas médias móveis configuráveis (rápida e lenta) com deslocamentos opcionais para frente e combina seu cruzamento com um filtro de confirmação de momentum. Um trade é aberto apenas quando:
A média rápida cruza a média lenta por pelo menos a distância mínima configurada (em pips) durante as duas barras completadas mais recentes.
O indicador de momentum sobe acima (para comprado) ou cai abaixo (para vendido) do limite definido pelo usuário e melhora na direção do trade.
A fonte de preço do sinal pode ser escolhida entre preços de vela de abertura, máximo, mínimo, fechamento, mediana, típico ou ponderado para imitar os modos de preço aplicado do MetaTrader.
Gestão de Risco e Trade
O volume de ordem é fixo por trade e é aplicado tanto ao entrar em uma nova posição quanto ao reverter uma posição existente.
As distâncias de stop-loss / take-profit são configuradas em pips e automaticamente traduzidas em offsets de preço usando Security.PriceStep. Para instrumentos cotados com 3 ou 5 dígitos decimais, a estratégia multiplica o passo por 10 para reproduzir o tamanho de pip do MetaTrader.
O trailing stop ativa após o preço se mover por TrailingStop + TrailingStep (em pips) desde a entrada. Uma vez ativado, o stop é movido para preço atual - TrailingStop para posições compradas (ou preço atual + TrailingStop para vendidas) sempre que puder avançar pelo menos TrailingStep pips.
Os níveis protetores são avaliados em cada vela terminada: se o intervalo da vela toca o stop-loss ou take-profit, a posição é fechada ao mercado para imitar a execução de ordens no MetaTrader.
Indicadores
Média Móvel Rápida – período configurável, deslocamento e método de suavização (SMA, EMA, SMMA, WMA).
Média Móvel Lenta – mesmas opções que a MA rápida.
Momentum – período e fonte de preço idênticos às médias móvies. A estratégia detecta automaticamente se o indicador emite valores em torno de 0 ou 100 e aplica o filtro adequadamente.
Lógica de Sinais
Aguardar até que todos os indicadores estejam completamente formados. O algoritmo mantém um histórico interno dos valores mais recentes para avaliar cruzamentos deslocados exatamente como no consultor especialista original.
Calcular a distância de preço entre as médias rápida e lenta nas duas barras anteriores (com deslocamentos aplicados). A linha rápida deve cruzar a linha lenta e superar o filtro de distância mínima.
Recuperar os valores de momentum nas mesmas barras. Para entradas compradas, o momentum atual deve ser maior que tanto o limite configurado quanto o valor de momentum anterior; para entradas vendidas, o oposto é necessário.
Se um novo sinal aparece enquanto a posição é oposta, a estratégia fecha a posição existente e imediatamente abre uma na nova direção com o tamanho de lote configurado.
Referência de Parâmetros
Parâmetro
Descrição
Padrão
OrderVolume
Volume base usado para cada ordem de mercado.
1
StopLossPips
Distância de stop-loss em pips (0 desabilita o stop).
50
TakeProfitPips
Distância de take-profit em pips (0 desabilita o alvo).
50
TrailingStopPips
Distância do trailing stop em pips (0 desabilita o trailing).
5
TrailingStepPips
Melhoria mínima em pips necessária para mover o trailing stop.
5
MinDistancePips
Separação mínima entre MAs para validar o cruzamento.
0
MomentumFilter
Diferença mínima de momentum necessária para permitir entradas.
0.1
FastPeriod / FastShift
Comprimento da MA rápida e deslocamento horizontal (barras).
13 / 1
SlowPeriod / SlowShift
Comprimento da MA lenta e deslocamento horizontal (barras).
34 / 3
MaMethod
Tipo de suavização de média móvel (Simple, Exponential, Smoothed, Weighted).
Exponential
AppliedPrice
Preço da vela usado para cálculos do indicador.
Close
MomentumPeriod
Comprimento de retrospectiva do momentum em barras.
14
CandleType
Tipo de dados de velas fornecidas à estratégia.
TimeFrame(1m)
Notas Práticas
Sempre garantir que Security.PriceStep esteja configurado para o instrumento; caso contrário, o gerenciamento de risco baseado em pips recorrerá a unidades de preço brutas.
A lógica de trailing requer um TrailingStepPips positivo quando TrailingStopPips está habilitado — refletindo a validação original do MetaTrader.
Como os níveis de stop e take são avaliados nos intervalos das velas, o uso de velas de maior resolução fornece uma aproximação mais próxima da execução baseada em ticks.
Mensagens de registro em entradas e ajustes de trailing são incluídas para facilitar a depuração e a otimização de parâmetros.
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>
/// Moving average crossover strategy.
/// Enters long when fast EMA crosses above slow EMA, enters short on the opposite crossover.
/// </summary>
public class CrossingMovingAverageStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public CrossingMovingAverageStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 34)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, 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 fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
// Crossover detection
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast;
_prevSlow = slow;
// Golden cross: fast crosses above slow
if (!prevAbove && currAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
// Death cross: fast crosses below slow
else if (prevAbove && !currAbove)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
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
from StockSharp.Algo.Strategies import Strategy
class crossing_moving_average_strategy(Strategy):
def __init__(self):
super(crossing_moving_average_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 13) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 34) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(crossing_moving_average_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(crossing_moving_average_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
# Golden cross: fast crosses above slow
if not prev_above and curr_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
# Death cross: fast crosses below slow
elif prev_above and not curr_above:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return crossing_moving_average_strategy()