Esta estrategia es una conversión a C# del experto de MetaTrader Exp_X2MACandle_MMRec. Observa el color de una vela doblemente suavizada, producida por el indicador personalizado X2MA original, para decidir cuándo abrir o cerrar posiciones. La versión de StockSharp recrea el pipeline de doble suavizado y mantiene una capa ligera de gestión de dinero que reduce el volumen de trading tras un número configurable de pérdidas recientes.
El algoritmo procesa solo velas completadas. Se suscribe a un marco temporal configurable, aplica dos medias móviles encadenadas a los valores OHLC de la vela, deriva un color sintético de vela (verde, gris o rojo) y usa transiciones de color con un desplazamiento de barra seleccionable por el usuario para activar acciones. Las operaciones largas se abren cuando el color cambia de alcista a cualquier otra cosa. Las operaciones cortas siguen la condición simétrica. Las salidas de posición están alineadas con los mismos controles de color y pueden habilitarse o deshabilitarse por separado para cada lado.
Lógica del indicador
Cada vela se suaviza dos veces. Ambas etapas pueden usar diferentes métodos y longitudes.
Las opciones de suavizado se mapean a indicadores de StockSharp:
Simple → SimpleMovingAverage
Exponential → ExponentialMovingAverage
Smoothed → SmoothedMovingAverage (RMA)
Weighted → WeightedMovingAverage
Jurik → JurikMovingAverage (el parámetro Phase se respeta cuando está disponible).
El cuerpo de la vela sintética se aplana cuando la diferencia absoluta apertura/cierre es menor que GapPoints * Security.StepPrice.
Los colores se asignan de la siguiente manera: apertura < cierre → 2 (alcista), apertura > cierre → 0 (bajista), en caso contrario → 1 (neutral).
Las señales se evalúan en la barra SignalBar + 1 (dos barras atrás con la configuración predeterminada) para que las órdenes se envíen solo después de que una vela completa confirme el cambio de color.
Gestión de dinero
El experto original reducía dinámicamente el tamaño de la posición tras una serie de pérdidas usando estadísticas históricas de transacciones. StockSharp no expone el historial exacto de MetaTrader, por lo que el port mantiene una cola interna de transacciones cerradas recientes.
La longitud de la cola está controlada por HistoryDepth y el volumen cae a ReducedVolume una vez que se detectan LossTrigger o más pérdidas dentro de la ventana.
La estrategia registra los resultados de las transacciones usando los precios de cierre de las velas cuando se activa una salida manual. Las órdenes stop-loss/take-profit de la versión MetaTrader no se recrean. Puedes agregar tus propias reglas de protección a través de los gestores de riesgo de StockSharp si es necesario.
Parámetros
Nombre
Descripción
CandleType
Marco temporal de las velas usadas para el suavizado y el trading.
FirstMethod, FirstLength, FirstPhase
Método de suavizado primario, longitud y fase Jurik.
SecondMethod, SecondLength, SecondPhase
Método de suavizado secundario, longitud y fase Jurik.
GapPoints
Umbral de aplanamiento del cuerpo en pasos de precio.
SignalBar
Desplazamiento (0 = última vela terminada) usado al leer los buffers de color.
AllowLongEntry / AllowShortEntry
Habilitar apertura de posiciones largas o cortas.
AllowLongExit / AllowShortExit
Habilitar cierre de posiciones largas o cortas.
NormalVolume
Tamaño de orden estándar (lotes, acciones, contratos).
ReducedVolume
Tamaño de orden usado tras el número configurado de pérdidas.
HistoryDepth
Número de transacciones recientes inspeccionadas para pérdidas (0 desactiva el seguimiento del historial).
LossTrigger
Recuento de pérdidas que activa el volumen reducido (0 desactiva el interruptor).
Notas de uso
La estrategia opera en un único instrumento devuelto por GetWorkingSecurities().
Las señales y salidas se procesan una vez por vela terminada para evitar órdenes duplicadas.
Establecer ReducedVolume igual a NormalVolume si deseas desactivar la reducción de volumen mientras mantienes las estadísticas del historial.
Dado que el port se basa en los precios de cierre de las velas para clasificar las transacciones, el contador de pérdidas puede diferir ligeramente de MetaTrader cuando ocurren deslizamientos o ejecuciones parciales. La documentación debería ayudarte a ajustar los parámetros para lograr un comportamiento similar.
Los stops y take-profits de la versión MQL no se recrean automáticamente. Usar los gestores de riesgo de StockSharp (StartProtection) si necesitas protección a nivel de plataforma.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ExpX2MaCandleMmRecStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _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 ExpX2MaCandleMmRecStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 8).SetGreaterThanZero().SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 25).SetGreaterThanZero().SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(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 fast = new WeightedMovingAverage { Length = FastPeriod };
var slow = new WeightedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); 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; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && 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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_x2_ma_candle_mm_rec_strategy(Strategy):
def __init__(self):
super(exp_x2_ma_candle_mm_rec_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", 8) \
.SetDisplay("Fast WMA", "Fast WMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 25) \
.SetDisplay("Slow WMA", "Slow WMA 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(exp_x2_ma_candle_mm_rec_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(exp_x2_ma_candle_mm_rec_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = WeightedMovingAverage()
fast.Length = self.FastPeriod
slow = WeightedMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
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
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return exp_x2_ma_candle_mm_rec_strategy()