Ver en GitHub

Estrategia de BB Swing

Descripción general

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

  1. Trabajar únicamente con velas terminadas del marco temporal CandleType.
  2. Rastrear las últimas cuatro velas completadas para inspeccionar extremos recientes y cuerpos de velas.
  3. Esperar que la LWMA rápida se mantenga por encima (para largos) o por debajo (para cortos) de la LWMA lenta.
  4. 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).
  5. Requerir que la vela anterior tenga un cuerpo más fuerte que su predecesora, señalando Momentum alejándose de la banda.
  6. 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.
  7. 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.
  8. 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;
	}
}