Открыть на GitHub

Стратегия Spread Informer

Собирает расширенную статистику по спреду (разнице между лучшими ценами Bid и Ask) выбранного инструмента и уведомляет, когда спред превышает заданный предел. Стратегия непрерывно подписывается на котировки Level1, отслеживает максимальное, минимальное и среднее значение спреда в пунктах и выводит сводку при завершении работы. Подходит для анализа ликвидности перед запуском чувствительных к задержкам алгоритмов или для оценки качества исторических данных в тестере.

Подробности

  • Источник данных: лучшие цены Bid и Ask из ленты Level1.
  • Собираемая статистика:
    • Время начала и окончания периода наблюдения.
    • Максимальный спред и момент его возникновения.
    • Минимальный спред и момент его возникновения.
    • Средний спред по всем обработанным событиям Level1.
  • Оповещения:
    • Необязательное предупреждение, когда спред (в пунктах) поднимается выше порога MaxSpreadPoints.
    • Частота срабатывания ограничена параметром AlertIntervalSeconds, чтобы не заспамить журнал.
    • Сигнал возникает только при пересечении порога снизу вверх.
  • Логирование:
    • Текущие предупреждения выводятся через LogInfo.
    • Итоговая сводка публикуется в методе OnStopped.
  • Значения по умолчанию:
    • MaxSpreadPoints = 0 (оповещения отключены).
    • AlertIntervalSeconds = 0 (без ограничения частоты).

Параметры

Имя Описание Значение по умолчанию Примечание
MaxSpreadPoints Максимально допустимый спред в пунктах. Значение 0 отключает оповещения. 0 Пункты рассчитываются через шаг цены инструмента.
AlertIntervalSeconds Минимальный интервал между срабатываниями оповещения. 0 Помогает избежать повторов, когда спред остаётся широким.

Рекомендации по использованию

  1. Запустите стратегию на выбранном инструменте и убедитесь, что доступны данные Level1.
  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;
	}
}