The JBrainTrend1Stop Strategy is a StockSharp port of the MetaTrader 5 expert advisor Exp_JBrainTrend1Stop. It combines two Average True Range measurements, a Stochastic oscillator and Jurik moving averages to detect BrainTrading trend reversals. Whenever the Jurik-smoothed price makes a sufficiently large swing and the Stochastic leaves its neutral zone, the strategy switches bias, updates the BrainTrend stop line and (optionally) reverses the net position after a configurable delay.
Trading logic
Subscribe to candles defined by CandleType and feed them into:
A primary AverageTrueRange with length AtrPeriod.
An extended AverageTrueRange with period AtrPeriod + StopDPeriod.
A StochasticOscillator with StochasticPeriod and a single-bar %K smoothing (to match the MT5 settings).
Three JurikMovingAverage instances (high, low and close) configured with JmaLength and JmaPhase.
For each finished candle compute:
range = ATR / 2.3 (matching the original constant d = 2.3).
range1 = ATR_extended * 1.5 (matching s = 1.5).
val3 = |JMA_close - JMA_close[shift 2]| which reproduces the MT5 buffer difference.
When val3 > range and the Stochastic leaves its neutral band:
If %K < 47 the strategy enters the bearish BrainTrend state (_trendState = -1), seeds the sell stop at JMA_high + range1 / 4 and raises a sell signal.
If %K > 53 the strategy enters the bullish state (_trendState = 1), seeds the buy stop at JMA_low - range1 / 4 and raises a buy signal.
While the state remains unchanged the BrainTrend stop is trailed toward price by range1 (JMA_high + range1 for bearish trends, JMA_low - range1 for bullish trends).
Signals are released after SignalBar completed bars. Upon execution:
A buy signal closes short positions (if SellClose is enabled) and optionally opens a new long (if BuyOpen is enabled).
A sell signal closes long positions (if BuyClose is enabled) and optionally opens a new short (if SellOpen is enabled).
Charts automatically display the Jurik-smoothed close and the Stochastic oscillator alongside trade markers.
Parameters
Parameter
Description
Default
CandleType
Candle series processed by the strategy.
H4 (4-hour time frame)
AtrPeriod
Length of the primary ATR used for the BrainTrend trigger.
7
StochasticPeriod
Period for %K/%D of the Stochastic oscillator (single-bar %K smoothing).
9
StopDPeriod
Extra bars added to the secondary ATR period (AtrPeriod + StopDPeriod).
3
JmaLength
Jurik moving average length applied to high/low/close.
7
JmaPhase
Phase argument forwarded to the Jurik moving averages (clamped to [-100; 100]).
100
SignalBar
Number of completed bars to wait before firing a new signal.
1
BuyOpen / SellOpen
Allow entering long/short positions after a signal.
true
BuyClose / SellClose
Allow closing existing long/short positions on an opposite signal.
true
Use the strategy's Volume property or broker configuration to control order size.
Differences vs. the MT5 version
The original money-management block (MM, MMMode, Deviation_, dynamic lot sizing) is replaced by StockSharp's standard order sizing via Volume and market orders. Slippage control is not reproduced.
Absolute stop-loss and take-profit distances (StopLoss_, TakeProfit_) are not implemented. Protection can be configured manually through the hosting environment if required.
The BrainTrend stop levels are used internally for signal timing; they are not placed as pending orders.
The Jurik moving averages rely on StockSharp's JurikMovingAverage implementation. The phase parameter is applied through reflection, matching the behaviour of other BrainTrading ports in this repository.
Usage
Attach the strategy to a security and set CandleType (e.g., 4-hour candles for consistency with the EA).
Tune the indicator parameters (AtrPeriod, StochasticPeriod, StopDPeriod, JmaLength, JmaPhase) to align with the desired BrainTrend sensitivity.
Adjust SignalBar to delay signal execution by several completed bars if needed.
Configure Volume and the open/close toggles to reflect the preferred trading direction.
(Optional) Add external risk management such as stop-loss or portfolio limits via the hosting platform.
Once running, the strategy will track BrainTrend reversals, close opposing positions and optionally flip the direction after the configured delay.
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;
public class JBrainTrend1StopStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public JBrainTrend1StopStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 j_brain_trend1_stop_strategy(Strategy):
def __init__(self):
super(j_brain_trend1_stop_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(j_brain_trend1_stop_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(j_brain_trend1_stop_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return j_brain_trend1_stop_strategy()