Открыть на GitHub

Многотаймфреймовый трейдер

Стратегия анализирует девять таймфреймов свечных данных и строит для каждого линейный регрессионный канал одинаковой длины. Наклон канала на часовом таймфрейме (H1) выступает фильтром тренда: отрицательное значение разрешает только продажи, положительное — только покупки. Каналы на M5 и M1 используются для точного определения зон входа.

Логика работы

  • Источники данных: свечи на таймфреймах M1, M5, M15, M30, H1, H4, D1, W1 и MN1.
  • Индикаторы: для каждого потока рассчитывается линейная регрессия. Верхняя и нижняя границы строятся симметрично относительно центральной линии по максимальному отклонению цены закрытия за выбранное окно.
  • Фильтр тренда: проверяется знак наклона регрессии на H1. Пока наклон отрицательный, стратегия ищет только шорты; при положительном наклоне ищутся только лонги.
  • Условия входа:
    • Шорт — экстремумы последней свечи M5 и последней свечи M1 находятся выше верхних границ своих каналов при отрицательном наклоне H1.
    • Лонг — минимумы последней свечи M5 и последней свечи M1 касаются нижних границ каналов при положительном наклоне H1.
  • Исполнение: заявки отправляются по рынку с заданным объемом. Стоп-лосс рассчитывается как половина расстояния от верхней/нижней границы канала M5 до центральной линии. Тейк-профит равен самой линии M5.
  • Выход: на каждом закрытии свечи M1 проверяются уровни стопа и цели. При достижении любого из уровней позиция закрывается рыночной заявкой.
  • Управление позицией: одновременно может существовать только одна позиция (без усреднений и переворотов).

Параметры

Параметр Описание
EnableTrading Включает или выключает возможность выставления заявок.
BarsToCount Количество свечей в окне регрессионных каналов (по умолчанию 50).
Volume Объем рыночной заявки в лотах.

Дополнительные замечания

  • Увеличение BarsToCount делает наклон каналов плавнее, но замедляет реакции на изменение тренда.
  • Отображение наклонов по всем таймфреймам полезно для визуальной оценки синхронизации, хотя торговые решения принимает только наклон H1.
  • Стопы и цели пересчитываются на каждой новой свече M5, поэтому риск подстраивается под актуальную ширину канала.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Linear regression channel strategy.
/// Uses LinearReg as the center line with Highest/Lowest to form a channel.
/// Sells at upper channel, buys at lower channel, with trend filter from regression slope.
/// </summary>
public class MultiTimeFrameRegressionStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _regressionLength;
	private readonly StrategyParam<int> _channelLength;

	private decimal _prevLrValue;
	private bool _hasPrev;

	public MultiTimeFrameRegressionStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_regressionLength = Param(nameof(RegressionLength), 20)
			.SetDisplay("Regression Length", "Period for linear regression.", "Indicators");

		_channelLength = Param(nameof(ChannelLength), 20)
			.SetDisplay("Channel Length", "Period for highest/lowest channel.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RegressionLength
	{
		get => _regressionLength.Value;
		set => _regressionLength.Value = value;
	}

	public int ChannelLength
	{
		get => _channelLength.Value;
		set => _channelLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevLrValue = 0;
		_hasPrev = false;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevLrValue = 0;
		_hasPrev = false;

		var lr = new LinearReg { Length = RegressionLength };
		var highest = new Highest { Length = ChannelLength };
		var lowest = new Lowest { Length = ChannelLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(lr, highest, lowest, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, lr);
			DrawOwnTrades(area);
		}
	}

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

		var close = candle.ClosePrice;

		// Determine slope direction from regression
		var slope = _hasPrev ? lrValue - _prevLrValue : 0m;

		// Channel boundaries
		var channelMid = (highestValue + lowestValue) / 2m;
		var channelWidth = highestValue - lowestValue;

		if (channelWidth <= 0)
		{
			_prevLrValue = lrValue;
			_hasPrev = true;
			return;
		}

		// Upper/lower thresholds
		var upperThreshold = channelMid + channelWidth * 0.4m;
		var lowerThreshold = channelMid - channelWidth * 0.4m;

		// Exit conditions
		if (Position > 0 && (close >= upperThreshold || slope < 0))
		{
			SellMarket();
		}
		else if (Position < 0 && (close <= lowerThreshold || slope > 0))
		{
			BuyMarket();
		}

		// Entry conditions
		if (Position == 0)
		{
			if (close <= lowerThreshold && slope >= 0)
			{
				// Price near lower channel with flat/rising regression
				BuyMarket();
			}
			else if (close >= upperThreshold && slope <= 0)
			{
				// Price near upper channel with flat/falling regression
				SellMarket();
			}
		}

		_prevLrValue = lrValue;
		_hasPrev = true;
	}
}