Esta estrategia replica la lógica del asesor experto original Aeron JJN. Observa una vela de reversión fuerte y coloca una orden stop en la apertura de la última vela opuesta. El stop y el objetivo se establecen a un ATR de distancia, y un trailing stop opcional protege las posiciones abiertas.
Las pruebas muestran que la idea funciona mejor en los principales pares Forex usando velas de 1 minuto.
Se coloca una orden buy stop cuando la vela anterior es bajista con cuerpo mayor que DojiDiff1 y la vela actual es alcista pero aún por debajo de la última apertura bajista significativa. Una orden sell stop usa las condiciones espejo. Las órdenes pendientes se eliminan tras ResetTime minutos si permanecen sin ejecutar.
Detalles
Criterios de entrada:
Largo: Vela anterior bajista, vela actual alcista y cierra por debajo de la última apertura bajista.
Corto: Vela anterior alcista, vela actual bajista y cierra por encima de la última apertura alcista.
Largo/Corto: Ambos.
Criterios de salida:
Stop-loss y take-profit basados en ATR.
Trailing stop opcional en pips.
Stops: Sí, stop inicial y objetivo basados en ATR más trailing opcional.
Filtros:
Las órdenes pendientes expiran tras el tiempo configurado.
Parámetros
AtrPeriod – período de cálculo del ATR.
DojiDiff1 – umbral de tamaño de cuerpo para la vela anterior.
DojiDiff2 – umbral de tamaño de cuerpo al buscar la última vela opuesta.
TrailSl – activar trailing stop.
TrailPips – distancia de trailing en pips.
ResetTime – minutos antes de cancelar órdenes stop.
CandleType – marco temporal de trabajo.
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()