This strategy is a grid-based approach translated from the MetaTrader 4 expert VR---SETKAp2.
It trades when the daily close deviates from the day's high or low by a given percentage.
The strategy opens long positions after a significant drop from the daily high and
short positions after a significant rise from the daily low. Once in a position it
exits at a fixed take-profit distance. Volume can optionally increase using a simple
martingale scheme.
Parameters
TakeProfit – distance to the profit target in price steps.
Lot – base volume for every trade.
Percent – percentage threshold calculated from the daily range.
UseMartingale – enable volume increase when adding to a losing position.
Slippage – allowed price slippage for orders.
Correlation – offset applied when calculating grid levels.
Candle Type – timeframe used for calculations (daily by default).
Logic
Subscribe to daily candles.
For each finished candle, calculate percentage deviations from the daily high and low.
Enter long or short depending on the deviation and the previous candle direction.
Close the position when the take-profit level is reached.
This implementation demonstrates how a classic MetaTrader grid expert can be
ported to the StockSharp 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>
/// Grid based strategy using candle direction and EMA trend.
/// </summary>
public class VrSetkaP2Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevOpen;
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 VrSetkaP2Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevOpen = 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)
{
_prevOpen = candle.OpenPrice;
_prevClose = close;
_hasPrev = true;
return;
}
// Previous candle bullish + close above EMA => buy
if (_prevClose > _prevOpen && close > emaValue && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Previous candle bearish + close below EMA => sell
else if (_prevClose < _prevOpen && close < emaValue && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevOpen = candle.OpenPrice;
_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_p2_strategy(Strategy):
def __init__(self):
super(vr_setka_p2_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for analysis", "General")
self._prev_open = 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_p2_strategy, self).OnReseted()
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(vr_setka_p2_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if not self._has_prev:
self._prev_open = candle.OpenPrice
self._prev_close = close
self._has_prev = True
return
# Previous candle bullish + close above EMA => buy
if self._prev_close > self._prev_open and close > ema_value and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Previous candle bearish + close below EMA => sell
elif self._prev_close < self._prev_open and close < ema_value and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_open = candle.OpenPrice
self._prev_close = close
def CreateClone(self):
return vr_setka_p2_strategy()