Ver en GitHub

Estrategia Zone Recovery Button

La estrategia Zone Recovery Button es una conversión directa del asesor experto de MetaTrader "ZONE RECOVERY BUTTON VER1" (MQL/25347). El robot original dependía de botones BUY/SELL en el gráfico para iniciar una cesta cubierta. En esta adaptación a StockSharp, el panel manual se sustituye por parámetros, mientras que se conservan la lógica de recuperación, los take-profits monetarios/porcentuales, el trailing stop en divisa y la protección equity-stop.

Cuando la estrategia recibe una dirección inicial, abre una orden de mercado inicial. Cada vez que el precio atraviesa el ancho de zona configurado, el sistema apila una operación opuesta con mayor volumen. La cesta se cierra cuando se alcanza el take-profit de referencia, la ganancia flotante llega al objetivo monetario/porcentual configurado, el trailing stop devuelve demasiada ganancia o se infringe el umbral de equity-stop.

Reglas de trading

  1. Dirección inicial - emula pulsar el botón BUY o SELL. La estrategia abre la primera orden inmediatamente cuando recibe datos y tiene permiso para operar. Después de cerrar la cesta, puede reiniciar automáticamente con la misma dirección.
  2. Recuperación por zonas - en cada paso de recuperación, el algoritmo alterna la dirección. En ciclos largos vende cuando el precio cae por debajo de Base Price - Zone Width, y luego compra de nuevo cuando el mercado vuelve por encima de la base. En ciclos cortos, la lógica se refleja.
  3. Escalado de volumen - cada cobertura adicional multiplica el volumen anterior o añade un incremento fijo, reproduciendo la configuración "Lots"/"Multiply" del EA.
  4. Controles de take-profit - la cesta se cierra por:
    • take-profit basado en pips medido desde el precio de referencia;
    • objetivo monetario en la divisa de la cuenta;
    • objetivo porcentual calculado desde el valor actual de la cartera;
    • lógica trailing que bloquea ganancias cuando la ganancia flotante supera un umbral y después devuelve más que el drawdown permitido;
    • equity-stop de emergencia que compara la pérdida flotante actual con el equity más alto observado durante el ciclo.

Parámetros

Parámetro Predeterminado Descripción
CandleType TimeSpan.FromMinutes(5) Tipo de vela usado para monitorizar movimientos de precio.
StartDirection Buy Dirección inicial del ciclo (BUY/SELL/NONE).
AutoRestart true Reinicia automáticamente un nuevo ciclo después de cerrar la cesta anterior.
TakeProfitPips 200 Distancia en pips entre el precio base y el objetivo de take-profit en pips.
ZoneRecoveryPips 10 Distancia en pips que dispara la siguiente cobertura en la dirección opuesta.
InitialVolume 0.01 Volumen (lotes) de la primera operación.
UseVolumeMultiplier true Si está habilitado, cada cobertura multiplica el volumen anterior; de lo contrario se añade VolumeIncrement.
VolumeMultiplier 2 Multiplicador aplicado cuando UseVolumeMultiplier es true.
VolumeIncrement 0.01 Incremento de volumen cuando UseVolumeMultiplier es false.
MaxTrades 100 Número máximo de operaciones en la cesta.
UseMoneyTakeProfit false Habilita el cierre cuando la ganancia flotante supera MoneyTakeProfit.
MoneyTakeProfit 40 Objetivo de ganancia en la divisa de la cuenta.
UsePercentTakeProfit false Habilita el cierre cuando la ganancia flotante supera PercentTakeProfit por ciento del balance.
PercentTakeProfit 10 Objetivo de ganancia como porcentaje del valor actual de la cartera.
EnableTrailing true Habilita el trailing de ganancia en divisa.
TrailingProfitThreshold 40 Nivel de ganancia que activa trailing.
TrailingDrawdown 10 Drawdown permitido desde el pico de ganancia flotante antes de cerrar la cesta.
UseEquityStop true Habilita el equity stop de emergencia.
TotalEquityRiskPercent 1 Pérdida flotante máxima (en porcentaje del máximo de equity) antes de aplanar.

Notas

  • La estrategia funciona con cualquier instrumento que proporcione valores PriceStep y StepPrice. Estos parámetros son necesarios para convertir distancias en pips a unidades de precio y divisa.
  • Como StockSharp usa un modelo de posición neta, la cuadrícula de cobertura se simula internamente. La estrategia mantiene su propia lista de pasos de operación para reproducir el cálculo de ganancias de MetaTrader.
  • La lógica trailing opera sobre la ganancia flotante de la cesta activa. No usa trailing stops basados en órdenes.
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 ZoneRecoveryButtonStrategy : 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 ZoneRecoveryButtonStrategy()
	{
		_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;
	}
}