Ver no GitHub

Estratégia de Som de Barra Grande

Visão geral

A Estratégia de Som de Barra Grande reproduz o comportamento do consultor especialista do MetaTrader "BigBarSound". O algoritmo observa velas terminadas de um período de tempo configurável e reporta quando o intervalo da vela é amplo o suficiente para ser considerado uma "barra grande". Em vez de reproduzir um arquivo de áudio, escreve mensagens de log detalhadas que podem ser roteadas para qualquer subsistema de notificação suportado pelo StockSharp.

A estratégia é puramente informativa – não envia ordens nem gerencia posições. É projetada para ser usada como componente de alerta dentro de um fluxo de trabalho de trading automatizado ou discricionário maior.

Comportamento

  1. A estratégia se inscreve na série de velas especificada pelo parâmetro Tipo de vela.
  2. Para cada vela concluída, mede o tamanho da barra de acordo com o Modo de diferença selecionado:
    • OpenClose – diferença absoluta entre o preço de fechamento e o de abertura.
    • HighLow – diferença absoluta entre a máxima e a mínima da barra.
  3. O valor medido é comparado com o Limiar de pontos multiplicado pelo PriceStep do instrumento. Quando o tamanho da barra é maior ou igual a esse limiar, a estratégia registra uma entrada de log que simula reproduzir o arquivo de som configurado.
  4. Se Mostrar alerta estiver ativado, uma mensagem de log adicional no estilo alerta é escrita para destacar o evento.

Como a implementação processa apenas velas terminadas, cada barra pode disparar no máximo uma vez, espelhando o comportamento de disparo único do consultor especialista MQL original.

Parâmetros

  • Limiar de pontos (BarPoint) – número de passos de preço que devem ser excedidos antes que um alerta seja disparado. O valor padrão de 200 corresponde ao script original. Limites de otimização (50–500 com passo 50) são fornecidos para conveniência.
  • Modo de diferença (DifferenceMode) – seleciona como o tamanho da vela é medido: distância abertura/fechamento ou intervalo completo máxima/mínima.
  • Arquivo de som (SoundFile) – nome do arquivo WAV que deve ser reproduzido. A estratégia apenas registra esse valor para emular a chamada PlaySound do MetaTrader.
  • Mostrar alerta (ShowAlert) – quando ativado, a estratégia emite uma mensagem de log adicional para imitar o popup opcional Alert da versão MQL.
  • Tipo de vela (CandleType) – tipo de dados de vela (período de tempo) para se inscrever. Por padrão a estratégia usa velas de 1 minuto.

Alertas e registro

A estratégia usa LogInfo para anunciar que o arquivo de som teria sido reproduzido e AddInfoLog para fornecer uma mensagem de alerta separada. Essas entradas contêm o identificador do instrumento, o timestamp da vela e o tamanho medido da barra, facilitando a integração com os visualizadores de log ou destinos de notificação do StockSharp.

Se o broker não fornecer um PriceStep válido, um valor de fallback de 1 é usado para que a estratégia permaneça operacional. Ajuste o Limiar de pontos de acordo para refletir o tamanho real do tick do instrumento.

Notas de uso

  • Anexe a estratégia a qualquer instrumento que exponha dados de velas. O alerta funciona igualmente bem em forex, futuros, ações ou ativos cripto.
  • Combine-a com outras estratégias de trading inscrevendo-se na saída de log ou estendendo a classe para encaminhar eventos a manipuladores personalizados.
  • Como a implementação não gera ordens, Volume e parâmetros relacionados a posição são ignorados.
  • Para produzir notificações audíveis, conecte o subsistema de registro do StockSharp a um notificador de som ou estenda o código para chamar APIs de áudio específicas da plataforma.

Diferenças em relação ao consultor especialista MQL original

  • O script original operava com dados de ticks e rastreava as mudanças de barra manualmente. O port do StockSharp processa velas terminadas diretamente, o que garante exatamente um alerta por barra sem manter uma flag de disparo separada.
  • A reprodução de áudio é substituída por mensagens de log para que o comportamento permaneça multiplataforma dentro do ambiente StockSharp.
  • Os nomes de parâmetros seguem as convenções do StockSharp, mas retêm a mesma semântica: tamanho limiar em pontos, modo de medição, alerta opcional e nome de som.

Requisitos

Nenhum indicador adicional é necessário. Simplesmente certifique-se de que o CandleType selecionado seja suportado pela fonte de dados conectada para que a estratégia receba velas concluídas para processar.

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that trades when a candle exceeds a configurable size threshold.
/// Based on the BigBarSound MetaTrader EA concept - trades in the direction of
/// large candles with ATR-based stop-loss and take-profit.
/// </summary>
public class BigBarSoundStrategy : Strategy
{
	/// <summary>
	/// Defines how the candle size is calculated.
	/// </summary>
	public enum BigBarDifferenceModes
	{
		/// <summary>
		/// Measure the difference between close and open prices.
		/// </summary>
		OpenClose,

		/// <summary>
		/// Measure the distance between the high and low of the candle.
		/// </summary>
		HighLow,
	}

	private readonly StrategyParam<int> _barPoint;
	private readonly StrategyParam<BigBarDifferenceModes> _differenceMode;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrStopMultiplier;
	private readonly StrategyParam<decimal> _atrTpMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _stopPrice;
	private decimal _takeProfitPrice;
	private int _direction;

	/// <summary>
	/// Number of price steps required to trigger the alert.
	/// </summary>
	public int BarPoint
	{
		get => _barPoint.Value;
		set => _barPoint.Value = value;
	}

	/// <summary>
	/// Defines how the candle size is calculated.
	/// </summary>
	public BigBarDifferenceModes DifferenceMode
	{
		get => _differenceMode.Value;
		set => _differenceMode.Value = value;
	}

	/// <summary>
	/// ATR period for stop/take-profit calculations.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// ATR multiplier for stop-loss distance.
	/// </summary>
	public decimal AtrStopMultiplier
	{
		get => _atrStopMultiplier.Value;
		set => _atrStopMultiplier.Value = value;
	}

	/// <summary>
	/// ATR multiplier for take-profit distance.
	/// </summary>
	public decimal AtrTpMultiplier
	{
		get => _atrTpMultiplier.Value;
		set => _atrTpMultiplier.Value = value;
	}

	/// <summary>
	/// Candle type used to monitor the market.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="BigBarSoundStrategy"/> class.
	/// </summary>
	public BigBarSoundStrategy()
	{
		_barPoint = Param(nameof(BarPoint), 180)
			.SetGreaterThanZero()
			.SetDisplay("Point Threshold", "Number of price steps required to trigger entry", "General")
			.SetOptimize(50, 500, 50);

		_differenceMode = Param(nameof(DifferenceMode), BigBarDifferenceModes.OpenClose)
			.SetDisplay("Difference Mode", "How the candle size is calculated", "General");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "Period for ATR calculation", "Indicators")
			.SetOptimize(7, 28, 7);

		_atrStopMultiplier = Param(nameof(AtrStopMultiplier), 2m)
			.SetGreaterThanZero()
			.SetDisplay("ATR Stop Mult", "ATR multiplier for stop-loss", "Risk")
			.SetOptimize(1m, 3m, 0.5m);

		_atrTpMultiplier = Param(nameof(AtrTpMultiplier), 3m)
			.SetGreaterThanZero()
			.SetDisplay("ATR TP Mult", "ATR multiplier for take-profit", "Risk")
			.SetOptimize(1m, 4m, 0.5m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to monitor", "Data");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_stopPrice = 0m;
		_takeProfitPrice = 0m;
		_direction = 0;
	}

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

		var atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(atr, ProcessCandle)
			.Start();

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

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

		// Manage existing position
		if (Position > 0 && _direction > 0)
		{
			if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takeProfitPrice)
			{
				SellMarket(Position);
				_direction = 0;
				_stopPrice = 0m;
				_takeProfitPrice = 0m;
			}
		}
		else if (Position < 0 && _direction < 0)
		{
			if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takeProfitPrice)
			{
				BuyMarket(Math.Abs(Position));
				_direction = 0;
				_stopPrice = 0m;
				_takeProfitPrice = 0m;
			}
		}

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (Position != 0)
			return;

		if (atrValue <= 0m)
			return;

		// Calculate candle size
		var difference = DifferenceMode == BigBarDifferenceModes.OpenClose
			? Math.Abs(candle.ClosePrice - candle.OpenPrice)
			: candle.HighPrice - candle.LowPrice;

		var priceStep = Security?.PriceStep;
		var step = priceStep is null or <= 0m ? 1m : priceStep.Value;
		var threshold = step * BarPoint;

		if (difference < threshold)
			return;

		var isBullish = candle.ClosePrice > candle.OpenPrice;
		var stopDist = atrValue * AtrStopMultiplier;
		var tpDist = atrValue * AtrTpMultiplier;

		if (isBullish)
		{
			BuyMarket(Volume);
			_direction = 1;
			_stopPrice = candle.ClosePrice - stopDist;
			_takeProfitPrice = candle.ClosePrice + tpDist;
		}
		else
		{
			SellMarket(Volume);
			_direction = -1;
			_stopPrice = candle.ClosePrice + stopDist;
			_takeProfitPrice = candle.ClosePrice - tpDist;
		}
	}
}