GitHub で見る

ReInitChart戦略

この戦略はMetaTraderのReInitChartユーティリティをStockSharpに移植します。元のスクリプトはすべてのチャートにボタンを作成し、インジケーターを強制的に再計算するためにタイムフレームを一時的に切り替えました。StockSharpバージョンは、内部SMAインジケーターをリセットしてリフレッシュイベントをログに記録する手動リフレッシュトグルとオプションの自動タイマーを公開することで同じ精神を保持します。インジケーターが再構築されたらトレードを示すために、シンプルなSMAトレンドフォロールールが適用されます。

動作方法

  1. プライマリデータフィード – 戦略はCandleTypeで定義されたタイムフレームを購読し、長さSmaLengthのシンプル移動平均を計算します。
  2. 手動リフレッシュManualRefreshRequesttrueになると、移動平均の状態がリセットされ、フラグがクリアされ、保存されたボタンメタデータ(RefreshCommandNameRefreshCommandTextTextColorNameBackgroundColorName)とともにアクションがログに報告されます。
  3. 自動リフレッシュAutoRefreshEnabledを有効にすると、AutoRefreshIntervalごとに定期的なリセットがスケジュールされ、MetaTraderのタイマー駆動再初期化を再現します。
  4. トレードロジック – SMAが形成された後、戦略は最大1つのポジションを維持します。クローズ価格がSMAより上のときにロングになり、価格が下落したときにショートに反転し、まず反対側をクローズします。

この動作は、チャートのタイムフレームを切り替える代わりにStockSharpのイディオマティックなコンポーネント(インジケーターリセットとロギング)を使用しながら、元のエキスパートアドバイザーからすべてのチャートを再初期化するというアイデアを反映しています。

パラメーター

パラメーター 説明
CandleType ローソク足サブスクリプションの作業タイムフレーム。
SmaLength 各リフレッシュ後に再構築される移動平均に使用するローソク足の数。
AutoRefreshEnabled 定期的なリフレッシュタイマーを有効にする。
AutoRefreshInterval 自動リフレッシュイベント間の間隔。
ManualRefreshRequest 即時リフレッシュを開始するために手動でtrueに設定する。戦略は処理後にそれをクリアする。
RefreshCommandName MetaTraderのボタン名を反映するメタデータ;リフレッシュが発生したときにログに報告される。
RefreshCommandText MetaTraderのボタンキャプションを反映するメタデータ;リフレッシュが発生したときにログに報告される。
TextColorName MQLスクリプトから保存されたボタンテキストの色の説明。
BackgroundColorName MQLスクリプトから保存されたボタン背景色の説明。

使用方法

  1. 監視したい市場とタイムフレームに合わせてCandleTypeSmaLengthを設定します。
  2. スケジュールされたインジケーター再構築が必要な場合はAutoRefreshEnabledを有効にしてAutoRefreshIntervalを選択します。手動制御のみが必要な場合は無効のままにします。
  3. インジケーターの状態をフラッシュしたいときはいつでもManualRefreshRequesttrueに切り替えます。リフレッシュが登録されるとフラグは自動的にfalseに戻ります。
  4. 戦略を開始してマーケットデータを購読します。ローソク足、SMAカーブ、独自のトレードをチャートに描画し、インジケーターの準備ができると基本的なSMAトレンドフォロートレードを実行します。

元のMQLスクリプトとの違い

  • StockSharpは同じ方法でチャートボタンを公開していないため、リフレッシュトリガーは戦略パラメーターを通じて実装されます。
  • M1とM5のタイムフレームの間をジャンプする代わりに、StockSharpポートはインジケーターを直接リセットします。これはフレームワーク内でより信頼性があります。
  • ボタンのラベルと色はメタデータとしてロギング用に保持され、チャートコントロールが作成されなくてもMetaTraderインターフェースへのリンクを保持します。
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}.");
	}
}