Ver no GitHub

Standard Deviation Channel Strategy

Overview

This strategy is a StockSharp port of the MetaTrader expert Standard Deviation Channel. It plots a linear weighted moving average (LWMA) based volatility channel and trades breakouts that align with the prevailing trend. Entries are filtered by momentum strength and a MACD confirmation, while exits combine fixed targets, break-even jumps, and trailing protection.

Indicators and Signals

  • Standard deviation channel built from a LWMA baseline and a configurable deviation multiplier. Long setups require the upper band to slope upward; short setups require the lower band to slope downward.
  • Trend filter: fast and slow LWMA calculated on the same candles. Longs demand LWMA_fast > LWMA_slow; shorts require the opposite.
  • Momentum filter: a 14-period Momentum indicator. At least one of the last three readings must deviate from the neutral 100 level by the configured threshold.
  • MACD filter: classic 12/26/9 configuration. Long entries need MACD ≥ signal, while short entries require MACD ≤ signal.

Trade Management

  • Position sizing: uses the TradeVolume parameter. Reversals automatically close the opposite exposure before opening the new side.
  • Take-profit & stop-loss: expressed in pips and evaluated against the instrument PriceStep. The strategy issues market exits once the candle range touches the target or stop price.
  • Break-even jump: once unrealized profit reaches BreakEvenTriggerPips, the stop is moved to entry plus BreakEvenOffsetPips (or minus for shorts).
  • Trailing stop: after reaching TrailingStartPips, the stop follows price by TrailingStepPips, locking in gains on both sides.
  • Channel rejection exit: if price closes back inside the channel and the slope flattens against the position, the trade is closed early.

Parameters

Name Description
CandleType Primary timeframe used for all calculations.
TradeVolume Base order size.
TrendLength LWMA lookback that defines the channel baseline.
DeviationMultiplier Standard deviation multiplier for channel width.
FastMaLength / SlowMaLength LWMA lengths for the trend filter.
MomentumPeriod Lookback for the momentum filter.
MomentumThreshold Minimum deviation from 100 required in any of the last three momentum values.
TakeProfitPips / StopLossPips Distance of the fixed exit levels (converted using PriceStep).
BreakEvenTriggerPips / BreakEvenOffsetPips Controls when and how the break-even stop is activated.
TrailingStartPips / TrailingStepPips Enables and sizes the trailing stop.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod MACD configuration.
MaxPositionUnits Maximum absolute net position; prevents over-leveraging.

Usage Notes

  1. Attach the strategy to a security that exposes a valid PriceStep. Pips are converted by multiplying this step value.
  2. Use TrendLength and DeviationMultiplier to adapt the channel to different markets.
  3. Momentum and MACD filters can be relaxed (lower threshold, shorter periods) to increase trade frequency.
  4. The trailing logic works on candle closes; intrabar spikes that do not finish beyond the thresholds are ignored.

Differences from the Original Expert Advisor

  • The MetaTrader version relies on graphical objects to read the channel slope and uses several money-management branches (martingale sizing, equity protection). This port keeps the slope check but simplifies risk control to fixed-size trades capped by MaxPositionUnits.
  • All exits are handled with market orders at candle completion because StockSharp strategies do not directly mirror MT4 order modification APIs.
  • Email and push notifications are replaced by AddInfoLog messages to keep the conversion self-contained.
  • Equity-based account stop-outs were omitted; instead, focus is placed on per-position protection features.

Disclaimer

This sample is intended for educational use. Always forward-test and validate the configuration before deploying it on a live account.

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 StandardDeviationChannelStrategy : 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 StandardDeviationChannelStrategy()
	{
		_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;
	}
}