The Nextbar Strategy is a direct translation of the MetaTrader 4 expert advisor nextbar.mq4. The original EA evaluates the distance between the last completed candle and a candle that is several bars older. When price travels far enough in one direction it either follows the momentum or trades against it, depending on the configured direction flag. Positions are then protected with symmetric take-profit/stop-loss levels and are force-closed after a fixed number of bars.
This StockSharp version keeps the same behaviour while using the high-level strategy API. It processes completed candles only, ensuring that all calculations match the bar-on-close logic of the MT4 script.
Original MQL logic
Momentum distance – compare Close[1] with Close[bars2check+1]. If the difference is at least minbar * Point, treat it as a valid signal.
Direction flag – the MQL input direction equals 1 for trend-following (buy after a rally, sell after a drop) and 2 for contrarian trading (buy after a drop, sell after a rally).
Entry constraint – only one order can be open at a time. A new trade is sent at the start of the bar following the signal.
Exit rules – close a long if the last close hits the profit distance above the entry or the loss distance below it; the inverse applies for shorts. If neither level is reached, close the trade after bars2hold completed candles.
StockSharp implementation highlights
Uses SubscribeCandles() and Bind to receive completed candles on the configured timeframe.
Stores a short rolling history of close prices to reference the candle that matches the MQL bars2check + 1 offset.
Converts all point-based parameters with Security.PriceStep, mimicking the MetaTrader Point constant.
Places market orders with the strategy Volume and supports either momentum-following or contrarian entries via the Direction parameter.
Implements profit, loss, and holding-period exits exactly once per finished candle to stay aligned with the original workflow.
Parameters
Parameter
Description
Default
Notes
CandleType
Timeframe used for signal evaluation.
1-hour time frame
Attach the strategy to a security that can provide this candle type.
BarsToCheck
Number of completed candles between the reference close and the latest close.
8
Matches bars2check from the EA.
BarsToHold
Maximum number of completed candles to keep a position open.
10
Matches bars2hold. The position is closed on the bar where the counter reaches this number.
MinMovePoints
Minimum distance (in MetaTrader points) between the two compared closes.
77
Corresponds to minbar. Converted using Security.PriceStep.
TakeProfitPoints
Profit target distance in MetaTrader points.
115
Equivalent to the profit input. Set to zero to disable if desired.
StopLossPoints
Stop-loss distance in MetaTrader points.
115
Equivalent to the loss input. Set to zero to disable if desired.
Direction
Trading mode: Follow (trend) or Reverse (contrarian).
Follow
Mirrors the direction input (1 = follow, 2 = reverse).
Volume
Trade volume used for market orders.
Strategy volume
Configure through the standard Strategy.Volume property.
Trading workflow
Wait for a finished candle and cache its close price.
Fetch the close from BarsToCheck candles ago and compute the difference.
If the absolute move is below MinMovePoints * PriceStep, do nothing.
Otherwise:
In Follow mode, buy if price rose, sell if price fell.
In Reverse mode, buy if price fell, sell if price rose.
On every subsequent finished candle while the position is open:
Close longs when the close is TakeProfitPoints above or StopLossPoints below the stored entry price.
Close shorts when the close is TakeProfitPoints below or StopLossPoints above the entry.
Force-close once BarsToHold candles have elapsed since entry.
Usage notes
The conversion from points to absolute price requires Security.PriceStep. Provide the correct instrument metadata (price step, step price, volume rules) before running the strategy.
The strategy does not manage multiple simultaneous positions; ensure Volume corresponds to the size you expect for a single MT4 order.
Because decisions are evaluated on completed candles only, the strategy should be run with historical and real-time data that deliver finished bars.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class NextbarStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevOpen;
private decimal _prevClose;
private bool _hasPrev;
private int _cooldownRemaining;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public NextbarStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA filter", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevOpen = default;
_prevClose = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevOpen = 0;
_prevClose = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevOpen = candle.OpenPrice; _prevClose = close; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevOpen = candle.OpenPrice;
_prevClose = close;
return;
}
var prevBullish = _prevClose > _prevOpen;
var prevBearish = _prevClose < _prevOpen;
if (prevBullish && close > ema && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (prevBearish && close < ema && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_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 nextbar_strategy(Strategy):
def __init__(self):
super(nextbar_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA filter", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(nextbar_strategy, self).OnReseted()
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(nextbar_strategy, self).OnStarted2(time)
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
self._cooldown_remaining = 0
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
ema_val = float(ema)
if not self._has_prev:
self._prev_open = open_price
self._prev_close = close
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_open = open_price
self._prev_close = close
return
prev_bullish = self._prev_close > self._prev_open
prev_bearish = self._prev_close < self._prev_open
if prev_bullish and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif prev_bearish and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_open = open_price
self._prev_close = close
def CreateClone(self):
return nextbar_strategy()