Ver en GitHub

VR Setka Grid Strategy

This strategy is a StockSharp implementation of the MetaTrader "VR---SETKAa3hM" grid system. It opens a sequence of buy or sell orders based on percentage deviation from the daily range and optionally increases volume using a martingale multiplier. The average entry price of all open orders is tracked to place a unified take-profit target.

Parameters

  • Distance: Price distance in points between grid levels.
  • TakeProfit: Profit target in points for the initial order.
  • Correction: Extra profit in points added to the average price when more than one order is open.
  • SignalPercent: Percentage threshold used to detect deviation from the daily range.
  • UseMartingale: Multiply volume by the number of open orders.
  • CandleType: Candle timeframe used for signal calculations.

Logic

  1. When a finished candle appears, compute the current close in relation to the day high and low.
  2. If the previous candle was bullish and the close is sufficiently below the day high, start or continue a buy grid.
  3. If the previous candle was bearish and the close is sufficiently above the day low, start or continue a sell grid.
  4. Additional orders are placed whenever price moves against the position by Distance points.
  5. Once price returns to the average entry price plus Correction for buys or minus Correction for sells, all positions are closed with a market order.
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>
/// EMA trend + candle direction strategy (converted from grid).
/// </summary>
public class VrSetkaGridStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevEma;
	private decimal _prevClose;
	private bool _hasPrev;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public VrSetkaGridStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period for trend", "Indicators");

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

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevEma = 0;
		_prevClose = 0;
		_hasPrev = false;
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

		SubscribeCandles(CandleType)
			.Bind(ema, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevEma = emaValue;
			_prevClose = close;
			_hasPrev = true;
			return;
		}

		var crossUp = _prevClose <= _prevEma && close > emaValue;
		var crossDown = _prevClose >= _prevEma && close < emaValue;

		if (crossUp && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}

		_prevEma = emaValue;
		_prevClose = close;
	}
}