Esta estrategia es una StockSharp versión de alto nivel del clásico asesor experto Promedio móvil que se envía con MetaTrader 4. El sistema observa velas completadas y las compara con un promedio móvil simple desplazado (SMA) para detectar cambios de dirección. Las órdenes siempre se ejecutan en el mercado y la estrategia permanece en el mercado con como máximo una posición abierta en cualquier momento.
Lógica de trading
Suscríbete a velas del período de tiempo configurable (predeterminado: 5 minutos) y calcula un SMA con el período solicitado.
Cambie SMA por el número especificado de velas completadas para emular el comportamiento de la función iMA original.
Evalúe la vela terminada anterior:
Cruz alcista (abre por debajo del SMA desplazado y cierra por encima) desencadena una entrada larga cuando no hay ninguna posición abierta.
Cruz bajista (abre por encima y cierra por debajo del SMA desplazado) activa una entrada corta cuando no hay ninguna posición abierta.
Gestione las salidas utilizando las mismas reglas cruzadas:
Una posición larga se cierra cuando la última vela cruza por debajo del SMA desplazado.
Una posición corta se cierra cuando la última vela cruza por encima del SMA desplazado.
Solo puede existir una posición en cualquier momento, que coincida con el comportamiento del EA original que alternaba entre órdenes de compra y venta.
Parámetros
Nombre
Descripción
Predeterminado
CandleType
Serie de velas utilizadas para los cálculos. Se puede seleccionar cualquier período de tiempo DataType.
marco de tiempo de 5 minutos
MovingPeriod
Número de velas para la longitud SMA.
12
MovingShift
Compensación del valor SMA en velas completadas. Emula el argumento shift de iMA.
6
BaseVolume
Volumen de pedido predeterminado para las entradas. Se utiliza el mismo volumen tanto para operaciones largas como cortas.
1
Manejo de indicadores
Se crea un indicador SimpleMovingAverage en OnStarted y se vincula a la suscripción de vela a través del nivel alto Bind API.
La salida sin procesar de SMA se almacena en una pequeña cola FIFO para obtener el valor de hace MovingShift velas. No se realiza ningún recálculo manual del indicador.
La cola retiene solo valores MovingShift + 1, por lo que el uso de la memoria permanece constante incluso para turnos grandes.
Gestión de pedidos y riesgos
Los pedidos se realizan con BuyMarket/SellMarket y se dimensionan según el parámetro BaseVolume. Al cerrar, se utiliza el tamaño de la posición absoluta actual para garantizar una salida completa.
La implementación original de MetaTrader ajustó dinámicamente el tamaño del lote en función del margen libre y las pérdidas recientes. El puerto StockSharp mantiene la lógica determinista y delega el tamaño de la posición al usuario a través del parámetro BaseVolume. Esto evita depender de métricas de cuentas específicas del corredor y al mismo tiempo preserva las reglas de entrada/salida.
Notas de conversión
Las señales se evalúan en la vela anterior, coincidiendo con la verificación Volume[0] == 1 de MetaTrader que esperó una nueva barra antes de reaccionar.
Solo se procesan velas completadas (CandleStates.Finished) para evitar operaciones prematuras.
La estrategia utiliza los ayudantes de gráficos StockSharp para trazar velas, valores de indicadores y marcadores comerciales cuando hay un área de gráfico disponible.
Uso
Compile la estrategia dentro de StockSharp Designer, Shell o Runner.
Seleccione el instrumento deseado y asigne un portafolio.
Configure los parámetros si se requieren diferentes plazos, duraciones o volúmenes.
Iniciar la estrategia; se suscribirá a la serie de velas elegida, monitoreará SMA cruces y operará en consecuencia.
Más ideas
Agregue paradas protectoras o niveles de toma de ganancias usando StartProtection si se requiere una gestión de riesgos más allá de la salida de reversión básica.
Reemplace el SMA simple con otro indicador (EMA, LWMA, etc.) modificando la instancia del indicador manteniendo el flujo de trabajo de suscripción existente.
Introduzca reglas de escala de posición ajustando el método GetEntryVolume.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MovingAverageShiftStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
private int _cooldownRemaining;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MovingAverageShiftStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevEma = ema;
}
}
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 moving_average_shift_strategy(Strategy):
def __init__(self):
super(moving_average_shift_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(moving_average_shift_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(moving_average_shift_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_ema = ema_val
return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return moving_average_shift_strategy()