Esta estrategia es un port en C# del asesor experto de MetaTrader 5 3sma.mq5 (id 21495). Sigue la misma idea de tradear cuando tres medias móviles simples se separan entre sí por un spread configurable. La implementación utiliza la API de alto nivel de StockSharp con suscripciones de velas y vinculación de indicadores para que no se requiera ninguna gestión manual de series.
Comportamiento original de MT5
El experto MT5 se basa en tres medias móviles simples con diferentes períodos y desplazamientos de visualización. La media rápida usa la barra actual, mientras que las medias media y lenta están desplazadas uno y dos barras hacia el pasado. En cada tick:
Convierte el spread definido por el usuario de pips a unidades de precio basándose en la precisión del símbolo.
Cierra posiciones largas cuando la SMA rápida cae por debajo de la SMA media al menos la mitad del spread, y cierra posiciones cortas cuando la SMA rápida sube por encima de la SMA media en la mitad del spread.
Abre nuevas posiciones largas si MA1 > MA2 + spread y MA2 > MA3 + spread mientras no queden otros trades largos del experto. Análogamente, abre posiciones cortas cuando las tres medias están alineadas en el orden opuesto.
Utiliza solo órdenes de mercado con un tamaño de lote fijo y no aplica niveles explícitos de stop-loss o take-profit.
Implementación en StockSharp
Indicadores – tres instancias de SimpleMovingAverage se suscriben a la misma fuente de velas. Los buffers de historial compactos reproducen los parámetros de "shift" de MT5 para que cada comparación use valores de barras terminadas con los desplazamientos solicitados.
Manejo del spread – el parámetro de spread se ingresa en pips. La estrategia deriva un tamaño de pip de Security.PriceStep (o Security.Step) y lo multiplica por diez para símbolos FX de 3/5 dígitos, coincidiendo con el ajuste de MT5 para cotizaciones fraccionales.
Flujo de órdenes – las órdenes se envían con BuyMarket/SellMarket. Cuando aparece una condición de reversión, la estrategia suma el valor absoluto de la posición neta actual al volumen base para aplanar la exposición opuesta y establecer la nueva dirección con una única orden de mercado.
Visualización – si hay gráficos disponibles, la estrategia traza las velas fuente junto con las tres medias móviles y los trades ejecutados.
Parámetros
Nombre
Descripción
Predeterminado
Volume
Volumen de orden usado para cada entrada de mercado.
0.1
FastMaPeriod
Período de la SMA rápida (equivalente a MA1 en MT5).
9
FastMaShift
Número de barras terminadas usadas para desplazar la SMA rápida.
0
MiddleMaPeriod
Período de la SMA media (MA2).
14
MiddleMaShift
Desplazamiento en barras terminadas para la SMA media.
1
SlowMaPeriod
Período de la SMA lenta (MA3).
29
SlowMaShift
Desplazamiento en barras terminadas para la SMA lenta.
2
MaSpreadPips
Spread mínimo requerido entre SMAs consecutivas medido en pips.
10
CandleType
Serie de velas usada para cálculos.
Marco temporal de 1 minuto
Lógica de trading
Esperar a que las tres medias móviles estén formadas y que los buffers de historial contengan valores para los desplazamientos solicitados.
Convertir el parámetro de spread de pips a unidades de precio y calcular el medio-spread para filtros de salida.
Filtros de salida –
Cerrar exposición larga si la SMA rápida desplazada cae por debajo de la SMA media desplazada en al menos la mitad del spread.
Cerrar exposición corta si la SMA rápida desplazada sube por encima de la SMA media desplazada en al menos la mitad del spread.
Condiciones de entrada –
Entrar largo (o revertir de corto a largo) cuando la SMA rápida es mayor que la SMA media más el spread y la SMA media es mayor que la SMA lenta más el spread.
Entrar corto (o revertir de largo a corto) cuando la SMA rápida es menor que la SMA media menos el spread y la SMA media es menor que la SMA lenta menos el spread.
Diferencias con la versión MT5
StockSharp trabaja con una única posición neta por instrumento. Cuando aparece una señal de reversión la estrategia emite una única orden de mercado dimensionada para aplanar la exposición neta anterior y establecer la nueva. El experto MT5 podía mantener posiciones largas y cortas independientes.
La conversión de pips usa los mejores metadatos de Security disponibles. Si el broker no proporciona ni PriceStep ni Step, se usa un valor de 1 como respaldo.
Las órdenes se envían en velas terminadas en lugar de cada tick porque la API de alto nivel opera en suscripciones de velas.
La estrategia no implementa los helpers de registro verboso del código MT5; se puede usar el registro integrado de StockSharp si es necesario.
Notas de uso
Asegurarse de que la serie de velas seleccionada coincida con el marco temporal usado en la configuración original de MT5.
Ajustar el parámetro de spread cuando el instrumento use tamaños de pip no estándar.
Dado que la estrategia trabaja con velas terminadas, la ejecución se retrasará hasta que la vela actual cierre.
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>
/// Triple SMA strategy that trades when three moving averages are properly aligned.
/// Enters long when fast > middle > slow, enters short when fast < middle < slow.
/// </summary>
public class TripleSmaSpreadStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _middlePeriod;
private readonly StrategyParam<int> _slowPeriod;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int MiddlePeriod
{
get => _middlePeriod.Value;
set => _middlePeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public TripleSmaSpreadStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicators");
_middlePeriod = Param(nameof(MiddlePeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Middle Period", "Middle SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 29)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var fastSma = new ExponentialMovingAverage { Length = FastPeriod };
var slowSma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastSma, slowSma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastSma);
DrawIndicator(area, slowSma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var signal = 0;
if (fast > slow && close > fast)
signal = 1;
else if (fast < slow && close < fast)
signal = -1;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
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 triple_sma_spread_strategy(Strategy):
def __init__(self):
super(triple_sma_spread_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", 9) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicators")
self._middle_period = self.Param("MiddlePeriod", 14) \
.SetDisplay("Middle Period", "Middle SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 29) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def MiddlePeriod(self):
return self._middle_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(triple_sma_spread_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(triple_sma_spread_strategy, self).OnStarted2(time)
self._prev_signal = 0
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
close = float(candle.ClosePrice)
fv = float(fast_value)
sv = float(slow_value)
signal = 0
if fv > sv and close > fv:
signal = 1
elif fv < sv and close < fv:
signal = -1
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return triple_sma_spread_strategy()