Esta estrategia reproduce el asesor experto MetaTrader 4 "800BB" utilizando la API de alto nivel de StockSharp. Realiza operaciones de reversión a la media cuando el precio perfora una Bollinger Band muy larga y vuelve a entrar inmediatamente al canal en la siguiente barra. El riesgo se controla mediante distancias de stop y take-profit basadas en ATR combinadas con un dimensionamiento dinámico de posición basado en el porcentaje de riesgo configurado.
Descripción general
Funciona en cualquier instrumento y marco temporal suministrado a través del parámetro CandleType.
Utiliza una Bollinger Band de 800 períodos con un envolvente de dos desviaciones estándar para detectar excursiones extremas.
Confirma entradas en la barra que abre de vuelta dentro de la banda justo después de un cierre exterior.
Dimensiona las órdenes estimando la distancia de stop derivada del ATR en pips y aplicando el RiskPercent seleccionado al valor actual de la cartera.
Replica el cálculo de pips de MetaTrader multiplicando el paso de precio por 10 cuando el símbolo tiene 3 o 5 decimales.
Lógica de trading
Configuración larga
La vela completada anterior abrió o cerró por debajo de la banda inferior de Bollinger, señalando una excursión de sobrecompra.
La vela actual abre en o por encima del nivel anterior de la banda inferior (el precio ha vuelto a entrar en el canal).
No hay ninguna posición larga actualmente activa. Cualquier posición corta abierta se cierra antes de abrir la nueva larga.
El tamaño de la posición se calcula usando la distancia de stop basada en ATR y el porcentaje de riesgo configurado.
Se envía una orden de compra de mercado en la apertura de la vela. El stop-loss se coloca StopLossAtrMultiplier × ATR por debajo del precio de entrada, mientras que el take-profit es TakeProfitAtrMultiplier × ATR por encima.
Configuración corta
La vela completada anterior abrió o cerró por encima de la banda superior de Bollinger, señalando una excursión de sobrecompra.
La vela actual abre en o por debajo del nivel anterior de la banda superior (el precio ha vuelto a entrar en el canal).
No hay ninguna posición corta actualmente activa. Cualquier posición larga abierta se cierra antes de abrir la nueva corta.
El tamaño de la posición se determina mediante el mismo cálculo de ATR y porcentaje de riesgo.
Se envía una orden de venta de mercado en la apertura de la vela. El stop-loss se coloca StopLossAtrMultiplier × ATR por encima del precio de entrada, mientras que el take-profit es TakeProfitAtrMultiplier × ATR por debajo.
Gestión de salidas
Órdenes protectoras: Los niveles de stop-loss y take-profit se rastrean internamente y se evalúan en cada vela completada. Si se supera cualquiera de los umbrales, la posición se cierra a mercado.
Señales opuestas: Cuando se activa una configuración opuesta, la posición actual se cierra antes de colocar la nueva orden.
Visualización: El EA original podía dibujar líneas verticales para operaciones potenciales. Las anotaciones de gráfico no se recrean aquí; en cambio, la estrategia dibuja velas, la Bollinger Band y las propias operaciones cuando hay un área de gráfico disponible.
Parámetros
Parámetro
Predeterminado
Descripción
RiskPercent
2
Porcentaje del valor de la cartera arriesgado por operación.
TakeProfitAtrMultiplier
1.5
Múltiplo de ATR utilizado para calcular la distancia del take-profit.
StopLossAtrMultiplier
1
Múltiplo de ATR utilizado para calcular la distancia del stop-loss.
AtrPeriod
14
Período de retrospectiva para el indicador ATR.
BollingerPeriod
800
Período de la media móvil de la Bollinger Band.
BollingerDeviation
2
Multiplicador de desviación estándar para la Bollinger Band.
CandleType
1 hour
Marco temporal (o cualquier otro tipo de vela) utilizado para la generación de señales.
Notas
Asegúrese de que el adaptador de cartera proporcione Portfolio.CurrentValue; de lo contrario, el dimensionamiento de posición basado en riesgo devuelve cero y la estrategia no operará.
Si el símbolo no expone un paso de precio o valor de tick válido, los cálculos de pips y dinero por pip recurren a valores predeterminados conservadores.
El largo período de retrospectiva de Bollinger (800 barras) significa que la primera operación solo puede ocurrir después de recibir suficientes datos históricos para calentar tanto los indicadores de Bollinger como de ATR.
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 EightHundredBbStrategy : 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 EightHundredBbStrategy()
{
_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 eight_hundred_bb_strategy(Strategy):
def __init__(self):
super(eight_hundred_bb_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(eight_hundred_bb_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(eight_hundred_bb_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 eight_hundred_bb_strategy()