Esta estratégia replica a lógica do consultor especialista original Aeron JJN. Ela observa uma vela de reversão forte e coloca uma ordem stop na abertura da última vela oposta. O stop e o alvo são definidos a um ATR de distância, e um trailing stop opcional protege as posições abertas.
Os testes mostram que a ideia funciona melhor em pares Forex principais usando velas de 1 minuto.
Uma ordem buy stop é colocada quando a vela anterior é baixista com corpo maior que DojiDiff1 e a vela atual é altista, mas ainda abaixo da última abertura baixista significativa. Uma ordem sell stop usa as condições espelho. Ordens pendentes são removidas após ResetTime minutos se permanecerem não executadas.
Detalhes
Critérios de entrada:
Comprado: Vela anterior baixista, vela atual altista e fecha abaixo da última abertura baixista.
Vendido: Vela anterior altista, vela atual baixista e fecha acima da última abertura altista.
Comprado/Vendido: Ambos.
Critérios de saída:
Stop-loss e take-profit baseados em ATR.
Trailing stop opcional em pips.
Stops: Sim, stop inicial e alvo baseados em ATR mais trailing opcional.
Filtros:
Ordens pendentes expiram após o tempo configurado.
Parâmetros
AtrPeriod – período de cálculo do ATR.
DojiDiff1 – limiar de tamanho do corpo para a vela anterior.
DojiDiff2 – limiar de tamanho do corpo ao buscar a última vela oposta.
TrailSl – ativar trailing stop.
TrailPips – distância de trailing em pips.
ResetTime – minutos antes de cancelar ordens stop.
CandleType – período de trabalho.
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()