Ver en GitHub

VR Smart Grid Lite Averaging Strategy

Overview

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);
		}
	}
}