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.
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
Adjunte la estrategia a un instrumento que proporcione velas horarias, diarias y mensuales (o ajuste los parámetros según corresponda).
Asegúrese de que el instrumento exponga PriceStep para que las distancias en pips se traduzcan en offsets de precio.
Configure el volumen y los parámetros de riesgo deseados en la UI o en código antes de iniciar la estrategia.
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;
}
}
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 one_h_bollinger_bands_strategy(Strategy):
def __init__(self):
super(one_h_bollinger_bands_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(one_h_bollinger_bands_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(one_h_bollinger_bands_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 one_h_bollinger_bands_strategy()