GitHub で見る

Two MA Four Level Bands Strategy

Overview

This strategy recreates the MetaTrader expert advisor ytg_2MA_4Level. It compares a fast moving average with a slower one and triggers entries when the fast curve crosses the slow curve either directly or within four configurable offset bands. Positions are protected by symmetric stop-loss and take-profit distances expressed in pips, just like in the original implementation.

Signal logic

  1. Two moving averages are calculated on the selected candle series. Both the averaging method (SMA, EMA, SMMA, LWMA) and the applied price can be tuned independently for the fast and slow lines.
  2. On every finished candle the strategy samples the moving averages CalculationBar bars back (default 1) and also one bar earlier. This mirrors the MetaTrader iMA(..., shift) call and ensures that only closed candles generate trades.
  3. A buy signal fires when the fast average crosses above the slow one, or when the crossover happens above/below the slow average shifted by UpperLevel1, UpperLevel2, LowerLevel1, or LowerLevel2 pips.
  4. A sell signal uses the mirrored conditions with the fast average crossing below the slow line (and the same four offset bands).
  5. The strategy only opens a new market position when no orders are active and the current position is flat, matching the single-ticket behaviour of the MQL expert.

Parameters

Name Type Default Description
TakeProfitPips int 130 Take-profit distance in pips. Set to 0 to disable the target.
StopLossPips int 1000 Stop-loss distance in pips. Set to 0 to disable the protective stop.
TradeVolume decimal 1 Base lot size sent with each order (auto-adjusted to VolumeStep).
CalculationBar int 1 Number of bars used as the anchor for the MA comparison (MetaTrader shift).
FastPeriod / SlowPeriod int 14 / 180 Period lengths of the moving averages.
FastMethod / SlowMethod MovingAverageMethod Smoothed Averaging technique: Simple, Exponential, Smoothed, or LinearWeighted.
FastPrice / SlowPrice CandlePrice Median Applied price used by each moving average.
UpperLevel1 / UpperLevel2 int 500 / 250 Positive offsets (in pips) added to the slow MA for tolerance checks.
LowerLevel1 / LowerLevel2 int 500 / 250 Negative offsets (in pips) subtracted from the slow MA for tolerance checks.
CandleType DataType 15m time frame Candle series on which the indicators operate.

Implementation notes

  • Stop-loss and take-profit orders are emulated through StartProtection with distances converted from pips to price units using the instrument’s PriceStep. Five-digit FX quotes automatically receive the MetaTrader-style *10 multiplier.
  • Internal queues store only the data needed to reproduce the shift logic; no full candle history is accumulated.
  • Orders are issued with BuyMarket / SellMarket and inherit the normalized volume so that the UI reflects the active lot size.
  • Chart output draws the candle series together with both moving averages and executed trades for quick visual inspection.
  • All inline comments are in English to comply with the project guidelines.

Usage tips

  • Pick the same candle interval that you would use in MetaTrader; the default 15-minute series can be changed via CandleType.
  • Reduce the offset levels to make the signals more selective, or enlarge them to accept wider “near miss” crossovers.
  • Setting CalculationBar to 0 makes the strategy react to the latest closed candle (no lag), while higher values move the trigger further into the past for additional confirmation.
  • Disable the protective legs (StopLossPips = 0, TakeProfitPips = 0) if the exits should be managed manually or by another module.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Two MA with four level bands: trades when fast MA crosses slow MA
/// or its offset bands. Exits on opposite crossover.
/// </summary>
public class TwoMaFourLevelBandsStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _bandMultiplier;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public TwoMaFourLevelBandsStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastPeriod = Param(nameof(FastPeriod), 14)
			.SetDisplay("Fast MA", "Fast EMA period.", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 180)
			.SetDisplay("Slow MA", "Slow SMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR for band calculation.", "Indicators");

		_bandMultiplier = Param(nameof(BandMultiplier), 1.5m)
			.SetDisplay("Band Mult", "ATR multiplier for offset bands.", "Bands");
	}

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal BandMultiplier
	{
		get => _bandMultiplier.Value;
		set => _bandMultiplier.Value = value;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, atr, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var bandOffset = atrVal * BandMultiplier;

		// Check crossovers at multiple band levels
		var bullish = false;
		var bearish = false;

		// Main line cross
		if (_prevFast <= _prevSlow && fastVal > slowVal) bullish = true;
		if (_prevFast >= _prevSlow && fastVal < slowVal) bearish = true;

		// Upper band cross
		if (_prevFast <= _prevSlow + bandOffset && fastVal > slowVal + bandOffset) bullish = true;
		if (_prevFast >= _prevSlow + bandOffset && fastVal < slowVal + bandOffset) bearish = true;

		// Lower band cross
		if (_prevFast <= _prevSlow - bandOffset && fastVal > slowVal - bandOffset) bullish = true;
		if (_prevFast >= _prevSlow - bandOffset && fastVal < slowVal - bandOffset) bearish = true;

		// Upper band 2
		if (_prevFast <= _prevSlow + bandOffset * 2 && fastVal > slowVal + bandOffset * 2) bullish = true;
		if (_prevFast >= _prevSlow + bandOffset * 2 && fastVal < slowVal + bandOffset * 2) bearish = true;

		// Lower band 2
		if (_prevFast <= _prevSlow - bandOffset * 2 && fastVal > slowVal - bandOffset * 2) bullish = true;
		if (_prevFast >= _prevSlow - bandOffset * 2 && fastVal < slowVal - bandOffset * 2) bearish = true;

		// Exit
		if (Position > 0 && bearish)
		{
			SellMarket();
			_entryPrice = 0;
		}
		else if (Position < 0 && bullish)
		{
			BuyMarket();
			_entryPrice = 0;
		}

		// Entry
		if (Position == 0)
		{
			if (bullish)
			{
				_entryPrice = candle.ClosePrice;
				BuyMarket();
			}
			else if (bearish)
			{
				_entryPrice = candle.ClosePrice;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}