VR Smart Grid Lite — это стратегия усреднения по сетке, переведённая с оригинального советника MetaTrader 5. Алгоритм открывает рыночные ордера по направлению последней завершённой свечи и наращивает мартингейл-лестницу при движении цены против позиции. Все расстояния, объёмы и логика выхода настраиваются и соответствуют поведению MQL-версии.
Торговая логика
После закрытия каждой свечи анализируется её направление.
Если свеча бычья, можно открыть новый buy-ордер, когда текущая цена минимум на Order Step (pips) ниже самой дешёвой покупки.
Если свеча медвежья, можно открыть новый sell-ордер, когда текущая цена минимум на Order Step (pips) выше самой дорогой продажи.
Первый ордер в каждом направлении использует Start Volume. Каждый следующий ордер удваивает объём самой дальней позиции, но ограничивается параметром Max Volume.
При наличии только одной позиции срабатывает фиксация по достижении расстояния Take Profit (pips).
При двух и более позициях используется выбранный режим закрытия Close Mode:
Average — закрывает самую высокую и самую низкую сделки, когда цена достигает их взвешенного среднего плюс Minimal Profit (pips).
PartialClose — полностью закрывает самую низкую сделку и уменьшает самую высокую на Start Volume, когда цена достигает целевого уровня.
Управление рисками
Объёмы автоматически подгоняются под MinVolume, MaxVolume и StepVolume инструмента, чтобы избежать отклонённых сделок.
Вызов StartProtection() активирует защиту счёта StockSharp перед началом торговли.
Параметры
Параметр
Описание
Take Profit (pips)
Дистанция тейк-профита для одиночной позиции.
Start Volume
Начальный объём для первого ордера в каждом направлении.
Max Volume
Максимальный объём ордера (0 отключает ограничение).
Close Mode
Выбор между усреднённым выходом и частичным закрытием.
Order Step (pips)
Минимальный шаг против позиции для открытия нового ордера.
Minimal Profit (pips)
Запас прибыли, добавляемый к целевой цене усреднения.
Candle Type
Тип свечей, используемых в расчётах.
Примечания
Стратегия работает только с рыночными ордерами; условные заявки оригинального советника моделируются проверкой условий на каждой свече.
Хранится отдельное состояние сделок, что позволяет повторить выборочное закрытие и частичное снятие объёма, как в MetaTrader.
Для идентичного поведения задайте тип свечей и тик-стоимость, соответствующие таймфрейму исходного MQL-скрипта.
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()