La Estrategia de JBrainTrend1Stop es un port de StockSharp del asesor experto de MetaTrader 5 Exp_JBrainTrend1Stop. Combina dos medidas de Average True Range, un oscilador Stochastic y medias móviles Jurik para detectar reversiones de tendencia BrainTrading. Cuando el precio suavizado por Jurik realiza un swing suficientemente grande y el Stochastic sale de su zona neutral, la estrategia cambia el sesgo, actualiza la línea de stop BrainTrend y (opcionalmente) invierte la posición neta después de un retraso configurable.
Lógica de trading
Suscribirse a velas definidas por CandleType y alimentarlas a:
Un AverageTrueRange primario con longitud AtrPeriod.
Un AverageTrueRange extendido con período AtrPeriod + StopDPeriod.
Un StochasticOscillator con StochasticPeriod y suavizado de %K de una barra (para coincidir con la configuración MT5).
Tres instancias de JurikMovingAverage (máximo, mínimo y cierre) configuradas con JmaLength y JmaPhase.
Para cada vela terminada calcular:
range = ATR / 2.3 (coincidiendo con la constante original d = 2.3).
range1 = ATR_extended * 1.5 (coincidiendo con s = 1.5).
val3 = |JMA_close - JMA_close[shift 2]| que reproduce la diferencia del buffer MT5.
Cuando val3 > range y el Stochastic sale de su banda neutral:
Si %K < 47 la estrategia entra en estado BrainTrend bajista (_trendState = -1), inicializa el stop de venta en JMA_high + range1 / 4 y genera una señal de venta.
Si %K > 53 la estrategia entra en estado alcista (_trendState = 1), inicializa el stop de compra en JMA_low - range1 / 4 y genera una señal de compra.
Mientras el estado permanece sin cambios, el stop BrainTrend se arrastra hacia el precio por range1 (JMA_high + range1 para tendencias bajistas, JMA_low - range1 para tendencias alcistas).
Las señales se liberan después de SignalBar barras completadas. Al ejecutarse:
Una señal de compra cierra posiciones cortas (si SellClose está habilitado) y opcionalmente abre una nueva larga (si BuyOpen está habilitado).
Una señal de venta cierra posiciones largas (si BuyClose está habilitado) y opcionalmente abre una nueva corta (si SellOpen está habilitado).
Los gráficos muestran automáticamente el cierre suavizado por Jurik y el oscilador Stochastic junto con marcadores de operaciones.
Parámetros
Parámetro
Descripción
Predeterminado
CandleType
Serie de velas procesada por la estrategia.
H4 (marco temporal de 4 horas)
AtrPeriod
Longitud del ATR primario usado para el desencadenador BrainTrend.
7
StochasticPeriod
Período para %K/%D del oscilador Stochastic (suavizado de %K de una barra).
9
StopDPeriod
Barras adicionales añadidas al período ATR secundario (AtrPeriod + StopDPeriod).
3
JmaLength
Longitud de la media móvil Jurik aplicada a máximo/mínimo/cierre.
7
JmaPhase
Argumento de fase enviado a las medias móviles Jurik (limitado a [-100; 100]).
100
SignalBar
Número de barras completadas a esperar antes de disparar una nueva señal.
1
BuyOpen / SellOpen
Permitir entrar en posiciones largas/cortas después de una señal.
true
BuyClose / SellClose
Permitir cerrar posiciones largas/cortas existentes ante una señal opuesta.
true
Usar la propiedad Volume de la estrategia o la configuración del broker para controlar el tamaño de la orden.
Diferencias con la versión MT5
El bloque de gestión de dinero original (MM, MMMode, Deviation_, dimensionamiento dinámico de lotes) es reemplazado por el dimensionamiento estándar de órdenes de StockSharp via Volume y órdenes de mercado. El control de deslizamiento no está reproducido.
Las distancias absolutas de stop-loss y take-profit (StopLoss_, TakeProfit_) no están implementadas. La protección puede configurarse manualmente a través del entorno de hosting si se requiere.
Los niveles de stop BrainTrend se usan internamente para el tiempo de señal; no se colocan como órdenes pendientes.
Las medias móviles Jurik dependen de la implementación JurikMovingAverage de StockSharp. El parámetro de fase se aplica mediante reflexión, coincidiendo con el comportamiento de otros ports BrainTrading en este repositorio.
Uso
Adjuntar la estrategia a un instrumento y establecer CandleType (p. ej., velas de 4 horas para consistencia con el EA).
Ajustar los parámetros del indicador (AtrPeriod, StochasticPeriod, StopDPeriod, JmaLength, JmaPhase) para alinearse con la sensibilidad BrainTrend deseada.
Ajustar SignalBar para retrasar la ejecución de señales por varias barras completadas si es necesario.
Configurar Volume y los toggles de apertura/cierre para reflejar la dirección de trading preferida.
(Opcional) Agregar gestión de riesgo externa como stop-loss o límites de cartera via la plataforma de hosting.
Una vez ejecutándose, la estrategia rastreará las reversiones BrainTrend, cerrará las posiciones opuestas y opcionalmente cambiará la dirección después del retraso configurado.
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()