View on GitHub

Big Bar Sound Strategy

Overview

The Big Bar Sound Strategy reproduces the behaviour of the MetaTrader expert advisor "BigBarSound". The algorithm watches finished candles of a configurable timeframe and reports whenever the candle range is wide enough to be considered a "big bar". Instead of playing an audio file it writes detailed log messages, which can be further routed to any notification subsystem supported by StockSharp.

The strategy is purely informational – it does not submit orders or manage positions. It is designed to be used as an alerting component inside a larger automated or discretionary trading workflow.

Behaviour

  1. The strategy subscribes to the candle series specified by the Candle Type parameter.
  2. For each completed candle it measures the bar size according to the selected Difference Mode:
    • OpenClose – absolute difference between close and open price.
    • HighLow – absolute difference between the high and the low of the bar.
  3. The measured value is compared against the Point Threshold multiplied by the instrument's PriceStep. When the bar size is greater than or equal to this threshold, the strategy records a log entry that simulates playing the configured sound file.
  4. If Show Alert is enabled, an additional alert-style log message is written to highlight the event.

Because the implementation processes only finished candles, each bar can trigger at most once, mirroring the single-shot behaviour of the original MQL expert advisor.

Parameters

  • Point Threshold (BarPoint) – number of price steps that must be exceeded before an alert is triggered. The default value of 200 matches the original script. Optimisation boundaries (50–500 with step 50) are provided for convenience.
  • Difference Mode (DifferenceMode) – selects how the candle size is measured: open/close distance or full high/low range.
  • Sound File (SoundFile) – name of the WAV file that should be played. The strategy only logs this value to emulate the MetaTrader PlaySound call.
  • Show Alert (ShowAlert) – when enabled the strategy emits an extra log message to mimic the optional Alert popup from the MQL version.
  • Candle Type (CandleType) – candle data type (timeframe) to subscribe to. By default the strategy uses 1-minute candles.

Alerts and logging

The strategy uses LogInfo to announce that the sound file would have been played and AddInfoLog to provide a separate alert message. These entries contain the instrument identifier, the candle timestamp and the measured bar size, making it easy to integrate with StockSharp's logging viewers or notification sinks.

If the broker does not supply a valid PriceStep, a fallback value of 1 is used so that the strategy remains operational. Adjust the Point Threshold accordingly to reflect the actual tick size of the instrument.

Usage notes

  • Attach the strategy to any instrument that exposes candle data. The alert works equally well on forex, futures, stocks or crypto assets.
  • Combine it with other trading strategies by subscribing to its log output or by extending the class to forward events to custom handlers.
  • Since the implementation does not generate orders, Volume and position-related parameters are ignored.
  • To produce audible notifications, connect StockSharp's logging subsystem to a sound notifier or extend the code to call platform-specific audio APIs.

Differences from the original MQL expert advisor

  • The original script operated on tick data and tracked bar changes manually. The StockSharp port processes finished candles directly, which guarantees exactly one alert per bar without maintaining a separate trigger flag.
  • Audio playback is replaced with log messages so that the behaviour remains cross-platform within the StockSharp environment.
  • Parameter names follow StockSharp conventions but retain the same semantics: threshold size in points, measurement mode, optional alert and sound name.

Requirements

No additional indicators are required. Simply ensure that the selected CandleType is supported by the connected data source so that the strategy receives completed candles for processing.

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;
		}
	}
}