La estrategia Double Up es un puerto directo del MetaTrader asesor experto DoubleUp.mq4. Combina un oscilador del índice de canal de productos básicos (CCI) con la línea principal del indicador MACD para detectar condiciones de impulso extremas y luego aplica un modelo de tamaño de posición estilo martingala. Siempre que ambos osciladores alcanzan la misma zona extrema, el algoritmo se prepara para operar en la dirección opuesta. Una vez que CCI regresa hacia el punto medio, la estrategia abre una nueva posición larga (después de cerrar los cortos existentes) o abre una nueva posición corta (después de cerrar los largos existentes).
El volumen de cada nueva posición se basa en una progresión exponencial (baseVolume * 2^lossCounter). Las salidas perdedoras consecutivas aumentan el exponente, mientras que una salida rentable restablece la progresión según el buffer de espera acumulado. Este comportamiento refleja la lógica piramidal del código original donde las variables pos y wait controlan el multiplicador aplicado.
Lógica de trading
Suscríbase a una sola serie de velas y calcule la línea principal CCI (longitud predeterminada 8) y MACD (rápida 13 predeterminada, 33 lenta, señal 2).
Multiplique la lectura de MACD por un millón para que su magnitud coincida con el nivel de umbral de CCI.
Cuando ambos osciladores superen +Threshold, prepare la estrategia para una futura entrada corta. Cuando ambos osciladores caigan por debajo de -Threshold, prepárelo para una futura entrada larga.
Una entrada larga pendiente se ejecuta tan pronto como el valor CCI vuelve a estar por debajo de +Threshold. Una entrada corta pendiente se ejecuta cuando CCI cae por debajo de -Threshold mientras la bandera corta está activa, reproduciendo el orden exacto del código original.
Antes de abrir una nueva posición, el algoritmo cierra completamente cualquier exposición opuesta. La nueva orden se envía sólo después de que finalizan todas las operaciones de cierre.
Las operaciones de salida son órdenes de mercado generadas durante las reversiones de señales. La ganancia o pérdida realizada de cada operación cerrada alimenta los contadores de martingala.
Modelo de dimensionamiento de posiciones
Las salidas perdedoras incrementan el contador de pérdidas. Una vez que el contador llega a PreWait, su valor se agrega al búfer de espera y el contador de pérdidas se restablece a cero.
Una salida rentable transfiere el valor del buffer de espera (truncado) al contador de pérdidas y borra el buffer. Por lo tanto, los tamaños de las operaciones futuras comienzan a partir de 2^lossCounter lotes.
El búfer de espera se inicializa desde InitialWait y, por lo demás, está controlado por las reglas anteriores.
Parámetros
Nombre
Predeterminado
Descripción
CciPeriod
8
Período del índice del canal de materias primas.
Threshold
230
Nivel absoluto utilizado para detectar extremos del oscilador.
MacdFastPeriod
13
Duración rápida de EMA del cálculo MACD.
MacdSlowPeriod
33
Longitud lenta de EMA del cálculo MACD.
MacdSignalPeriod
2
Longitud de la señal EMA, necesaria para coincidir con la firma de llamada MetaTrader.
BaseVolume
0,01
Multiplicador de volumen inicial antes de aplicar el exponente de martingala.
InitialWait
0
Valor inicial del búfer de espera (variable wait en el script original).
PreWait
2
Número mínimo de salidas perdedoras consecutivas requeridas antes de que el búfer de espera acumule el contador de pérdidas.
BackShift
0
Cambio histórico de lecturas de indicadores. En este puerto solo se admite cero.
CandleType
plazo de 15 minutos
Tipo de vela solicitado desde la fuente de datos. Ajústelo para que coincida con el período de tiempo del gráfico utilizado en MetaTrader.
Notas
Actualmente, el puerto solo admite BackShift = 0, lo que refleja la configuración predeterminada del asesor experto original.
Cada envío y cierre de órdenes utiliza órdenes de mercado. Adjunte protecciones separadas (stop-loss, take-profit) si es necesario.
Debido a que la estrategia duplica el tamaño de la posición después de perder operaciones, asegúrese de que los límites de margen y los controles de riesgo sean apropiados para el instrumento negociado.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class DoubleUpStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _cooldownRemaining;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DoubleUpStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 100).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();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevFast = fast;
_prevSlow = slow;
}
}