Открыть на GitHub

Стратегия VR Smart Grid Lite

VR Smart Grid Lite переносит логику одноимённого советника MetaTrader в инфраструктуру StockSharp. Стратегия строит усредняющую сетку рыночных ордеров: стартовый объём задаётся пользователем, а при движении цены против позиции на заданное число пунктов объём каждой новой сделки удваивается. Для выхода предусмотрены два режима — полное закрытие крайних ордеров по взвешенной цене или частичное закрытие для постепенного разгрузки сетки.

Параметры

  • Take Profit (pips) – расстояние для фиксации прибыли, когда открыт только один ордер.
  • Start Volume – стартовый объём первой сделки в каждом направлении.
  • Maximal Volume – ограничение на максимальный объём одного ордера.
  • Close Mode – режим закрытия: Average закрывает самый старый и самый новый ордера по общей цели, PartClose уменьшает объём последнего ордера на стартовый объём и полностью закрывает первый ордер.
  • Order Step (pips) – минимальное движение против позиции, после которого разрешается ставить новый ордер.
  • Minimal Profit (pips) – дополнительная надбавка к взвешенной цене закрытия.
  • Slippage (pips) – параметр, сохранённый из оригинального советника для полноты настроек.
  • Candle Type – тип свечей, по которым синхронизируется работа стратегии; направление предыдущей закрытой свечи задаёт торговое смещение.

Алгоритм работы

  1. После получения каждой завершённой свечи оценивается направление предыдущей свечи.
  2. Если предыдущая свеча бычья и нет открытых покупок (или цена ушла ниже минимальной покупки на требуемый шаг), выставляется новый рыночный ордер на покупку.
  3. Если предыдущая свеча медвежья и нет открытых продаж (или цена поднялась выше максимальной продажи на требуемый шаг), выставляется новый рыночный ордер на продажу.
  4. Объём новой сделки равен стартовому объёму при первом входе и удваивается от объёма самого «дешёвого» ордера при последующих входах, с учётом максимального объёма и шага объёма инструмента.
  5. Когда остаётся один ордер, используется простое расстояние Take Profit для выхода.
  6. При наличии нескольких ордеров рассчитываются взвешенные уровни по крайним позициям:
    • В режиме Average при достижении цели закрываются оба крайних ордера, что фиксирует усреднённую прибыль.
    • В режиме PartClose частично закрывается объём последнего ордера (на величину стартового объёма) и полностью закрывается первый ордер, сохраняя сетку активной.
  7. Стратегия отслеживает все открытые и закрытые ордера, чтобы внутреннее состояние сетки соответствовало реальному портфелю.

Особенности и рекомендации

  • Стратегия использует рыночные ордера, поэтому фактическое проскальзывание зависит от ликвидности.
  • Убедитесь, что минимальный объём и шаг объёма инструмента совместимы со стартовым объёмом.
  • Сеточные и мартина́льные подходы подразумевают быстрое наращивание риска при сильных трендах. Настоятельно рекомендуется контролировать объём позиций и устанавливать ограничения по капиталу.
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: grid trading based on price levels with SMA filter.
/// </summary>
public class VrSmartGridLiteStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _gridPercent;
	private readonly StrategyParam<int> _smaPeriod;

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

	public decimal GridPercent
	{
		get => _gridPercent.Value;
		set => _gridPercent.Value = value;
	}

	public int SmaPeriod
	{
		get => _smaPeriod.Value;
		set => _smaPeriod.Value = value;
	}

	public VrSmartGridLiteStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_gridPercent = Param(nameof(GridPercent), 3.0m)
			.SetGreaterThanZero()
			.SetDisplay("Grid %", "Grid step percentage", "Grid");

		_smaPeriod = Param(nameof(SmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA period for trend", "Indicators");
	}

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

		var sma = new SimpleMovingAverage { Length = SmaPeriod };

		decimal? lastTradePrice = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, (candle, smaVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				if (!lastTradePrice.HasValue)
				{
					lastTradePrice = close;
					return;
				}

				var step = lastTradePrice.Value * GridPercent / 100m;

				if (close <= lastTradePrice.Value - step)
				{
					BuyMarket();
					lastTradePrice = close;
				}
				else if (close >= lastTradePrice.Value + step)
				{
					SellMarket();
					lastTradePrice = close;
				}
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, sma);
			DrawOwnTrades(area);
		}
	}
}