Ver en GitHub

Estrategia de 800BB

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

  1. La vela completada anterior abrió o cerró por debajo de la banda inferior de Bollinger, señalando una excursión de sobrecompra.
  2. La vela actual abre en o por encima del nivel anterior de la banda inferior (el precio ha vuelto a entrar en el canal).
  3. No hay ninguna posición larga actualmente activa. Cualquier posición corta abierta se cierra antes de abrir la nueva larga.
  4. El tamaño de la posición se calcula usando la distancia de stop basada en ATR y el porcentaje de riesgo configurado.
  5. 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

  1. La vela completada anterior abrió o cerró por encima de la banda superior de Bollinger, señalando una excursión de sobrecompra.
  2. La vela actual abre en o por debajo del nivel anterior de la banda superior (el precio ha vuelto a entrar en el canal).
  3. No hay ninguna posición corta actualmente activa. Cualquier posición larga abierta se cierra antes de abrir la nueva corta.
  4. El tamaño de la posición se determina mediante el mismo cálculo de ATR y porcentaje de riesgo.
  5. 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;
	}
}