Стратегия реализует в StockSharp систему сеточного трейдинга "VR---SETKAa3hM" из MetaTrader 4. Ордера на покупку или продажу открываются при отклонении цены от дневного диапазона на заданный процент. При включённом мартингейле объём каждой новой позиции увеличивается. Средняя цена входа по всем ордерам используется для выставления общего тейк-профита.
Параметры
Distance: расстояние между уровнями сетки в пунктах.
TakeProfit: тейк-профит для первого ордера в пунктах.
Correction: добавочный профит в пунктах, прибавляемый к средней цене при нескольких ордерах.
SignalPercent: процент отклонения от дневного диапазона для формирования сигнала.
UseMartingale: включение увеличения объёма по мартингейлу.
CandleType: таймфрейм свечей для расчётов.
Логика
По завершённой свече рассчитывается положение закрытия относительно дневного максимума и минимума.
Если предыдущая свеча бычья и цена ниже дневного максимума на указанный процент, открывается или добавляется покупка.
Если предыдущая свеча медвежья и цена выше дневного минимума на указанный процент, открывается или добавляется продажа.
Дополнительные ордера ставятся каждый раз, когда цена идёт против позиции на Distance пунктов.
Когда цена возвращается к средней цене плюс Correction для покупок или минус Correction для продаж, все ордера закрываются рыночным ордером.
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;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class vr_setka_grid_strategy(Strategy):
def __init__(self):
super(vr_setka_grid_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA period for trend", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Base candle series", "General")
self._prev_ema = 0.0
self._prev_close = 0.0
self._has_prev = False
@property
def ema_period(self):
return self._ema_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vr_setka_grid_strategy, self).OnReseted()
self._prev_ema = 0.0
self._prev_close = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(vr_setka_grid_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
self.SubscribeCandles(self.candle_type).Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ev = float(ema_value)
if not self._has_prev:
self._prev_ema = ev
self._prev_close = close
self._has_prev = True
return
cross_up = self._prev_close <= self._prev_ema and close > ev
cross_down = self._prev_close >= self._prev_ema and close < ev
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_ema = ev
self._prev_close = close
def CreateClone(self):
return vr_setka_grid_strategy()