The VR Smart Grid Lite Averaging strategy is a grid averaging system that follows the original MetaTrader 5 expert advisor. The algorithm opens market orders in the direction of the most recent bullish or bearish candle and builds a martingale-style ladder whenever price moves against the position. Distances, volumes and exit logic can be tuned to match the original MQL implementation.
Trading Logic
On every completed candle the strategy checks its direction.
A bullish candle allows a new buy order if the current price is at least Order Step (pips) below the lowest existing buy entry.
A bearish candle allows a new sell order if the current price is at least Order Step (pips) above the highest existing sell entry.
The first order for each side uses Start Volume. Every additional order doubles the volume of the farthest order on that side, while Max Volume limits the absolute size.
When only a single position exists on a side, the trade is closed once price reaches the Take Profit (pips) distance.
With two or more positions the closing logic depends on the selected Close Mode:
Average – closes the highest and lowest orders once price hits their weighted average plus Minimal Profit (pips).
PartialClose – closes the lowest order entirely and reduces the highest order by Start Volume when price reaches the blended target.
Risk Management
Volumes are adjusted to the broker’s MinVolume, MaxVolume and StepVolume to avoid rejection.
The built-in StartProtection() call ensures that StockSharp account protection is activated before trading.
Parameters
Name
Description
Take Profit (pips)
Target distance for single open positions.
Start Volume
Volume for the initial order in each direction.
Max Volume
Maximum allowed volume per order (0 disables the limit).
Close Mode
Choose between averaging exits or partial closes.
Order Step (pips)
Minimum adverse movement before adding a new order.
Minimal Profit (pips)
Extra profit buffer added to the averaging exit.
Candle Type
Candle series used for signal generation.
Notes
The strategy uses market orders only; pending orders from the original EA are emulated by evaluating conditions on each candle.
The implementation keeps per-order state to mimic MetaTrader’s ticket-based management, including partial closes and selective exits.
Configure the candle type and symbol pip size to match the timeframe used in the MQL script for consistent behaviour.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// VR Smart Grid Lite Averaging: grid with averaging approach using Bollinger Bands.
/// Buys near lower band, sells near upper band.
/// </summary>
public class VrSmartGridLiteAveragingStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bbPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BbPeriod
{
get => _bbPeriod.Value;
set => _bbPeriod.Value = value;
}
public VrSmartGridLiteAveragingStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_bbPeriod = Param(nameof(BbPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bb = new BollingerBands { Length = BbPeriod };
decimal? prevClose = null;
decimal? prevMid = null;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bb, (candle, bbVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bbv = (BollingerBandsValue)bbVal;
if (bbv.UpBand is not decimal upper || bbv.LowBand is not decimal lower)
return;
var close = candle.ClosePrice;
var mid = (upper + lower) / 2m;
if (prevClose.HasValue && prevMid.HasValue)
{
var crossBelow = prevClose.Value >= prevMid.Value && close < mid;
var crossAbove = prevClose.Value <= prevMid.Value && close > mid;
if (crossBelow && Position <= 0)
BuyMarket();
else if (crossAbove && Position >= 0)
SellMarket();
}
prevClose = close;
prevMid = mid;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bb);
DrawOwnTrades(area);
}
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class vr_smart_grid_lite_averaging_strategy(Strategy):
"""Bollinger Bands grid averaging: buy below midline, sell above midline."""
def __init__(self):
super(vr_smart_grid_lite_averaging_strategy, self).__init__()
self._bb_period = self.Param("BbPeriod", 20).SetGreaterThanZero().SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(vr_smart_grid_lite_averaging_strategy, self).OnReseted()
self._prev_close = None
self._prev_mid = None
def OnStarted2(self, time):
super(vr_smart_grid_lite_averaging_strategy, self).OnStarted2(time)
self._prev_close = None
self._prev_mid = None
bb = BollingerBands()
bb.Length = self._bb_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(bb, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def OnProcess(self, candle, bb_val):
if candle.State != CandleStates.Finished:
return
if not bb_val.IsFormed:
return
# Extract upper and lower bands
upper = None
lower = None
for key in bb_val.InnerValues:
name = str(key.Key.Name) if hasattr(key.Key, 'Name') else str(key.Key)
val = float(key.Value)
name_lower = name.lower()
if 'up' in name_lower:
upper = val
elif 'low' in name_lower or 'down' in name_lower:
lower = val
if upper is None or lower is None:
return
close = float(candle.ClosePrice)
mid = (upper + lower) / 2.0
if self._prev_close is not None and self._prev_mid is not None:
cross_below = self._prev_close >= self._prev_mid and close < mid
cross_above = self._prev_close <= self._prev_mid and close > mid
if cross_below and self.Position <= 0:
self.BuyMarket()
elif cross_above and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return vr_smart_grid_lite_averaging_strategy()