Auf GitHub ansehen

Strategie „Großer Balken mit Ton"

Übersicht

Die Strategie „Großer Balken mit Ton" repliziert das Verhalten des MetaTrader Expert Advisors „BigBarSound". Der Algorithmus beobachtet abgeschlossene Kerzen eines konfigurierbaren Zeitrahmens und meldet, wenn die Kerzenspanne breit genug ist, um als „großer Balken" zu gelten. Anstatt eine Audiodatei abzuspielen, schreibt er detaillierte Lognachrichten, die an jedes von StockSharp unterstützte Benachrichtigungssubsystem weitergeleitet werden können.

Die Strategie ist rein informativ – sie sendet keine Orders und verwaltet keine Positionen. Sie ist dafür ausgelegt, als Alarmierungskomponente innerhalb eines größeren automatisierten oder diskretionären Handelsworkflows verwendet zu werden.

Verhalten

  1. Die Strategie abonniert die durch den Parameter Kerzentyp angegebene Kerzenserie.
  2. Für jede abgeschlossene Kerze misst sie die Balkengröße gemäß dem gewählten Differenzmodus:
    • OpenClose – absolute Differenz zwischen Schlusskurs und Eröffnungskurs.
    • HighLow – absolute Differenz zwischen Hoch und Tief des Balkens.
  3. Der gemessene Wert wird gegen den Punkte-Schwellenwert multipliziert mit dem PriceStep des Instruments verglichen. Wenn die Balkengröße größer oder gleich diesem Schwellenwert ist, erfasst die Strategie einen Log-Eintrag, der das Abspielen der konfigurierten Sounddatei simuliert.
  4. Wenn Alarm anzeigen aktiviert ist, wird eine zusätzliche Alarmlog-Meldung geschrieben, um das Ereignis hervorzuheben.

Da die Implementierung nur abgeschlossene Kerzen verarbeitet, kann jeder Balken höchstens einmal auslösen, was das Einzelschuss-Verhalten des ursprünglichen MQL Expert Advisors widerspiegelt.

Parameter

  • Punkte-Schwellenwert (BarPoint) – Anzahl der Preisschritte, die überschritten werden müssen, bevor ein Alarm ausgelöst wird. Der Standardwert von 200 entspricht dem ursprünglichen Skript. Optimierungsgrenzen (50–500 mit Schritt 50) werden zur Vereinfachung bereitgestellt.
  • Differenzmodus (DifferenceMode) – wählt, wie die Kerzengröße gemessen wird: Öffnungs-/Schluss-Abstand oder vollständige Hoch-/Tief-Spanne.
  • Sound-Datei (SoundFile) – Name der WAV-Datei, die abgespielt werden soll. Die Strategie protokolliert nur diesen Wert, um den MetaTrader PlaySound-Aufruf zu emulieren.
  • Alarm anzeigen (ShowAlert) – wenn aktiviert, gibt die Strategie eine zusätzliche Lognachricht aus, um das optionale Alert-Popup der MQL-Version nachzuahmen.
  • Kerzentyp (CandleType) – Kerzendatentyp (Zeitrahmen), den es zu abonnieren gilt. Standardmäßig verwendet die Strategie 1-Minuten-Kerzen.

Alarme und Protokollierung

Die Strategie verwendet LogInfo, um anzukündigen, dass die Sounddatei abgespielt worden wäre, und AddInfoLog, um eine separate Alarmmeldung bereitzustellen. Diese Einträge enthalten die Instrumentenkennung, den Kerzen-Zeitstempel und die gemessene Balkengröße, was die Integration mit den Logging-Viewern oder Benachrichtigungssenken von StockSharp erleichtert.

Wenn der Broker keinen gültigen PriceStep liefert, wird ein Fallback-Wert von 1 verwendet, damit die Strategie betriebsbereit bleibt. Passen Sie den Punkte-Schwellenwert entsprechend an, um die tatsächliche Tick-Größe des Instruments widerzuspiegeln.

Verwendungshinweise

  • Fügen Sie die Strategie an jedes Instrument an, das Kerzendaten bereitstellt. Der Alarm funktioniert gleich gut bei Forex, Futures, Aktien oder Krypto-Assets.
  • Kombinieren Sie sie mit anderen Handelsstrategien, indem Sie ihre Log-Ausgabe abonnieren oder die Klasse erweitern, um Ereignisse an benutzerdefinierte Handler weiterzuleiten.
  • Da die Implementierung keine Orders generiert, werden Volume und positionsbezogene Parameter ignoriert.
  • Um hörbare Benachrichtigungen zu erzeugen, verbinden Sie das Logging-Subsystem von StockSharp mit einem Sound-Notifier oder erweitern Sie den Code, um plattformspezifische Audio-APIs aufzurufen.

Unterschiede zum ursprünglichen MQL Expert Advisor

  • Das ursprüngliche Skript arbeitete mit Tick-Daten und verfolgte Balkenänderungen manuell. Der StockSharp-Port verarbeitet abgeschlossene Kerzen direkt, was genau einen Alarm pro Balken ohne separate Trigger-Flag garantiert.
  • Audio-Wiedergabe wird durch Lognachrichten ersetzt, damit das Verhalten innerhalb der StockSharp-Umgebung plattformübergreifend bleibt.
  • Parameternamen folgen StockSharp-Konventionen, behalten aber dieselbe Semantik: Schwellengröße in Punkten, Messmodus, optionaler Alarm und Sound-Name.

Anforderungen

Es sind keine zusätzlichen Indikatoren erforderlich. Stellen Sie einfach sicher, dass der ausgewählte CandleType von der verbundenen Datenquelle unterstützt wird, damit die Strategie abgeschlossene Kerzen zur Verarbeitung erhält.

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