Conversión del asesor experto de MetaTrader 5 "Crossing Moving Average (barabashkakvn's edition)" del fuente MQL/21515.
Implementa la lógica sobre la API de alto nivel de StockSharp con suscripciones de velas y vinculación de indicadores.
Diseñado para instrumentos donde el momentum y los cruces de medias móviles capturan reversiones de tendencia.
Este paquete contiene solo la versión en C#. Una traducción a Python se omite intencionalmente según lo solicitado.
Idea Central
La estrategia monitorea dos medias móviles configurables (rápida y lenta) con desplazamientos opcionales hacia adelante y combina su cruce con un filtro de confirmación de momentum. Un trade se abre solo cuando:
La media rápida cruza la media lenta por al menos la distancia mínima configurada (en pips) durante las dos barras completadas más recientes.
El indicador de momentum sube por encima (para largo) o cae por debajo (para corto) del umbral definido por el usuario y mejora en la dirección del trade.
La fuente de precio de la señal puede elegirse entre precios de vela de apertura, máximo, mínimo, cierre, mediana, típico o ponderado para imitar los modos de precio aplicado de MetaTrader.
Gestión de Riesgo y Trade
El volumen de orden es fijo por trade y se aplica tanto cuando se entra en una posición nueva como cuando se revierte una posición existente.
Las distancias de stop-loss / take-profit se configuran en pips y se traducen automáticamente en offsets de precio usando Security.PriceStep. Para instrumentos cotizados con 3 o 5 dígitos decimales, la estrategia multiplica el paso por 10 para reproducir el tamaño de pip de MetaTrader.
El trailing stop se activa después de que el precio se mueve por TrailingStop + TrailingStep (en pips) desde la entrada. Una vez activado, el stop se mueve a precio actual - TrailingStop para posiciones largas (o precio actual + TrailingStop para cortas) siempre que pueda avanzar al menos TrailingStep pips.
Los niveles protectores se evalúan en cada vela terminada: si el rango de la vela toca el stop-loss o take-profit, la posición se cierra al mercado para imitar la ejecución de órdenes en MetaTrader.
Indicadores
Media Móvil Rápida – período configurable, desplazamiento y método de suavizado (SMA, EMA, SMMA, WMA).
Media Móvil Lenta – mismas opciones que la MA rápida.
Momentum – período y fuente de precio idénticos a las medias móviles. La estrategia detecta automáticamente si el indicador emite valores alrededor de 0 o 100 y aplica el filtro en consecuencia.
Lógica de Señales
Esperar hasta que todos los indicadores estén completamente formados. El algoritmo mantiene un historial interno de los valores más recientes para evaluar los cruces desplazados exactamente como en el asesor experto original.
Calcular la distancia de precio entre las medias rápida y lenta en las dos barras anteriores (con desplazamientos aplicados). La línea rápida debe cruzar la línea lenta y superar el filtro de distancia mínima.
Recuperar los valores de momentum en las mismas barras. Para entradas largas, el momentum actual debe ser mayor que tanto el umbral configurado como el valor de momentum anterior; para entradas cortas, se requiere lo opuesto.
Si aparece una nueva señal mientras la posición es opuesta, la estrategia cierra la posición existente e inmediatamente abre una en la nueva dirección con el tamaño de lote configurado.
Referencia de Parámetros
Parámetro
Descripción
Predeterminado
OrderVolume
Volumen base usado para cada orden de mercado.
1
StopLossPips
Distancia de stop-loss en pips (0 deshabilita el stop).
50
TakeProfitPips
Distancia de take-profit en pips (0 deshabilita el objetivo).
50
TrailingStopPips
Distancia del trailing stop en pips (0 deshabilita el trailing).
5
TrailingStepPips
Mejora mínima en pips requerida para mover el trailing stop.
5
MinDistancePips
Separación mínima entre MAs para validar el cruce.
0
MomentumFilter
Diferencia de momentum mínima requerida para permitir entradas.
0.1
FastPeriod / FastShift
Longitud de MA rápida y desplazamiento horizontal (barras).
13 / 1
SlowPeriod / SlowShift
Longitud de MA lenta y desplazamiento horizontal (barras).
34 / 3
MaMethod
Tipo de suavizado de media móvil (Simple, Exponential, Smoothed, Weighted).
Exponential
AppliedPrice
Precio de la vela usado para cálculos del indicador.
Close
MomentumPeriod
Longitud de retrospección del momentum en barras.
14
CandleType
Tipo de datos de velas suministradas a la estrategia.
TimeFrame(1m)
Notas Prácticas
Siempre asegurarse de que Security.PriceStep esté configurado para el instrumento; de lo contrario, la gestión de riesgo basada en pips recurrirá a unidades de precio brutas.
La lógica de trailing requiere un TrailingStepPips positivo cuando TrailingStopPips está habilitado—reflejando la validación original de MetaTrader.
Dado que los niveles de stop y take se evalúan en los rangos de velas, el uso de velas de mayor resolución proporciona una aproximación más cercana a la ejecución basada en ticks.
Se incluyen mensajes de registro en entradas y ajustes de trailing para facilitar la depuración y la optimización 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()