La Estrategia BB Swing es un port fiel del asesor experto de MetaTrader "BB SWING". Opera retrocesos de las Bandas de Bollinger alineados con la tendencia predominante definida por dos medias móviles ponderadas linealmente (LWMAs). Un filtro de Momentum de marco temporal superior y un MACD muy lento ayudan a confirmar la fortaleza de la reversión antes de abrir cualquier posición.
Lógica de trading
Trabajar únicamente con velas terminadas del marco temporal CandleType.
Rastrear las últimas cuatro velas completadas para inspeccionar extremos recientes y cuerpos de velas.
Esperar que la LWMA rápida se mantenga por encima (para largos) o por debajo (para cortos) de la LWMA lenta.
Verificar que uno de los últimos tres mínimos toca la banda inferior de Bollinger (configuración larga) o uno de los máximos toca la banda superior (configuración corta).
Requerir que la vela anterior tenga un cuerpo más fuerte que su predecesora, señalando Momentum alejándose de la banda.
Confirmar la fortaleza de la tendencia con el Momentum calculado en MomentumCandleType. La estrategia mide la distancia absoluta entre la lectura de Momentum y 100; la distancia debe superar los umbrales de compra/venta configurados en cualquiera de los últimos tres valores de Momentum.
Validar la dirección a largo plazo con un MACD calculado en el marco temporal MacdCandleType. Las entradas largas se permiten mientras la línea principal del MACD se mantiene por encima de la línea de señal; los cortos requieren la relación opuesta.
Cuando todas las condiciones se alinean, entrar en una posición de mercado usando el volumen del paso martingala actual.
Dimensionamiento de posición y escalado
InitialVolume define el volumen de la primera entrada.
Cada add-on adicional multiplica el volumen base por LotExponent (volume = InitialVolume * LotExponent^n).
MaxTrades limita el número de add-ons secuenciales para que el tamaño total de la posición nunca exceda InitialVolume * MaxTrades.
Reglas de salida y protección
Valores fijos de StopLoss y TakeProfit expresados en pasos de precio.
Lógica opcional de punto de equilibrio (EnableBreakEven) que mueve el stop a BreakEvenOffset una vez que el precio avanza BreakEvenTrigger pasos.
Trailing stop clásico (EnableTrailingStop) que sigue el precio extremo por TrailingStop pasos.
Herramientas de gestión de capital:
UseMoneyTakeProfit cierra posiciones cuando la ganancia no realizada en moneda de cuenta alcanza MoneyTakeProfit.
UsePercentTakeProfit cierra posiciones cuando la ganancia equivale a PercentTakeProfit porcentaje del capital inicial.
UseMoneyTrailing activa un trailing de ganancia: una vez que la ganancia supera MoneyTrailTarget, un retroceso de MoneyTrailStop activa una salida.
UseEquityStop monitorea el drawdown de capital relativo al pico de capital registrado durante la sesión. Un drawdown mayor que EquityRiskPercent cierra todas las posiciones.
CloseOnMacdCross opcional sale siempre que la línea principal del MACD cruce la línea de señal contra la dirección de la posición actual.
Todas las acciones de protección dependen de órdenes de mercado (BuyMarket / SellMarket) para neutralizar toda la posición.
Parámetros
Nombre
Descripción
InitialVolume
Volumen base de trading usado para la primera entrada.
LotExponent
Multiplicador aplicado al volumen de cada entrada adicional al escalar.
MaxTrades
Número máximo de add-ons secuenciales permitidos en cualquier momento.
TakeProfit
Take profit expresado en pasos de precio.
StopLoss
Stop loss expresado en pasos de precio.
FastMaPeriod
Período de la LWMA rápida calculada en precios típicos.
SlowMaPeriod
Período de la LWMA lenta calculada en precios típicos.
MomentumLength
Número de barras usadas en el cálculo del Momentum.
MomentumBuyThreshold
Distancia mínima desde 100 para que el Momentum de marco temporal superior valide operaciones largas.
MomentumSellThreshold
Distancia mínima desde 100 para que el Momentum de marco temporal superior valide operaciones cortas.
EnableBreakEven
Habilita el movimiento de stop al punto de equilibrio.
BreakEvenTrigger
Pasos de precio requeridos para activar el movimiento de punto de equilibrio.
BreakEvenOffset
Offset aplicado al stop una vez que se activa el punto de equilibrio.
EnableTrailingStop
Habilita el trailing stop clásico en pasos de precio.
TrailingStop
Tamaño del trailing stop expresado en pasos.
UseMoneyTakeProfit
Habilita toma de ganancias fija en moneda de cuenta.
MoneyTakeProfit
Ganancia en moneda que cierra la posición cuando UseMoneyTakeProfit está activo.
UsePercentTakeProfit
Habilita toma de ganancias basada en porcentaje de capital.
PercentTakeProfit
Porcentaje del capital inicial que activa una salida cuando UsePercentTakeProfit está activo.
UseMoneyTrailing
Habilita trailing basado en capital tras alcanzar una ganancia objetivo.
MoneyTrailTarget
Nivel de ganancia que activa la lógica de trailing monetario.
MoneyTrailStop
Máximo retroceso permitido en moneda tras la activación.
UseEquityStop
Habilita el cierre de posiciones cuando el drawdown flotante supera un umbral.
EquityRiskPercent
Máximo drawdown de capital permitido en porcentaje.
CloseOnMacdCross
Habilita el filtrado de salida basado en MACD.
CandleType
Marco temporal primario usado para los cálculos de señales.
MomentumCandleType
Marco temporal superior usado para el filtro de Momentum.
MacdCandleType
Marco temporal muy lento usado por el filtro de salida MACD.
Notas
La estrategia procesa únicamente velas terminadas; no reacciona dentro de una barra.
Todos los cálculos de stop y objetivo utilizan el paso de precio del instrumento reportado por el exchange conectado. Asegúrate de que PriceStep esté configurado correctamente para un control preciso del riesgo.
Las protecciones monetarias y basadas en capital dependen de las estadísticas del portafolio de estrategia disponibles en StockSharp. Al ejecutar en modo tester, asegúrate de que la alimentación del portafolio esté habilitada.
A diferencia del experto MQL original, esta implementación en C# mantiene una sola posición agregada por dirección. El escalado aumenta la posición agregada en lugar de abrir múltiples tickets discretos.
Las Bandas de Bollinger usan una longitud fija de 20 y una anchura de 2 desviaciones estándar en precios típicos, coincidiendo con el código original.
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 BbSwingStrategy : 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 BbSwingStrategy()
{
_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 bb_swing_strategy(Strategy):
def __init__(self):
super(bb_swing_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(bb_swing_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(bb_swing_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 bb_swing_strategy()