Auf GitHub ansehen

ReInitChart-Strategie

Diese Strategie portiert das MetaTrader-Dienstprogramm ReInitChart auf StockSharp. Das ursprüngliche Skript erstellte auf jedem Chart eine Schaltfläche, die vorübergehend den Zeitrahmen wechselte, um die Neuberechnung der Indikatoren zu erzwingen. Die StockSharp-Version behält denselben Geist bei, indem sie einen manuellen Aktualisierungsschalter und optionalen automatischen Timer bereitstellt, die den internen SMA-Indikator zurücksetzen und das Aktualisierungsereignis protokollieren. Eine einfache SMA-Trendfolgeregel wird angewendet, um das Trading nach der Neuinitialisierung des Indikators zu demonstrieren.

Funktionsweise

  1. Primärer Datenfeed – die Strategie abonniert den durch CandleType definierten Zeitrahmen und berechnet einen einfachen gleitenden Durchschnitt mit der Länge SmaLength.
  2. Manuelle Aktualisierung – wenn ManualRefreshRequest auf true gesetzt wird, wird der Zustand des gleitenden Durchschnitts zurückgesetzt, das Flag wird gelöscht, und die Aktion wird im Log zusammen mit den gespeicherten Schaltflächen-Metadaten gemeldet (RefreshCommandName, RefreshCommandText, TextColorName, BackgroundColorName).
  3. Automatische Aktualisierung – die Aktivierung von AutoRefreshEnabled plant wiederkehrende Resets alle AutoRefreshInterval, was die timer-gesteuerte Neuinitialisierung von MetaTrader reproduziert.
  4. Handelslogik – nachdem der SMA gebildet ist, hält die Strategie höchstens eine Position. Sie geht long, wenn der Schlusskurs über dem SMA liegt, und wechselt zu short, wenn der Preis darunter fällt, wobei zuerst die entgegengesetzte Seite geschlossen wird.

Dieses Verhalten spiegelt die Idee der Neuinitialisierung aller Charts aus dem ursprünglichen Expert Advisor wider, während idiomatische StockSharp-Komponenten (Indikator-Reset und Logging) anstatt des Wechsels von Chart-Zeitrahmen verwendet werden.

Parameter

Parameter Beschreibung
CandleType Arbeits-Zeitrahmen für Kerzen-Abonnement.
SmaLength Anzahl der Kerzen für den gleitenden Durchschnitt, der nach jeder Aktualisierung neu erstellt wird.
AutoRefreshEnabled Aktiviert den periodischen Aktualisierungs-Timer.
AutoRefreshInterval Intervall zwischen automatischen Aktualisierungsereignissen.
ManualRefreshRequest Manuell auf true setzen, um eine sofortige Aktualisierung auszulösen. Die Strategie löscht es nach der Verarbeitung.
RefreshCommandName Metadaten, die den MetaTrader-Schaltflächennamen widerspiegeln; bei einer Aktualisierung im Log gemeldet.
RefreshCommandText Metadaten, die die MetaTrader-Schaltflächenbeschriftung widerspiegeln; bei einer Aktualisierung im Log gemeldet.
TextColorName Gespeicherte Schaltflächen-Textfarbbeschreibung aus dem MQL-Skript.
BackgroundColorName Gespeicherte Schaltflächen-Hintergrundfarbbeschreibung aus dem MQL-Skript.

Verwendung

  1. Konfigurieren Sie CandleType und SmaLength passend zum Markt und Zeitrahmen, den Sie überwachen möchten.
  2. Aktivieren Sie AutoRefreshEnabled und wählen Sie AutoRefreshInterval, wenn Sie geplante Indikator-Neuaufbauten benötigen. Lassen Sie es deaktiviert, wenn Sie nur manuelle Steuerung wünschen.
  3. Schalten Sie ManualRefreshRequest auf true, wenn Sie den Indikator-Zustand leeren möchten. Das Flag wird automatisch wieder auf false gesetzt, sobald die Aktualisierung registriert ist.
  4. Starten Sie die Strategie, um Marktdaten zu abonnieren. Sie zeichnet Kerzen, die SMA-Kurve und Ihre eigenen Trades im Chart und führt die grundlegenden SMA-Trendfolge-Trades aus, sobald der Indikator bereit ist.

Unterschiede zum ursprünglichen MQL-Skript

  • StockSharp stellt Chart-Schaltflächen nicht auf dieselbe Weise zur Verfügung, daher wird der Aktualisierungsauslöser über Strategie-Parameter implementiert.
  • Anstatt zwischen M1- und M5-Zeitrahmen zu wechseln, setzt der StockSharp-Port seine Indikatoren direkt zurück, was innerhalb des Frameworks zuverlässiger ist.
  • Schaltflächenbeschriftungen und -farben werden als Metadaten für das Logging beibehalten, um eine Verbindung zur MetaTrader-Oberfläche zu erhalten, auch wenn keine Chart-Steuerelemente erstellt werden.
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 mimics the MetaTrader ReInitChart utility by resetting indicators on demand.
/// It resets a moving average either manually or on a timer and uses the SMA for simple trend-following entries.
/// </summary>
public class ReInitChartStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _smaLength;
	private readonly StrategyParam<bool> _autoRefreshEnabled;
	private readonly StrategyParam<TimeSpan> _autoRefreshInterval;
	private readonly StrategyParam<bool> _manualRefreshRequest;
	private readonly StrategyParam<string> _refreshCommandName;
	private readonly StrategyParam<string> _refreshCommandText;
	private readonly StrategyParam<string> _textColorName;
	private readonly StrategyParam<string> _backgroundColorName;

	private SimpleMovingAverage _sma = null!;
	private bool _manualRefreshArmed;
	private DateTimeOffset? _nextAutoRefreshTime;
	private int _previousRelation;

	/// <summary>
	/// Primary candle type used to drive the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Length of the moving average that is recalculated after each refresh.
	/// </summary>
	public int SmaLength
	{
		get => _smaLength.Value;
		set => _smaLength.Value = value;
	}

	/// <summary>
	/// Enables or disables automatic refresh events.
	/// </summary>
	public bool AutoRefreshEnabled
	{
		get => _autoRefreshEnabled.Value;
		set => _autoRefreshEnabled.Value = value;
	}

	/// <summary>
	/// Interval between automatic refresh operations.
	/// </summary>
	public TimeSpan AutoRefreshInterval
	{
		get => _autoRefreshInterval.Value;
		set => _autoRefreshInterval.Value = value;
	}

	/// <summary>
	/// Manual refresh flag that emulates the original MetaTrader button press.
	/// </summary>
	public bool ManualRefreshRequest
	{
		get => _manualRefreshRequest.Value;
		set => _manualRefreshRequest.Value = value;
	}

	/// <summary>
	/// Identifier of the refresh command, matching the MetaTrader button name.
	/// </summary>
	public string RefreshCommandName
	{
		get => _refreshCommandName.Value;
		set => _refreshCommandName.Value = value;
	}

	/// <summary>
	/// Text displayed for the refresh command.
	/// </summary>
	public string RefreshCommandText
	{
		get => _refreshCommandText.Value;
		set => _refreshCommandText.Value = value;
	}

	/// <summary>
	/// Text color descriptor preserved from the original script.
	/// </summary>
	public string TextColorName
	{
		get => _textColorName.Value;
		set => _textColorName.Value = value;
	}

	/// <summary>
	/// Background color descriptor preserved from the original script.
	/// </summary>
	public string BackgroundColorName
	{
		get => _backgroundColorName.Value;
		set => _backgroundColorName.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public ReInitChartStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame used for the primary chart subscription.", "Data");

		_smaLength = Param(nameof(SmaLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("SMA Length", "Number of candles for the recalculated moving average.", "Reinitialization");

		_autoRefreshEnabled = Param(nameof(AutoRefreshEnabled), false)
			.SetDisplay("Auto Refresh", "Enable periodic indicator reinitialization.", "Reinitialization");

		_autoRefreshInterval = Param(nameof(AutoRefreshInterval), TimeSpan.FromMinutes(5))
			.SetDisplay("Refresh Interval", "Time interval between automatic refresh operations.", "Reinitialization");

		_manualRefreshRequest = Param(nameof(ManualRefreshRequest), false)
			.SetDisplay("Manual Refresh", "Set to true to trigger a one-time indicator reset.", "Reinitialization");

		_refreshCommandName = Param(nameof(RefreshCommandName), "ButtonReDraw")
			.SetDisplay("Command Name", "Identifier that matches the MetaTrader button name.", "Appearance");

		_refreshCommandText = Param(nameof(RefreshCommandText), "Recalculate")
			.SetDisplay("Command Text", "Text shown on logs to mirror the MetaTrader button caption.", "Appearance");

		_textColorName = Param(nameof(TextColorName), "NavajoWhite")
			.SetDisplay("Text Color", "Original MetaTrader button text color.", "Appearance");

		_backgroundColorName = Param(nameof(BackgroundColorName), "SlateGray")
			.SetDisplay("Background Color", "Original MetaTrader button background color.", "Appearance");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_manualRefreshArmed = false;
		_nextAutoRefreshTime = null;
		_previousRelation = 0;
	}

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

		_sma = new SMA
		{
			Length = SmaLength,
		};

		// Subscribe to the primary candle stream and bind the moving average.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_sma, ProcessCandle)
			.Start();

		// Draw candles, indicator and executed trades when charting is available.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _sma);
			DrawOwnTrades(area);
		}

		if (AutoRefreshEnabled)
		{
			_nextAutoRefreshTime = time + AutoRefreshInterval;
		}
	}

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

		// Handle manual or automatic refresh triggers using the candle close time.
		CheckManualRefresh(candle.CloseTime);
		CheckAutoRefresh(candle.CloseTime);

		// Synchronize the moving average length if the parameter changed on the fly.
		if (_sma.Length != SmaLength)
		{
			_sma.Length = SmaLength;
			_sma.Reset();
			this.LogInfo($"[{RefreshCommandName}] SMA length changed to {SmaLength}. Indicator state reset.");
			return;
		}

		if (!_sma.IsFormed)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (Volume <= 0)
			return;

		var relation = candle.ClosePrice > smaValue ? 1 : candle.ClosePrice < smaValue ? -1 : 0;
		if (relation == 0 || relation == _previousRelation)
		{
			_previousRelation = relation;
			return;
		}

		if (relation > 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));

			if (Position <= 0)
				BuyMarket(Volume);
		}
		else if (relation < 0)
		{
			if (Position > 0)
				SellMarket(Position);

			if (Position >= 0)
				SellMarket(Volume);
		}

		_previousRelation = relation;
	}

	private void CheckManualRefresh(DateTimeOffset currentTime)
	{
		if (ManualRefreshRequest)
		{
			if (!_manualRefreshArmed)
			{
				PerformRefresh(currentTime, "manual request");
				ManualRefreshRequest = false;
				_manualRefreshArmed = true;
			}
		}
		else
		{
			_manualRefreshArmed = false;
		}
	}

	private void CheckAutoRefresh(DateTimeOffset currentTime)
	{
		if (!AutoRefreshEnabled)
		{
			_nextAutoRefreshTime = null;
			return;
		}

		if (_nextAutoRefreshTime is null)
			_nextAutoRefreshTime = currentTime + AutoRefreshInterval;

		if (currentTime < _nextAutoRefreshTime.Value)
			return;

		PerformRefresh(currentTime, "automatic schedule");
		_nextAutoRefreshTime = currentTime + AutoRefreshInterval;
	}

	private void PerformRefresh(DateTimeOffset time, string reason)
	{
		// Reset the moving average so that future candles rebuild the indicator state.
		_sma.Reset();
		_previousRelation = 0;

		this.LogInfo($"[{RefreshCommandName}] {RefreshCommandText} triggered by {reason} at {time:O}. TextColor={TextColorName}, BackgroundColor={BackgroundColorName}.");
	}
}