GitHub で見る

スプレッドインフォーマー戦略

選択した商品の買値と売値のスプレッドに関する詳細な統計を収集し、スプレッドが設定可能な制限を超えたときに通知します。この戦略はレベル 1 の更新を継続的にリッスンし、ポイント単位で最大、最小、平均のスプレッドを追跡し、停止すると概要をログに記録します。これは、レイテンシの影響を受けやすいシステムを実行する前に、またはストラテジー テスターで取引ウィンドウを最適化する前に、流動性の状態を調査するのに役立ちます。

詳細

  • データ ソース: レベル 1 の最良入札および最良売値。
  • 取得された統計:
    • 観察期間の開始および終了のタイムスタンプ。
    • 最大スプレッドとそれが発生した時刻。
    • 最小スプレッドとそれが発生した時間。
    • 観測されたすべてのレベル 1 サンプルにわたって計算された平均スプレッド。
  • アラート:
    • スプレッド(ポイント単位)が設定された MaxSpreadPoints しきい値を超えた場合のオプションのアラート。
    • ログのスパム送信を避けるため、アラートの頻度は AlertIntervalSeconds によって制限されます。
    • アラートは、スプレッドがしきい値を下から超えた場合にのみトリガーされます。
  • ロギング:
    • リアルタイム アラートは、LogInfo を通じて書き込まれます。
    • 最終的な統計概要は OnStopped 中に出力されます。
  • デフォルト値:
    • MaxSpreadPoints = 0 (アラートは無効)。
    • AlertIntervalSeconds = 0 (スロットリングなし)。

パラメーター

名前 説明 デフォルト 注意事項
MaxSpreadPoints 最大許容スプレッド (ポイント単位)。アラートを無効にするには、0 に設定します。 0 ポイントは商品価格ステップを使用して計算されます。
AlertIntervalSeconds 連続するアラート間の最小時間。 0 スプレッドが広いままの場合、アラートの重複を防ぎます。

使用上の注意

  1. ストラテジーを機器にアタッチし、レベル 1 データが利用可能であることを確認します。
  2. 金融商品の許容スプレッドに従って、MaxSpreadPoints を設定します。
  3. 必要に応じて、AlertIntervalSeconds を増やして、不安定な期間中に繰り返される通知を抑制します。
  4. 戦略を停止して、ターミナル出力に記録された統計を確認します。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Spread Informer strategy: CCI momentum crossover.
/// Buys when CCI crosses above zero, sells when crosses below zero.
/// </summary>
public class SpreadInformerStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _cciLevel;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevCci;
	private int _candlesSinceTrade;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public SpreadInformerStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_cciPeriod = Param(nameof(CciPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI period", "Indicators");
		_cciLevel = Param(nameof(CciLevel), 100m)
			.SetDisplay("CCI Level", "CCI threshold for crossover", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCci = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev)
		{
			if (_prevCci < -CciLevel && cciValue >= -CciLevel && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (_prevCci > CciLevel && cciValue <= CciLevel && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevCci = cciValue;
		_hasPrev = true;
	}
}