Ver no GitHub

Estratégia BnB

Esta estratégia é um port do Expert Advisor do MetaTrader 5 "Exp_BnB". Ela utiliza o indicador personalizado BnB (Bulls and Bears) que mede a pressão de alta e baixa dentro de cada candle e as suaviza com uma média móvel exponencial.

Como funciona

  1. Para cada candle finalizado, a estratégia calcula os valores de bulls e bears.
  2. Ambas as séries são suavizadas com EMA.
  3. Quando a linha bulls cruza acima da linha bears:
    • Qualquer posição vendida é fechada.
    • Uma posição comprada é aberta.
  4. Quando a linha bears cruza acima da linha bulls:
    • Qualquer posição comprada é fechada.
    • Uma posição vendida é aberta.
  5. Níveis de stop loss e take profit são gerenciados em pontos de preço absolutos.

Parâmetros

  • Candle Type – período dos candles usados para os cálculos.
  • EMA Length – período de suavização para bulls e bears.
  • Stop Loss – distância até o stop de proteção em pontos de preço.
  • Take Profit – distância até o alvo de lucro em pontos de preço.
  • Allow Long Entry – habilitar abertura de posições compradas.
  • Allow Short Entry – habilitar abertura de posições vendidas.
  • Allow Long Exit – habilitar fechamento de posições compradas.
  • Allow Short Exit – habilitar fechamento de posições vendidas.

Observações

O indicador original suporta múltiplos métodos de suavização. Neste port, o filtro universal é aproximado com uma média móvel exponencial padrão.

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;

/// <summary>
/// Strategy based on bull/bear power comparison using EMA smoothing.
/// </summary>
public class BnBStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _length;
	private readonly StrategyParam<decimal> _minNetPower;
	private readonly StrategyParam<int> _cooldownBars;

	private decimal _prevBull;
	private decimal _prevBear;
	private bool _initialized;
	private decimal _bullEma;
	private decimal _bearEma;
	private decimal _k;
	private int _count;
	private int _cooldownRemaining;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int Length { get => _length.Value; set => _length.Value = value; }
	public decimal MinNetPower { get => _minNetPower.Value; set => _minNetPower.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }

	public BnBStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candles used for calculations", "General");

		_length = Param(nameof(Length), 14)
			.SetDisplay("EMA Length", "Length of smoothing for bulls and bears", "Parameters");

		_minNetPower = Param(nameof(MinNetPower), 20m)
			.SetDisplay("Minimum Net Power", "Minimum absolute net bull/bear power for entries", "Filters");

		_cooldownBars = Param(nameof(CooldownBars), 4)
			.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevBull = 0m;
		_prevBear = 0m;
		_initialized = false;
		_bullEma = 0m;
		_bearEma = 0m;
		_k = 0m;
		_count = 0;
		_cooldownRemaining = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_k = 2m / (Length + 1m);
		_count = 0;

		var sma = new SimpleMovingAverage { Length = Length };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(sma, ProcessCandle).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, sma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal smaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		var bullPower = candle.HighPrice - smaValue;
		var bearPower = candle.LowPrice - smaValue;
		_count++;
		if (_count == 1)
		{
			_bullEma = bullPower;
			_bearEma = bearPower;
		}
		else
		{
			_bullEma = bullPower * _k + _bullEma * (1m - _k);
			_bearEma = bearPower * _k + _bearEma * (1m - _k);
		}

		if (_count < Length)
			return;

		if (!_initialized)
		{
			_prevBull = _bullEma;
			_prevBear = _bearEma;
			_initialized = true;
			return;
		}

		var netPower = _bullEma + _bearEma;
		var prevNet = _prevBull + _prevBear;
		var crossUp = prevNet <= 0m && netPower > 0m && Math.Abs(netPower) >= MinNetPower;
		var crossDown = prevNet >= 0m && netPower < 0m && Math.Abs(netPower) >= MinNetPower;

		if (_cooldownRemaining == 0)
		{
			if (crossUp && Position <= 0)
			{
				if (Position < 0)
					BuyMarket();

				BuyMarket();
				_cooldownRemaining = CooldownBars;
			}
			else if (crossDown && Position >= 0)
			{
				if (Position > 0)
					SellMarket();

				SellMarket();
				_cooldownRemaining = CooldownBars;
			}
		}

		_prevBull = _bullEma;
		_prevBear = _bearEma;
	}
}