Esta estrategia es un port de StockSharp del BullsBearsEyes EA para MetaTrader 5. Reconstruye el indicador personalizado combinando los osciladores clásicos Bulls Power y Bears Power con el mismo suavizado IIR de cuatro etapas usado en el código original. La relación resultante oscila entre 0 y 1 y refleja el dominio de vendedores o compradores. Cuando la relación cae a 0, el mercado se considera agotado por los osos y la estrategia prepara una entrada larga. Cuando la relación sube a 1, la presión alcista se considera agotada y la estrategia busca una entrada corta. Todos los cálculos se realizan únicamente en velas totalmente cerradas, replicando la implementación MQL que evaluaba custom[1] al nacer de cada nueva barra.
Lógica de trading
Suscribirse a la serie de velas configurada y vincular los indicadores Bulls Power y Bears Power.
En cada vela terminada, los valores del indicador se procesan a través de la misma cascada de suavizado IIR (L0 – L3) que el EA original.
Se computa la relación CU / (CU + CD). Una secuencia puramente bajista hace que CU sea igual a cero, mientras que una puramente alcista fuerza a CD a cero.
La estrategia almacena la relación de la vela anterior y la usa como señal accionable:
Relación anterior igual a 0 ⇒ cerrar posiciones cortas y abrir una posición larga.
Relación anterior igual a 1 ⇒ cerrar posiciones largas y abrir una posición corta.
Las relaciones intermedias se ignoran para mantenerse fiel al código fuente.
Las órdenes se envían con el valor Volume actual y automáticamente netean la posición opuesta antes de abrir una nueva.
Gestión de riesgo
Stop Loss / Take Profit – expresados en pips, traducidos a precios absolutos con detección de tamaño de pip idéntica a la implementación MT5 (los instrumentos de 5 y 3 dígitos se manejan mediante el multiplicador de paso).
Trailing Stop / Trailing Step – lógica idéntica: una vez que el precio avanza TrailingStop + TrailingStep, el stop se mueve para mantener una distancia TrailingStop constante desde el cierre actual. Las posiciones largas y cortas se gestionan simétricamente.
Los niveles protectores se recalculan cuando cambia la posición neta, asegurando que se use el precio promedio de posición para cálculos adicionales.
La estrategia cierra la posición completa cuando un nivel protector es superado dentro del rango de la vela actual.
Filtro de sesión
El filtro de tiempo opcional coincide con las entradas del asesor experto:
Use Time Control – habilita/deshabilita el filtro.
Start Hour – hora de inicio inclusiva (0–23).
End Hour – hora de finalización exclusiva (0–23). Si la hora de finalización es menor que la de inicio, la sesión se extiende sobre la medianoche.
Durante las horas restringidas, la estrategia se abstiene de abrir nuevas posiciones pero sigue gestionando stops y trailing para operaciones existentes.
Parámetros
Parámetro
Descripción
Predeterminado
Period
Longitud de promediado para Bulls/Bears Power.
13
Gamma
Factor de suavizado usado por el filtro de cuatro etapas (0–1).
0.6
StopLossPips
Distancia de stop-loss medida en pips.
150
TakeProfitPips
Distancia de take-profit medida en pips.
150
TrailingStopPips
Distancia de trailing stop en pips (0 deshabilita el trailing).
25
TrailingStepPips
Avance mínimo antes de que el trailing stop pueda moverse.
5
UseTimeControl
Habilita el filtro de sesión de trading.
true
StartHour
Primera hora de trading (inclusiva).
10
EndHour
Última hora de trading (exclusiva).
16
CandleType
Tipo de vela/marco temporal usado para análisis.
Velas de 1 hora
Notas adicionales
La API de alto nivel SubscribeCandles().Bind(...) se usa para replicar los cálculos originales sin recolectar velas manualmente.
Los valores del indicador se procesan solo después de que la vela cierra (CandleStates.Finished).
La detección del tamaño de pip recurre a 1 si el paso del instrumento no está disponible, permitiendo que la estrategia ejecute en entornos de prueba sintéticos.
Los comentarios en el archivo C# explican cada sección lógica para un mantenimiento más fácil y comparación con la fuente MQL.
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 BullsBearsEyesEaStrategy : 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 BullsBearsEyesEaStrategy()
{
_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 bulls_bears_eyes_ea_strategy(Strategy):
def __init__(self):
super(bulls_bears_eyes_ea_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(bulls_bears_eyes_ea_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(bulls_bears_eyes_ea_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 bulls_bears_eyes_ea_strategy()