Auf GitHub ansehen

Spread Informer Strategy

Collects detailed statistics for the bid-ask spread of the selected instrument and notifies when the spread breaks a configurable limit. The strategy continuously listens to Level1 updates, tracks maximum, minimum, and average spread in points, and logs a summary once it stops. It is useful for researching liquidity conditions before running latency-sensitive systems or optimizing trading windows in the Strategy Tester.

Details

  • Data Source: Level1 best bid and best ask quotes.
  • Statistics Captured:
    • Start and end timestamps of the observation period.
    • Maximum spread and the time when it occurred.
    • Minimum spread and the time when it occurred.
    • Average spread calculated across all observed Level1 samples.
  • Alerts:
    • Optional alert when the spread (in points) rises above the configured MaxSpreadPoints threshold.
    • Alert frequency is limited by AlertIntervalSeconds to avoid spamming the log.
    • Alerts are only triggered when the spread crosses the threshold from below.
  • Logging:
    • Real-time alerts are written through LogInfo.
    • Final statistics summary is emitted during OnStopped.
  • Default Values:
    • MaxSpreadPoints = 0 (alerts disabled).
    • AlertIntervalSeconds = 0 (no throttling).

Parameters

Name Description Default Notes
MaxSpreadPoints Maximum allowed spread in points. Set to 0 to disable alerts. 0 Points are calculated using the instrument price step.
AlertIntervalSeconds Minimum time between consecutive alerts. 0 Prevents duplicate alerts when the spread stays wide.

Usage Notes

  1. Attach the strategy to an instrument and ensure Level1 data is available.
  2. Configure MaxSpreadPoints according to the acceptable spread for the instrument.
  3. Optionally increase AlertIntervalSeconds to suppress repeated notifications during volatile periods.
  4. Stop the strategy to review the logged statistics in the terminal output.
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;
	}
}