Ver en GitHub

Ang Zad C Time MM Recovery Strategy

Overview

Ang Zad C Time MM Recovery Strategy is a C# port of the MetaTrader 5 expert advisor Exp_Ang_Zad_C_Tm_MMRec. The strategy combines the custom Ang_Zad_C channel indicator with a configurable trading session filter and an adaptive position size model that reduces risk after a configurable number of losing trades.

Indicator logic

The Ang_Zad_C indicator builds two adaptive envelopes around the price. Each envelope is updated by comparing the chosen applied price of the current and previous candle and moving toward the new price with the smoothing factor Ki. The upper and lower lines are evaluated on historical bars defined by Signal Bar to avoid acting on unfinished candles.

Trading rules

  • Long entry – When the upper line was above the lower line on the previous reference bar and crosses below or touches the lower line on the most recent reference bar. When this happens any open short position is closed before a new long is opened (if enabled).
  • Short entry – When the upper line was below the lower line on the previous reference bar and crosses above or touches the lower line on the most recent reference bar. Any open long position is closed before a new short is opened (if enabled).
  • Long exit – When the upper line is below the lower line on the previous reference bar. The exit can be disabled via Enable Long Exit.
  • Short exit – When the upper line is above the lower line on the previous reference bar. The exit can be disabled via Enable Short Exit.

Money management and protections

  • Trading is allowed only inside the configured time window when Use Time Filter is enabled. Positions opened earlier are closed once the session ends.
  • The trade volume is selected between Normal Volume and Small Volume depending on how many losing trades occurred for each side. After Buy Loss Trigger losing long trades (or Sell Loss Trigger losing short trades) the reduced volume is used until a profitable trade resets the counter.
  • Optional stop loss and take profit levels are registered using price step distances defined by Stop Loss Steps and Take Profit Steps.

Parameters

Name Description
Candle Type Timeframe of the candles used by the indicator and signals.
Ki Smoothing coefficient of the Ang_Zad_C envelopes.
Applied Price Which candle price is fed to the indicator.
Signal Bar How many bars back are used for signal evaluation (1 = previous closed bar).
Use Time Filter / Trade Start / Trade End Enable session based trading and set the start and end time of the session.
Enable Long/Short Entry Allow opening new long or short trades.
Enable Long/Short Exit Allow the strategy to close positions on indicator reversal.
Buy/Sell Loss Trigger Number of losing trades before the reduced volume is applied.
Small Volume / Normal Volume Order sizes used for reduced and normal risk.
Stop Loss Steps / Take Profit Steps Distance for protective orders expressed in price steps.

Conversion notes

  • The logic follows the original MQL5 code, including the directional cross checks and time window behaviour.
  • The adaptive money management is implemented by tracking realised profit and loss per direction and switching to the reduced volume after the configured number of losses.
  • Indicator computations avoid any direct buffer access and are processed on finished candles using the high-level StockSharp API.
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);
	}
}