Ver en GitHub

Estrategia Reversing Martingale

Visión general

La estrategia Reversing Martingale es un port directo en C# del asesor experto de MetaTrader "Reversing Martingale EA". Mantiene continuamente una sola posición de mercado y alterna la dirección de la operación después de cada operación cerrada. Las operaciones perdedoras activan una progresión martingala de volumen, mientras que las rentables reinician el ciclo al tamaño de lote inicial. Todas las posiciones se protegen con niveles simétricos de stop-loss y take-profit expresados en puntos de precio.

La estrategia no depende de indicadores ni de estructura de mercado. Simplemente reacciona a posiciones completadas y mantiene la exposición de capital activa en todo momento (salvo que el trading esté desactivado).

Lógica principal

  1. Configuración inicial
    • Al iniciar, la estrategia envía de inmediato una orden de mercado usando Start Volume y la dirección configurada en First Trade Side.
    • Las órdenes protectoras de stop-loss y take-profit se adjuntan usando la distancia especificada en Target (points).
  2. Gestión de posición
    • Solo puede haber una posición abierta a la vez. La estrategia espera hasta que la posición actual se cierre por completo por sus órdenes protectoras o por acciones externas.
    • Después de cada salida, la estrategia invierte la dirección de operación (compra -> venta o venta -> compra).
    • Si la última operación realizó una pérdida, el volumen de la siguiente orden equivale al tamaño de posición anterior multiplicado por Lot Multiplier. En caso contrario, el volumen se reinicia a Start Volume.
  3. Continuación del ciclo
    • Una vez determinados el nuevo volumen y dirección, se envía inmediatamente la siguiente orden de mercado, manteniendo activo el ciclo martingala alternante.

Parámetros

Nombre Descripción
Start Volume Volumen inicial usado al comienzo de cada ciclo ganador.
Lot Multiplier Multiplicador de volumen aplicado después de una operación perdedora. Debe ser mayor que 1.
First Trade Side Dirección de la primera operación cuando comienza la sesión de estrategia.
Target (points) Distancia en pasos de precio usada para stop-loss y take-profit.
Order Comment Etiqueta de texto opcional asignada a cada orden de mercado generada.

Notas adicionales

  • La distancia de paso de precio se convierte en UnitTypes.Step y se pasa a StartProtection, por lo que stop-loss y take-profit están siempre activos.
  • Los ajustes de volumen respetan el paso de volumen, mínimos y máximos de la seguridad mediante el helper NormalizeVolume.
  • La estrategia espera eventos de ejecución del conector; si el trading se pausa o el conector está offline, el ciclo martingala se reanudará cuando el trading vuelva a estar permitido.
using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Reversing Martingale strategy: WMA crossover.
/// Buys when fast WMA crosses above slow WMA. Sells on cross below.
/// </summary>
public class ReversingMartingaleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	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 ReversingMartingaleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var fast = new WeightedMovingAverage { Length = FastPeriod };
		var slow = new WeightedMovingAverage { Length = SlowPeriod };

		decimal? prevFast = null;
		decimal? prevSlow = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, (candle, fastVal, slowVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevFast.HasValue && prevSlow.HasValue)
				{
					var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
					var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;

					if (crossUp && Position <= 0)
						BuyMarket();
					else if (crossDown && Position >= 0)
						SellMarket();
				}

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}
}