Esta estrategia es un port del Asesor Experto de MetaTrader 5 "Exp_BnB". Utiliza el indicador personalizado BnB (Bulls and Bears) que mide la presión alcista y bajista dentro de cada vela y las suaviza con una media móvil exponencial.
Cómo funciona
Para cada vela finalizada, la estrategia calcula los valores de bulls y bears.
Ambas series se suavizan con EMA.
Cuando la línea bulls cruza por encima de la línea bears:
Se cierra cualquier posición corta.
Se abre una posición larga.
Cuando la línea bears cruza por encima de la línea bulls:
Se cierra cualquier posición larga.
Se abre una posición corta.
Los niveles de stop loss y take profit se gestionan en puntos de precio absolutos.
Parámetros
Candle Type – marco temporal de las velas utilizadas para los cálculos.
EMA Length – período de suavizado para bulls y bears.
Stop Loss – distancia al stop de protección en puntos de precio.
Take Profit – distancia al objetivo de beneficio en puntos de precio.
Allow Long Entry – habilitar la apertura de posiciones largas.
Allow Short Entry – habilitar la apertura de posiciones cortas.
Allow Long Exit – habilitar el cierre de posiciones largas.
Allow Short Exit – habilitar el cierre de posiciones cortas.
Notas
El indicador original soporta múltiples métodos de suavizado. En este port, el filtro universal se aproxima con una media móvil exponencial estándar.
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>
/// Strategy based on bull/bear power comparison using EMA smoothing.
/// </summary>
public class BnBStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _minNetPower;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevBull;
private decimal _prevBear;
private bool _initialized;
private decimal _bullEma;
private decimal _bearEma;
private decimal _k;
private int _count;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Length { get => _length.Value; set => _length.Value = value; }
public decimal MinNetPower { get => _minNetPower.Value; set => _minNetPower.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public BnBStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles used for calculations", "General");
_length = Param(nameof(Length), 14)
.SetDisplay("EMA Length", "Length of smoothing for bulls and bears", "Parameters");
_minNetPower = Param(nameof(MinNetPower), 20m)
.SetDisplay("Minimum Net Power", "Minimum absolute net bull/bear power for entries", "Filters");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevBull = 0m;
_prevBear = 0m;
_initialized = false;
_bullEma = 0m;
_bearEma = 0m;
_k = 0m;
_count = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_k = 2m / (Length + 1m);
_count = 0;
var sma = new SimpleMovingAverage { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var bullPower = candle.HighPrice - smaValue;
var bearPower = candle.LowPrice - smaValue;
_count++;
if (_count == 1)
{
_bullEma = bullPower;
_bearEma = bearPower;
}
else
{
_bullEma = bullPower * _k + _bullEma * (1m - _k);
_bearEma = bearPower * _k + _bearEma * (1m - _k);
}
if (_count < Length)
return;
if (!_initialized)
{
_prevBull = _bullEma;
_prevBear = _bearEma;
_initialized = true;
return;
}
var netPower = _bullEma + _bearEma;
var prevNet = _prevBull + _prevBear;
var crossUp = prevNet <= 0m && netPower > 0m && Math.Abs(netPower) >= MinNetPower;
var crossDown = prevNet >= 0m && netPower < 0m && Math.Abs(netPower) >= MinNetPower;
if (_cooldownRemaining == 0)
{
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
_prevBull = _bullEma;
_prevBear = _bearEma;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bnb_strategy(Strategy):
def __init__(self):
super(bnb_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles used for calculations", "General")
self._length = self.Param("Length", 14) \
.SetDisplay("EMA Length", "Length of smoothing for bulls and bears", "Parameters")
self._min_net_power = self.Param("MinNetPower", 20.0) \
.SetDisplay("Minimum Net Power", "Minimum absolute net bull/bear power for entries", "Filters")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._prev_bull = 0.0
self._prev_bear = 0.0
self._initialized = False
self._bull_ema = 0.0
self._bear_ema = 0.0
self._k = 0.0
self._count = 0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def length(self):
return self._length.Value
@property
def min_net_power(self):
return self._min_net_power.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(bnb_strategy, self).OnReseted()
self._prev_bull = 0.0
self._prev_bear = 0.0
self._initialized = False
self._bull_ema = 0.0
self._bear_ema = 0.0
self._k = 0.0
self._count = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(bnb_strategy, self).OnStarted2(time)
self._k = 2.0 / (self.length + 1.0)
self._count = 0
sma = SimpleMovingAverage()
sma.Length = self.length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
sma_value = float(sma_value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
bull_power = float(candle.HighPrice) - sma_value
bear_power = float(candle.LowPrice) - sma_value
self._count += 1
if self._count == 1:
self._bull_ema = bull_power
self._bear_ema = bear_power
else:
self._bull_ema = bull_power * self._k + self._bull_ema * (1.0 - self._k)
self._bear_ema = bear_power * self._k + self._bear_ema * (1.0 - self._k)
if self._count < self.length:
return
if not self._initialized:
self._prev_bull = self._bull_ema
self._prev_bear = self._bear_ema
self._initialized = True
return
net_power = self._bull_ema + self._bear_ema
prev_net = self._prev_bull + self._prev_bear
min_np = float(self.min_net_power)
cross_up = prev_net <= 0 and net_power > 0 and abs(net_power) >= min_np
cross_down = prev_net >= 0 and net_power < 0 and abs(net_power) >= min_np
if self._cooldown_remaining == 0:
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_bull = self._bull_ema
self._prev_bear = self._bear_ema
def CreateClone(self):
return bnb_strategy()