Ver en GitHub

Estrategia de Hedging Martingale

Descripción general

Esta estrategia es un port StockSharp del asesor experto de MetaTrader "Hedging Martingale" (carpeta MQL/23693). Mantiene una cobertura equilibrada abriendo tanto una posición larga como una corta en cada nueva barra y luego aplica un esquema de promediado martingala. Cuando el precio se mueve adversamente por una distancia de pip configurable, la estrategia agrega una nueva posición en el lado perdedor con un volumen aumentado mientras mantiene la cobertura opuesta. La ganancia flotante se gestiona usando objetivos basados en dinero y porcentaje junto con un bloqueo de seguimiento opcional.

Lógica de trading

  • Cobertura inicial: siempre que la estrategia esté plana y una nueva vela cierre, simultáneamente compra y vende usando el mismo volumen base.
  • Pasos de martingala: si el precio se mueve en contra de un lado por Pip Step pips, se abre una orden adicional en ese lado. El volumen se multiplica por Volume Multiplier, emulando el dimensionamiento de lote progresivo de la versión MQL. El lado opuesto permanece abierto para mantener la cobertura.
  • Take-profit por operación: cada entrada abierta tiene una distancia de take-profit individual definida por Take Profit (pips). Cuando el mercado se mueve a favor de una pierna por esa distancia, la pierna se reduce emitiendo una orden compensatoria.
  • Salidas de canasta: el conjunto completo de posiciones puede cerrarse cuando la ganancia flotante alcanza un objetivo monetario, un porcentaje del capital inicial, o después de que un bloqueo de seguimiento da más que el retroceso permitido. Estos comportamientos replican Take_Profit_In_Money, Take_Profit_In_percent, y TRAIL_PROFIT_IN_MONEY2 del experto original.
  • Límites de operaciones: el parámetro Max Trades restringe cuántos pasos de martingala pueden estar activos. Si Close On Max está habilitado, la canasta se liquida una vez que se supera el límite.

Parámetros

Nombre Descripción
Candle Type Marco temporal que impulsa la lógica. Cada vela terminada puede desencadenar nuevas acciones de cobertura.
Use Money TP / Money Take Profit Habilitar y definir la ganancia flotante (en unidades de moneda) que cierra todas las posiciones.
Use Percent TP / Percent Take Profit Cerrar la canasta cuando la ganancia flotante alcanza un porcentaje del valor inicial del portafolio.
Enable Trailing / Trailing Start / Trailing Step Activar el bloqueo de seguimiento basado en dinero para la canasta y configurar el nivel de disparo junto con el retroceso de ganancia permitido.
Take Profit (pips) Distancia en pips para salidas de take-profit por pierna.
Pip Step Movimiento adverso de precio (en pips) requerido antes de agregar otra orden de martingala.
Base Volume Volumen inicial para las piernas de compra y venta.
Volume Multiplier Multiplicador aplicado al volumen de posición más grande al agregar entradas de martingala.
Max Trades Número máximo de entradas abiertas simultáneamente (en ambas direcciones).
Close On Max Si liquidar todas las posiciones una vez que se supera el conteo máximo de operaciones.

Notas

  • La estrategia usa BuyMarket y SellMarket para todas las colocaciones de órdenes, reflejando el modelo de ejecución de mercado del experto fuente.
  • Los valores de volumen se normalizan al paso de lote del instrumento para evitar órdenes rechazadas.
  • Cuando la estrategia queda plana, el bloqueo de seguimiento se reinicia para que las nuevas canastas comiencen con una referencia de ganancia limpia.

Archivos

  • CS/HedgingMartingaleStrategy.cs – implementación de la estrategia convertida (C#).
  • README.md – esta documentación (inglés).
  • README_zh.md – traducción al chino.
  • README_ru.md – traducción al ruso.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class HedgingMartingaleStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public HedgingMartingaleStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}