View on GitHub

Three Level Grid Strategy

This strategy implements a symmetric grid trading system with up to three take profit ranks. Limit orders are placed above and below the current price at fixed intervals. When an entry order is filled, an opposite limit order is submitted to capture profit at a configurable distance. The method is suitable for range-bound markets where price oscillates inside a band.

Parameters

  • Grid Size – distance between grid levels.
  • Levels – number of grid levels on each side of the current price.
  • Base Take Profit – base profit distance for the first rank.
  • Order Volume – volume used for each grid order.
  • Enable Rank1 – place orders with base take profit.
  • Enable Rank2 – place orders with base plus one grid size take profit.
  • Enable Rank3 – place orders with base plus two grid sizes take profit.
  • Allow Longs – enable the long side of the grid.
  • Allow Shorts – enable the short side of the grid.
  • Candle Type – candle type used to obtain the initial reference price.

Trading Logic

  1. On start the strategy subscribes to candles and waits for the first completed candle.
  2. Using the close price of that candle the grid is built with the configured number of levels.
  3. For each level buy and/or sell limit orders are placed depending on the allowed sides.
  4. When an entry order is filled an opposite limit order is registered at the take profit price calculated from the selected rank.
  5. Orders remain in the market until executed or cancelled manually.

This implementation is a simplified conversion from the original MQL grid system and aims to highlight the core mechanics in StockSharp's high level API.

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;

/// <summary>
/// Three-level grid strategy using EMA as center line.
/// Buys on dips below EMA at different levels, sells on rises above EMA.
/// </summary>
public class ThreeLevelGridStrategy : Strategy
{
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<DataType> _candleType;

	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ThreeLevelGridStrategy()
	{
		_emaLength = Param(nameof(EmaLength), 30)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA period for center line", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new StandardDeviation { Length = 14 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, atr, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (atrVal <= 0)
			return;

		var close = candle.ClosePrice;
		var diff = close - emaVal;

		// Buy at different grid levels below EMA
		if (diff < -1.5m * atrVal && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Sell at different grid levels above EMA
		else if (diff > 1.5m * atrVal && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
		// Mean reversion exit
		else if (Position > 0 && close > emaVal + 0.5m * atrVal)
		{
			SellMarket();
		}
		else if (Position < 0 && close < emaVal - 0.5m * atrVal)
		{
			BuyMarket();
		}
	}
}