La estrategia replica el asesor experto "55 MA" de MetaTrader 5 comparando dos puntos de una media móvil de 55 periodos y operando cuando su diferencia supera un umbral configurable. Todos los cálculos se realizan en velas completadas dentro de una sesión intradía definida por el usuario, y la dirección de la operación puede invertirse opcionalmente. El algoritmo preserva el comportamiento original donde se abre una posición corta siempre que no se cumpla ninguna condición alcista.
Lógica de trading
Suscribirse a la serie de velas seleccionada y calcular una media móvil con la longitud, el método y el precio aplicado elegidos.
Mantener los valores de la media móvil más recientes en un búfer para que los valores en los índices de barra BarA y BarB puedan accederse incluso cuando se usa un desplazamiento horizontal de MA.
Cuando llega una vela finalizada dentro de la ventana [StartHour, EndHour):
Obtener el valor de MA en BarA + MaShift y BarB + MaShift.
Si el valor en BarA supera al valor en BarB en más de DifferenceThreshold, abrir una posición larga a menos que ReverseSignals esté habilitado.
Si el valor en BarA es menor que el valor en BarB en más de DifferenceThreshold, abrir una posición corta (o una posición larga cuando ReverseSignals está habilitado).
De lo contrario la estrategia mantiene el comportamiento original del EA y activa una entrada corta.
Las órdenes siempre se envían al mercado usando el Volume de la estrategia. Cuando CloseOppositePositions está habilitado, el tamaño solicitado se incrementa para neutralizar cualquier exposición opuesta antes de establecer la nueva posición.
Las protecciones opcionales de stop-loss y take-profit se adjuntan mediante StartProtection. Las distancias se expresan en pips, donde un pip equivale a PriceStep multiplicado por 10 para instrumentos cotizados con 3 o 5 dígitos decimales.
Entradas
Nombre
Tipo
Por defecto
Descripción
CandleType
DataType
Marco temporal de 1 minuto
Serie de velas utilizada para cálculos y señales.
StopLossPips
int
30
Distancia del stop-loss en pips. Establecer en 0 para deshabilitar.
TakeProfitPips
int
50
Distancia del take-profit en pips. Establecer en 0 para deshabilitar.
StartHour
int
8
Hora inclusiva (0-23) que marca el inicio de la sesión de trading.
EndHour
int
21
Hora exclusiva (0-23) que marca el fin de la sesión de trading. Debe ser mayor que StartHour.
DifferenceThreshold
decimal
0.0001
Diferencia absoluta mínima entre los valores de MA comparados que activa una señal direccional.
BarA
int
0
Índice de la primera barra usada para la comparación de MA (0 = vela actual).
BarB
int
1
Índice de la segunda barra usada para la comparación de MA.
ReverseSignals
bool
false
Invierte las condiciones alcistas y bajistas.
CloseOppositePositions
bool
false
Si está habilitado, aumenta el tamaño de la orden para cerrar cualquier posición en la dirección opuesta antes de abrir el nuevo trade.
MaShift
int
0
Desplazamiento horizontal aplicado a la línea de media móvil. Valores positivos acceden a puntos de MA más antiguos.
MaLength
int
55
Periodo de la media móvil.
MaMethod
MovingAverageMethods
Exponential
Método de suavizado (Simple, Exponential, Smoothed, Weighted).
AppliedPrice
AppliedPriceTypes
Median
Precio usado como entrada de MA (Close, Open, High, Low, Median, Typical, Weighted).
Gestión de posiciones
Establecer el Volume de la estrategia para controlar el tamaño base del trade. Se combina con la posición actual cuando CloseOppositePositions está activo.
Las protecciones de stop-loss y take-profit son opcionales. Se adjuntan únicamente cuando la distancia en pips respectiva es mayor que cero.
Notas
La ventana de trading funciona en el tiempo del instrumento; las señales fuera de [StartHour, EndHour) son omitidas.
Cuando MaShift produce índices negativos, la estrategia espera hasta que se acumule suficiente historial, reflejando el comportamiento original del EA donde los búfers desplazados pueden devolver EMPTY_VALUE.
Dado que el experto original siempre tiene como valor predeterminado una orden de venta cuando no se cumple el umbral de diferencia, la estrategia convertida mantiene la misma lógica para plena fidelidad. Ajustar DifferenceThreshold si este comportamiento no es deseado.
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>
/// 55 MA bar comparison strategy. Compares candle body with MA direction.
/// </summary>
public class FiftyFiveMaBarComparisonStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private decimal? _prevMa;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public FiftyFiveMaBarComparisonStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_maPeriod = Param(nameof(MaPeriod), 55)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMa = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMa = null;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevMa = maVal;
return;
}
if (_prevMa == null)
{
_prevMa = maVal;
return;
}
var close = candle.ClosePrice;
var bullishBar = candle.ClosePrice > candle.OpenPrice;
var bearishBar = candle.ClosePrice < candle.OpenPrice;
var maRising = maVal > _prevMa.Value;
var maFalling = maVal < _prevMa.Value;
_prevMa = maVal;
// Bullish bar + rising MA + close above MA → buy
if (bullishBar && maRising && close > maVal && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Bearish bar + falling MA + close below MA → sell
else if (bearishBar && maFalling && close < maVal && Position >= 0)
{
if (Position > 0)
SellMarket();
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fifty_five_ma_bar_comparison_strategy(Strategy):
def __init__(self):
super(fifty_five_ma_bar_comparison_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ma_period = self.Param("MaPeriod", 55) \
.SetDisplay("MA Period", "Moving average period", "Indicators")
self._prev_ma = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def MaPeriod(self):
return self._ma_period.Value
def OnReseted(self):
super(fifty_five_ma_bar_comparison_strategy, self).OnReseted()
self._prev_ma = None
def OnStarted2(self, time):
super(fifty_five_ma_bar_comparison_strategy, self).OnStarted2(time)
self._prev_ma = None
sma = SimpleMovingAverage()
sma.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
mv = float(ma_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_ma = mv
return
if self._prev_ma is None:
self._prev_ma = mv
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
bullish_bar = close > open_price
bearish_bar = close < open_price
ma_rising = mv > self._prev_ma
ma_falling = mv < self._prev_ma
self._prev_ma = mv
# Bullish bar + rising MA + close above MA
if bullish_bar and ma_rising and close > mv and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Bearish bar + falling MA + close below MA
elif bearish_bar and ma_falling and close < mv and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return fifty_five_ma_bar_comparison_strategy()