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
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.
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).
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.
The trading window is controlled by StartHour and EndHour (terminal time). After the end hour all still-active entry orders are cancelled.
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.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class ma_envelopes_strategy(Strategy):
def __init__(self):
super(ma_envelopes_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 15) \
.SetDisplay("Channel Period", "Channel lookback", "Indicators")
self._prev_high = None
self._prev_low = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(ma_envelopes_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
def OnStarted2(self, time):
super(ma_envelopes_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
highest = Highest()
highest.Length = self.Period
lowest = Lowest()
lowest.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
if self._prev_high is None or self._prev_low is None:
self._prev_high = hv
self._prev_low = lv
return
close = float(candle.ClosePrice)
if close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = hv
self._prev_low = lv
def CreateClone(self):
return ma_envelopes_strategy()