Ver no GitHub

MA Envelopes Strategy

Converted from the MetaTrader 5 expert "MA Envelopes". The strategy looks for price retracements towards a moving average that is wrapped by an envelope channel. When a completed candle closes between the moving average and one of the envelope bands during the configured trading window, the strategy places limit entries at the moving average with protective exit orders derived from the envelope.

Trading logic

  1. A moving average is calculated with the selected method, price source and period. The same value is used to build symmetric envelope bands using the deviation parameter.
  2. When a finished candle closes above the moving average but below the upper envelope band and the current ask price remains above the moving average, a staggered sequence of buy limit orders is prepared at the moving average price.
    • Each buy limit uses the lower envelope as the stop-loss level and the upper envelope plus an additional pip offset as the take-profit.
    • Up to three independent orders are managed, each with its own take-profit offset (First, Second, Third SL/TP parameters).
  3. When a finished candle closes below the moving average but above the lower envelope band and the current bid price remains below the moving average, the logic is mirrored for sell limit orders.
  4. The trading window is controlled by StartHour and EndHour (terminal time). After the end hour all still-active entry orders are cancelled.
  5. Risk per trade is estimated through MaximumRisk and reduced after consecutive losses using DecreaseFactor. Order volume is aligned to the instrument’s volume step and limits.
  6. Once an entry order is fully filled, protective stop-loss and take-profit orders are registered immediately. If an exit order is triggered, the counterpart order is cancelled and, if there is remaining position volume, new protective orders are issued for the rest.

Parameters

Parameter Description
MaximumRisk Fraction of available equity risked per position.
DecreaseFactor Reduces position size after consecutive losing trades.
First/Second/ThirdStopTakeProfitPips Pip distances added to the envelope bands for the three staged orders.
StartHour, EndHour Trading session boundaries in terminal time (0–23).
MaPeriod, MaShift, MaMethodType, AppliedPrice Moving-average configuration.
EnvelopeDeviation Width of the envelope channel in percent.
CandleType Timeframe of candles used for the calculations.

Notes

  • Protective orders are recreated whenever only part of a position is closed, keeping the remaining size covered.
  • Pending entry orders are cancelled at the end of the session; open positions remain managed by their protective orders.
  • The strategy relies on order book updates to capture the latest bid/ask prices; candle close values are used as a fallback when order book data is unavailable.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

public class MaEnvelopesStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;
	private decimal? _prevHigh, _prevLow;

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

	public MaEnvelopesStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_period = Param(nameof(Period), 15).SetGreaterThanZero().SetDisplay("Channel Period", "Channel lookback", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = null;
		_prevLow = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevHigh = null; _prevLow = null;
		var highest = new Highest { Length = Period };
		var lowest = new Lowest { Length = Period };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevHigh = high; _prevLow = low; return; }
		if (_prevHigh == null || _prevLow == null) { _prevHigh = high; _prevLow = low; return; }
		if (candle.ClosePrice > _prevHigh.Value && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (candle.ClosePrice < _prevLow.Value && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevHigh = high; _prevLow = low;
	}
}