Ver en GitHub

Estrategia de 1H Bollinger Bands

Descripción general

La Estrategia de 1H Bollinger Bands adapta el experto de MetaTrader "1H Bolinger Bands" a la API de alto nivel de StockSharp. La idea es operar rebotes desde las Bollinger Bands diarias mientras la tendencia horaria está alineada y el MACD mensual a largo plazo confirma la dirección. La estrategia funciona en el marco temporal H1 (por defecto) y se basa en flujos de datos de marcos temporales superiores adicionales para la confirmación.

Lógica de trading

  • Filtro de tendencia: Dos medias móviles ponderadas linealmente (LWMA 250 y 500) en el marco temporal base aseguran que solo se permitan operaciones alineadas con la dirección dominante.
  • Patrón de activación: En el marco temporal superior (diario por defecto), la estrategia busca una vela cuyo mínimo perfora por debajo de la Banda de Bollinger inferior y la siguiente vela abre de nuevo por encima (inverso para posiciones short con la banda superior). Esto replica la condición de rebote original.
  • Confirmación de momentum: El Momentum (período 14) se calcula en el marco temporal superior. Al menos una de las tres desviaciones de momentum más recientes desde 100 debe superar el umbral configurado (por defecto 0.3).
  • Filtro MACD: Un MACD mensual (12/26/9) debe estar de acuerdo con la señal. Para operaciones long, la línea MACD debe estar por encima de la línea de señal; para posiciones short, debe estar por debajo.
  • Entrada: Cuando todos los filtros se alinean, la estrategia abre una orden de mercado. Si hay una posición opuesta abierta, el volumen solicitado neutraliza la exposición existente e invierte la dirección.

Gestión de posiciones

La gestión de riesgos se implementa directamente en la estrategia usando distancias basadas en pips convertidas a través de Security.PriceStep:

  • Stop Loss: Cierra la posición una vez que el precio se mueve contra la entrada por el número configurado de pips.
  • Take Profit: Asegura ganancias cuando el precio alcanza el objetivo de pips configurado.
  • Trailing Stop (opcional): Cuando está habilitado y el movimiento supera la distancia de trailing, un nivel de trailing interno sigue el precio. Una barra que penetra ese nivel cierra la operación.
  • Break-Even (opcional): Después de que el precio avanza por la distancia de activación, el nivel de stop se mueve al precio de entrada más el offset configurado (menos para posiciones short). Un retroceso a ese nivel sale de la posición.

La gestión de ganancias basada en dinero del experto original no se recrea; la versión de StockSharp se centra en controles basados en precio para permanecer agnóstica respecto al intercambio.

Parámetros

Parámetro Descripción Por defecto
CandleType Marco temporal base para la evaluación de señales. Velas de 1 hora
HigherTimeFrame Marco temporal utilizado para Bollinger Bands y momentum. Velas de 1 día
MacdTimeFrame Marco temporal para el MACD de confirmación. Velas de 30 días
FastMaPeriod / SlowMaPeriod Longitudes de LWMA rápida/lenta en el marco temporal base. 6 / 85
TrendFastPeriod / TrendSlowPeriod Filtros de tendencia LWMA a largo plazo. 250 / 500
MomentumPeriod Lookback de momentum en el marco temporal superior. 14
MomentumThreshold Desviación absoluta mínima desde 100 para momentum. 0.3
BollingerPeriod / BollingerWidth Configuración de la Banda de Bollinger diaria. 20 / 2.0
TradeVolume Volumen base para cada nueva posición. 1
StopLossPips / TakeProfitPips Stop de protección y objetivo en pips. 20 / 50
EnableTrailing / TrailingStopPips Toggle de trailing stop y distancia. true / 40
EnableBreakEven / BreakEvenTriggerPips / BreakEvenOffsetPips Toggle de break-even, distancia de activación y offset. true / 30 / 30

Todos los parámetros numéricos se exponen a través de StrategyParam<T> y se pueden optimizar en Designer/Runner.

Notas de implementación

  • La estrategia se suscribe simultáneamente a tres flujos de velas: marco temporal base, marco temporal superior para Bollinger/Momentum y marco temporal MACD.
  • El Momentum usa el indicador estándar de StockSharp Momentum y almacena las últimas tres desviaciones para imitar la lógica MQL.
  • El volumen de trading y las distancias en pips asumen que Security.PriceStep está correctamente rellenado; de lo contrario, la lógica protectora no se activará.
  • StockSharp mantiene una única posición neta. El comportamiento de escalado "Max_Trades" del script original se simplifica a una única posición agregada en este port.
  • Las salidas basadas en equity y las características de trailing de dinero de la versión MQL se omiten intencionalmente para mantener la implementación neutra respecto al intercambio.

Uso

  1. Adjunte la estrategia a un instrumento que proporcione velas horarias, diarias y mensuales (o ajuste los parámetros según corresponda).
  2. Asegúrese de que el instrumento exponga PriceStep para que las distancias en pips se traduzcan en offsets de precio.
  3. Configure el volumen y los parámetros de riesgo deseados en la UI o en código antes de iniciar la estrategia.
  4. Inicie la estrategia; se suscribirá automáticamente a los datos necesarios, evaluará señales en velas cerradas y gestionará la posición con las reglas de protección configuradas.

Diferencias conocidas con el experto MQL

  • El trailing basado en dinero y el stop total de equity no están implementados; solo se retienen los controles basados en precio.
  • Las alertas, correos electrónicos y notificaciones push del código MQL se omiten.
  • El apilamiento de órdenes se reemplaza por el modelo de posición neta única de StockSharp.

Estos ajustes mantienen la estrategia idiomática para StockSharp mientras preservan la idea de trading central del experto 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 OneHBollingerBandsStrategy : 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 OneHBollingerBandsStrategy()
	{
		_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;
	}
}