This strategy replicates the logic of the original Aeron JJN expert advisor. It watches for a strong reversal candle and places a stop order at the open of the last opposite candle. The stop and target are set one ATR away, and an optional trailing stop protects open positions.
Testing shows the idea works best on major Forex pairs using 1-minute candles.
A long stop order is placed when the previous candle is bearish with body larger than DojiDiff1 and the current candle is bullish but still below the last significant bearish open. A short stop order uses the mirror conditions. Pending orders are removed after ResetTime minutes if they remain unfilled.
Details
Entry Criteria:
Long: Previous candle bearish, current candle bullish and closes below last bearish open.
Short: Previous candle bullish, current candle bearish and closes above last bullish open.
Long/Short: Both.
Exit Criteria:
ATR-based stop-loss and take-profit.
Optional trailing stop in pips.
Stops: Yes, initial stop and target based on ATR plus optional trailing.
Filters:
Pending orders expire after the configured time.
Parameters
AtrPeriod – ATR calculation period.
DojiDiff1 – body size threshold for previous candle.
DojiDiff2 – body size threshold when searching last opposite candle.
TrailSl – enable trailing stop.
TrailPips – trailing distance in pips.
ResetTime – minutes before canceling stop orders.
CandleType – working timeframe.
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>
/// Aeron JJN breakout strategy.
/// Buys when candle reverses from bearish to bullish with body confirmation.
/// Sells when candle reverses from bullish to bearish.
/// </summary>
public class AeronJjnStrategy : 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 AeronJjnStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevOpen = 0;
_prevClose = 0;
_hasPrev = false;
}
/// <inheritdoc />
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;
if (_hasPrev)
{
var prevBull = _prevClose > _prevOpen;
var prevBear = _prevClose < _prevOpen;
var currBull = candle.ClosePrice > candle.OpenPrice;
var currBear = candle.ClosePrice < candle.OpenPrice;
// Buy: bearish to bullish reversal with price above EMA
if (prevBear && currBull && candle.ClosePrice > emaValue && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Sell: bullish to bearish reversal with price below EMA
else if (prevBull && currBear && candle.ClosePrice < emaValue && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
_hasPrev = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class aeron_jjn_strategy(Strategy):
def __init__(self):
super(aeron_jjn_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type", "General")
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(aeron_jjn_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_value)
if self._has_prev:
prev_bull = self._prev_close > self._prev_open
prev_bear = self._prev_close < self._prev_open
curr_bull = float(candle.ClosePrice) > float(candle.OpenPrice)
curr_bear = float(candle.ClosePrice) < float(candle.OpenPrice)
if prev_bear and curr_bull and float(candle.ClosePrice) > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_bull and curr_bear and float(candle.ClosePrice) < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_open = float(candle.OpenPrice)
self._prev_close = float(candle.ClosePrice)
self._has_prev = True
def OnReseted(self):
super(aeron_jjn_strategy, self).OnReseted()
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
def CreateClone(self):
return aeron_jjn_strategy()