Ver en GitHub

Estrategia Ang Zad C Time MM Recovery

Resumen

La Estrategia Ang Zad C Time MM Recovery es un port en C# del asesor experto de MetaTrader 5 Exp_Ang_Zad_C_Tm_MMRec. La estrategia combina el indicador de canal personalizado Ang_Zad_C con un filtro de sesión de trading configurable y un modelo de tamaño de posición adaptativo que reduce el riesgo después de un número configurable de trades perdedores.

Lógica del indicador

El indicador Ang_Zad_C construye dos envolventes adaptativas alrededor del precio. Cada envolvente se actualiza comparando el precio aplicado elegido de la vela actual y la anterior, moviéndose hacia el nuevo precio con el factor de suavizado Ki. Las líneas superior e inferior se evalúan en barras históricas definidas por Signal Bar para evitar actuar en velas no terminadas.

Reglas de trading

  • Entrada larga – Cuando la línea superior estaba por encima de la línea inferior en la barra de referencia anterior y cruza por debajo o toca la línea inferior en la barra de referencia más reciente. Cuando esto ocurre, cualquier posición corta abierta se cierra antes de abrir una nueva posición larga (si está habilitado).
  • Entrada corta – Cuando la línea superior estaba por debajo de la línea inferior en la barra de referencia anterior y cruza por encima o toca la línea inferior en la barra de referencia más reciente. Cualquier posición larga abierta se cierra antes de abrir una nueva posición corta (si está habilitado).
  • Salida larga – Cuando la línea superior está por debajo de la línea inferior en la barra de referencia anterior. La salida puede deshabilitarse mediante Enable Long Exit.
  • Salida corta – Cuando la línea superior está por encima de la línea inferior en la barra de referencia anterior. La salida puede deshabilitarse mediante Enable Short Exit.

Gestión monetaria y protecciones

  • El trading solo se permite dentro de la ventana de tiempo configurada cuando Use Time Filter está habilitado. Las posiciones abiertas anteriormente se cierran una vez que finaliza la sesión.
  • El volumen del trade se selecciona entre Normal Volume y Small Volume dependiendo de cuántos trades perdedores ocurrieron para cada lado. Después de Buy Loss Trigger trades largos perdedores (o Sell Loss Trigger trades cortos perdedores) se usa el volumen reducido hasta que un trade rentable resetea el contador.
  • Los niveles opcionales de stop-loss y take-profit se registran usando distancias en pasos de precio definidas por Stop Loss Steps y Take Profit Steps.

Parámetros

Nombre Descripción
Candle Type Marco temporal de las velas usadas por el indicador y las señales.
Ki Coeficiente de suavizado de las envolventes Ang_Zad_C.
Applied Price Qué precio de la vela se alimenta al indicador.
Signal Bar Cuántas barras atrás se usan para la evaluación de señales (1 = barra cerrada anterior).
Use Time Filter / Trade Start / Trade End Habilitar trading basado en sesión y establecer la hora de inicio y fin de la sesión.
Enable Long/Short Entry Permitir la apertura de nuevos trades largos o cortos.
Enable Long/Short Exit Permitir que la estrategia cierre posiciones en la reversión del indicador.
Buy/Sell Loss Trigger Número de trades perdedores antes de aplicar el volumen reducido.
Small Volume / Normal Volume Tamaños de orden usados para riesgo reducido y normal.
Stop Loss Steps / Take Profit Steps Distancia para órdenes protectoras expresada en pasos de precio.

Notas de conversión

  • La lógica sigue el código MQL5 original, incluyendo las verificaciones de cruce direccional y el comportamiento de la ventana de tiempo.
  • La gestión monetaria adaptativa se implementa rastreando el beneficio y pérdida realizados por dirección y cambiando al volumen reducido después del número configurado de pérdidas.
  • Los cálculos del indicador evitan cualquier acceso directo al buffer y se procesan en velas terminadas usando la API de alto nivel de StockSharp.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Ang_Zad_C adaptive channel strategy.
/// Trades when the upper/lower lines cross, indicating trend reversal.
/// </summary>
public class AngZadCTimeMMRecoveryStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _ki;

	private bool _hasState;
	private decimal _upperLine;
	private decimal _lowerLine;
	private decimal _previousPrice;
	private decimal? _prevUp;
	private decimal? _prevDn;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public decimal Ki
	{
		get => _ki.Value;
		set => _ki.Value = value;
	}

	public AngZadCTimeMMRecoveryStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_ki = Param(nameof(Ki), 4.000001m)
			.SetDisplay("Ki", "Smoothing coefficient", "Indicator")
			.SetGreaterThanZero();
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_hasState = false;
		_upperLine = 0m;
		_lowerLine = 0m;
		_previousPrice = 0m;
		_prevUp = null;
		_prevDn = null;
	}

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

		_hasState = false;
		_prevUp = null;
		_prevDn = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var price = candle.ClosePrice;
		var (upper, lower) = UpdateIndicator(price);

		if (_prevUp == null || _prevDn == null)
		{
			_prevUp = upper;
			_prevDn = lower;
			return;
		}

		var prevUp = _prevUp.Value;
		var prevDn = _prevDn.Value;

		// Buy signal: previous upper was below lower, now crossing above
		if (prevUp <= prevDn && upper > lower)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Sell signal: previous upper was above lower, now crossing below
		else if (prevUp >= prevDn && upper < lower)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevUp = upper;
		_prevDn = lower;
	}

	private (decimal Up, decimal Down) UpdateIndicator(decimal price)
	{
		if (!_hasState)
		{
			_upperLine = price;
			_lowerLine = price;
			_previousPrice = price;
			_hasState = true;
			return (_upperLine, _lowerLine);
		}

		var ki = Ki;

		if (price > _upperLine && price > _previousPrice)
			_upperLine += (price - _upperLine) / ki;

		if (price < _upperLine && price < _previousPrice)
			_upperLine += (price - _upperLine) / ki;

		if (price > _lowerLine && price < _previousPrice)
			_lowerLine += (price - _lowerLine) / ki;

		if (price < _lowerLine && price > _previousPrice)
			_lowerLine += (price - _lowerLine) / ki;

		_previousPrice = price;

		return (_upperLine, _lowerLine);
	}
}