Auf GitHub ansehen

Gleiche Lautstärke- und Bereichsbalken

Überblick

Equal Volume & Range Bars portiert das MetaTrader 4-Skript equalvolumebars.mq4 nach StockSharp. Das ursprüngliche Skript generierte Offline-Charts, deren Kerzen entweder nach einer festen Anzahl von Ticks oder nachdem der Preis einen konfigurierbaren Punktbereich durchlaufen hatte, geschlossen wurden. Die Strategie reproduziert die gleiche Kerzenbildungslogik innerhalb der StockSharp-Umgebung: Sie lauscht auf Live-Ticks, lädt optional historische M1-Kerzen vor und gibt detaillierte Protokolleinträge aus, wann immer ein synthetischer Balken abgeschlossen ist.

Logik der Kerzenkonstruktion

  • Zwei BetriebsmodiEqualVolumeBars schließt den Balken, sobald das kumulierte Tick-Volumen den konfigurierten Schwellenwert überschreitet, während RangeBars erfordert, dass der Hoch-Tief-Bereich der Kerze (gemessen in Wertpapierpreisschritten) denselben numerischen Schwellenwert überschreitet.
  • Tick-gesteuerte Aktualisierungen – bei jeder Handelsaktualisierung werden die aktuellen Höchst-, Tiefst- und Schlusskurse sowie das Tick-Volumen der Kerze aktualisiert. Wenn der Schwellenwert überschritten würde, finalisiert die Strategie die vorherige Kerze mit den vorhandenen Statistiken und startet sofort einen neuen Balken mit dem aktuellen Tick als erstem Eintrag.
  • Minutenverlaufs-Seeding (optional) – wenn FromMinuteHistory aktiviert ist, spielt die Strategie abgeschlossene M1-Kerzen als Folge synthetischer Ticks ab (Eröffnung → Zwischenextreme → Schluss). Dies entspricht in etwa dem Initialisierungsschritt des Offline-Diagramms, ohne dass externe CSV-Tick-Dateien erforderlich sind.
  • Monotone Zeitstempel – Der Builder erzwingt strikt ansteigende Zeitstempel, damit Protokollkonsumenten oder nachgeschaltete Module die Daten laden können, ohne auf doppelte Zeitschlüssel zu stoßen.

Parameter

  • Arbeitsmodus – wählt zwischen der Kerzenkonstruktion EqualVolumeBars und RangeBars.
  • Ticks In Bar – Anzahl der Ticks pro Kerze (Modus mit gleichem Volumen) oder Punktspanne, gemessen in Preisschritten (Range-Modus).
  • Minutenverlauf verwenden – ermöglicht die synthetische Wiedergabe fertiger M1-Kerzen, bevor Live-Ticks eintreffen.
  • Minutenkerzentyp – Kerzenabonnement, das für den historischen Seeding-Schritt verwendet wird (standardmäßig ein Zeitrahmen von einer Minute).

Zusätzliche Hinweise

  • Die Strategie leitet die Punktgröße von Security.PriceStep ab (wobei auf Security.MinPriceStep oder 0.0001 zurückgegriffen wird, wenn keine Metadaten verfügbar sind), um die von MetaTrader verwendete Konstante _Point widerzuspiegeln.
  • Anstatt .hst-Dateien zu schreiben und ein Diagrammfenster zu aktualisieren, protokolliert der C#-Port jede fertige Kerze mit vollständigen OHLCV-Daten, sodass es einfach ist, eine andere Komponente zu füttern oder Ergebnisse mit dem MT4-Offline-Diagrammersteller zu vergleichen.
  • Es werden nie Bestellungen aufgegeben; Der Kurs konzentriert sich ausschließlich auf die Datentransformation, genau wie das Originalskript.
  • Es wird nur die C#-Version bereitgestellt. Gemäß den Konvertierungsanforderungen wird absichtlich auf eine Python-Version und einen Python-Ordner verzichtet.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Equal Volume Range Bars strategy - ATR-based volatility breakout.
/// Buys when close breaks above EMA + ATR band.
/// Sells when close breaks below EMA - ATR band.
/// </summary>
public class EqualVolumeRangeBarsStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public EqualVolumeRangeBarsStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA lookback", "Indicators");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetDisplay("ATR Period", "ATR lookback", "Indicators");

		_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
			.SetDisplay("ATR Multiplier", "ATR band multiplier", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var atr = new AverageTrueRange { Length = AtrPeriod };

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

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

		var close = candle.ClosePrice;
		var upper = ema + atr * AtrMultiplier;
		var lower = ema - atr * AtrMultiplier;

		if (close > upper && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (close < lower && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}