The Avalanche strategy is a grid-style mean reversion system inspired by the original MetaTrader Avalanche v1.2 expert advisor. The idea is to monitor the relationship between price and a higher-timeframe equilibrium reference price (ERP) computed as a simple moving average. When price trades below the ERP the strategy expects a rebound toward the average and accumulates long positions. When price trades above the ERP the strategy looks for a decline and accumulates short positions. Each additional position is spaced by configurable distance thresholds, while every entry receives individual stop-loss and take-profit levels.
This StockSharp port focuses on the "toward" leg of the original algorithm. Away-from-ERP hedging orders from the MQL version are not replicated because StockSharp strategies operate on a single net position, but the grid stacking, buffering, and profit-taking logic remain faithful to the original approach.
How it works
Subscribe to two candle series: the trading timeframe and an ERP timeframe that feeds the moving average.
Calculate an ERP simple moving average and determine whether price is positioned above or below it. A configurable buffer prevents frequent flips.
When a new ERP bias appears, close any open grid and wait for fresh signals.
Open an initial position in the direction that should bring price back toward the ERP (long below, short above) if the OpenStartingOrders flag is enabled.
Keep adding positions in the same direction when price advances by the IntervalToward distance (momentum stacking).
Add additional protective entries when price moves against the grid by IntervalToward + StackBufferToward (martingale stacking).
Each entry has its own stop-loss and take-profit target measured in points, ensuring that profitable legs can be closed individually while the grid continues to manage the remaining exposure.
Parameters
Name
Description
BaseVolume
Base order volume used before applying multipliers.
TowardMultiplier
Lot multiplier for standard toward-ERP entries.
TowardInterestMultiplier
Multiplier used when the instrument pays positive swap in the trading direction.
IntervalToward
Distance in points required to add a trend-following stack.
StackBufferToward
Additional buffer added to the interval when stacking against adverse price moves.
TakeProfitToward
Take-profit distance in points for each entry. Set to 0 to disable.
StopLossToward
Stop-loss distance in points for each entry. Set to 0 to disable.
ErpPeriod
Number of periods for the ERP simple moving average.
ErpChangeBuffer
Buffer (in points) applied around the ERP before switching bias.
CandleType
Trading timeframe used to trigger entries and exits.
ErpCandleType
Timeframe used to calculate the ERP moving average.
OpenStartingOrders
If enabled, immediately opens the first grid order when conditions are satisfied.
Differences vs. the original EA
Only the toward-ERP leg is implemented because the StockSharp strategy maintains a single net position. Hedging away orders are omitted.
Order execution relies on market orders instead of the pending stop orders used by the MQL version.
Swap direction detection is preserved to choose between the standard and interest multipliers.
Usage tips
Adjust IntervalToward and StackBufferToward to control how aggressively the grid adds new trades.
Ensure the selected instrument and timeframes provide enough liquidity; grid systems can accumulate sizeable exposure.
Combine the strategy with external risk controls (equity stops, session filters) when running in production.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Avalanche grid strategy - mean reversion around EMA.
/// Buys when price drops below EMA, sells when above.
/// Uses RSI to confirm oversold/overbought conditions.
/// </summary>
public class AvalancheStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<DataType> _candleType;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal RsiOversold { get => _rsiOversold.Value; set => _rsiOversold.Value = value; }
public decimal RsiOverbought { get => _rsiOverbought.Value; set => _rsiOverbought.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public AvalancheStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "EMA period for equilibrium", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI period", "Indicators");
_rsiOversold = Param(nameof(RsiOversold), 35m)
.SetDisplay("RSI Oversold", "RSI oversold level", "Indicators");
_rsiOverbought = Param(nameof(RsiOverbought), 65m)
.SetDisplay("RSI Overbought", "RSI overbought level", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, rsi, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
// Below EMA and oversold - buy
if (close < emaValue && rsiValue <= RsiOversold && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Above EMA and overbought - sell
else if (close > emaValue && rsiValue >= RsiOverbought && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
}
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 ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class avalanche_strategy(Strategy):
def __init__(self):
super(avalanche_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA period for equilibrium", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._rsi_oversold = self.Param("RsiOversold", 35.0) \
.SetDisplay("RSI Oversold", "RSI oversold level", "Indicators")
self._rsi_overbought = self.Param("RsiOverbought", 65.0) \
.SetDisplay("RSI Overbought", "RSI overbought level", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def ema_period(self):
return self._ema_period.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def rsi_oversold(self):
return self._rsi_oversold.Value
@property
def rsi_overbought(self):
return self._rsi_overbought.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(avalanche_strategy, self).OnReseted()
def OnStarted2(self, time):
super(avalanche_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, rsi, self.process_candle).Start()
def process_candle(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
rsi_val = float(rsi_value)
if close < ema_val and rsi_val <= self.rsi_oversold and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close > ema_val and rsi_val >= self.rsi_overbought and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return avalanche_strategy()