Ver en GitHub

AutoAdjustingStrategy

AutoAdjustingStrategy replicates the MetaTrader expert Aouto Adjusting1 using StockSharp's high-level API. The port keeps the original multi-timeframe momentum filter, monthly MACD trend confirmation, and three-layer EMA stack to detect with-trend pullbacks. Stops and targets are projected from recent swing extremes and automatically adjusted every completed candle.

Core logic

  1. Trend structure – three exponential moving averages on the trading timeframe (6, 14, 26) must be aligned (EMA6 < EMA14 < EMA26 for longs, inverted for shorts). The previous candle needs to tag the middle EMA, while the prior candle forms a higher low / lower high to confirm a pullback.
  2. Momentum confirmation – momentum on the higher timeframe (mapped from the trading timeframe, e.g., H1 → D1) must deviate at least MomentumBuyThreshold / MomentumSellThreshold from 100 on any of the last three completed bars.
  3. Macro filter – a monthly MACD(12, 26, 9) signal ensures trades align with the dominant trend (MACD > Signal for buys, < for sells).
  4. Execution – market orders are submitted once all filters agree and no opposite exposure is present. Opposite positions are flattened before entering the new direction.
  5. Protection – stop-loss levels are placed a configurable pad of pips beyond the lowest low / highest high of the last CandlesBack bars. Take-profit distances scale by RewardRatio. Both stop and target are re-armed on each candle close while the position is active.

Risk and position sizing

The strategy mirrors the original risk parameterization:

  • RiskPercent calculates an adaptive position size whenever portfolio value and price step metadata are available. The algorithm divides the allowed monetary loss by the loss per unit implied by the current stop distance.
  • When risk-based sizing cannot be evaluated (e.g., missing portfolio statistics), the engine falls back to the fixed TradeVolume parameter.

Parameters

Name Type Default Description
CandleType DataType TimeFrame(H1) Trading timeframe used for the EMA stack.
MomentumCandleType DataType Derived from CandleType Higher timeframe feeding the momentum indicator (H1→D1, H4→W1, etc.).
MacroMacdCandleType DataType TimeFrame(30 days) Timeframe for the macro MACD confirmation (monthly by default).
PadAmount decimal 3 Extra pips beyond swing extremes when computing stops.
RiskPercent decimal 0.1 Percent of portfolio equity risked per trade.
RewardRatio decimal 2 Multiplier applied to the stop distance to place the take-profit.
CandlesBack int 3 Number of candles inspected for swing high/low detection.
MomentumBuyThreshold decimal 0.3 Minimum momentum deviation required to enable long entries.
MomentumSellThreshold decimal 0.3 Minimum momentum deviation required to enable short entries.
TradeVolume decimal 1 Fallback lot size when risk-based sizing is unavailable.

Charting and visualization

  • Subscribe to the trading timeframe and plot the three EMAs to observe pullbacks.
  • Track the momentum series on its higher timeframe panel to confirm energy thresholds.
  • Monitor the MACD values from the macro timeframe to validate the trend filter.

Notes

  • The automatic timeframe mapping matches the MQL expert: M1→M15, M5→M30, M15→H1, M30→H4, H1→D1, H4→W1, D1→MN1. Other frames keep their original value.
  • The strategy avoids indicator GetValue calls by storing the most recent values inside the strategy and feeding them through the bind callbacks.
  • Trailing behavior mirrors the original EA by recalculating the protective levels every time a candle closes.
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 AutoAdjustingStrategy : 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 AutoAdjustingStrategy()
	{
		_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;
	}
}