Открыть на GitHub

Стратегия предупреждений о пересечении трендовых линий

Общее описание

Стратегия повторяет логику оригинального эксперта MetaTrader: отслеживает произвольно нанесённые горизонтальные уровни и трендовые линии, и при первом пересечении закрывшейся свечой отправляет уведомление. Автоматическое выставление заявок не выполняется — модуль служит инструментом наблюдения за ключевыми зонами.

Особенности портирования

  • В расчёт берутся только те линии, цвет которых совпадает со значением параметра MonitoringColor, что соответствует фильтрации по цвету в MetaTrader.
  • После фиксации пересечения линия помечается как обработанная, благодаря чему повторные сигналы не возникают. Это эквивалент изменения цвета объекта на CrossedColor в терминале.
  • Графические объекты задаются строковыми параметрами: горизонтали описываются шаблоном Имя|Цвет|Цена, трендовые линии — Имя|Цвет|Время1|Цена1|Время2|Цена2. Линия рассматривается как бесконечная прямая, проходящая через две опорные точки.
  • Параметры EnableAlerts, EnableNotifications, EnableEmails формируют раздельные записи в журнале, что позволяет интегрировать стратегию с любыми внешними системами уведомлений.

Параметры

Параметр Тип Назначение
MonitoringColor string Цвет, по которому отбираются линии для мониторинга (без учёта регистра).
CrossedColor string Цвет, фигурирующий в сообщении после срабатывания.
HorizontalLevelsInput string Горизонтальные уровни, разделённые точкой с запятой. Формат Имя|Цвет|Цена; при отсутствии цвета используется MonitoringColor.
TrendlineDefinitions string Трендовые линии в формате Имя|Цвет|Время1|Цена1|Время2|Цена2. Время задаётся в ISO 8601 и должно совпадать с торговым часовым поясом.
EnableAlerts bool Записывать основное сообщение о пересечении.
EnableNotifications bool Добавлять вторую запись, имитирующую push-уведомление.
EnableEmails bool Добавлять запись, имитирующую письмо.
CandleType DataType Тип свечей, по которым осуществляется контроль.

Формат описания

  1. Линии разделяются символом ;.
  2. Горизонталь может опускать имя или цвет:
    • 1.1050 → автоматически создаётся уровень Horizontal 1 с ценой 1.1050.
    • Resistance|1.1180 → пользовательское имя, цвет берётся из MonitoringColor.
    • Breakout|Blue|1.1225 → имя и цвет заданы явно; уровень будет отслеживаться только при совпадении цвета с фильтром.
  3. Трендовая линия должна содержать две временные отметки (2024-03-14T08:00:00Z) и соответствующие цены. Линия продолжается за пределами опорных точек, что воспроизводит поведение MetaTrader.

Последовательность работы

  1. В OnStarted строки параметров разбираются и кэшируются в виде структурированных объектов.
  2. Каждая закрывшаяся свеча вызывает ProcessCandle.
  3. Если свеча открылась по одну сторону линии, а закрылась по другую, генерируется сообщение и линия помечается как обработанная.
  4. В тексте сообщения указываются направление пересечения, расчётная цена линии и фактическая цена закрытия.

Уведомления

Стратегия не формирует всплывающих окон, все события выводятся в журнал. Хост-приложение может перенаправлять эти записи в реальные каналы связи или использовать их для построения собственной панели оповещений.

Порядок использования

  1. Выберите инструмент и укажите соответствующий CandleType.
  2. Заполните HorizontalLevelsInput и TrendlineDefinitions в соответствии с линиями, которые нужно отслеживать.
  3. Настройте флаги уведомлений (EnableAlerts, EnableNotifications, EnableEmails).
  4. Запустите стратегию. При необходимости можно дополнительно отрисовать уровни на графике StockSharp.

Пример настроек

MonitoringColor = "Yellow"
CrossedColor = "Green"
HorizontalLevelsInput = "DailyPivot|Yellow|1.1025;WeeklyHigh|Yellow|1.1100"
TrendlineDefinitions = "UpperChannel|Yellow|2024-03-14T08:00:00Z|1.0950|2024-03-14T16:00:00Z|1.1080"
EnableAlerts = true
EnableNotifications = true
EnableEmails = false
CandleType = 15-минутные свечи

При первом пересечении любого уровня в журнале появится запись вида Price crossed horizontal line 'DailyPivot' upward ....

Риски и развитие

  • Стратегия не управляет позициями. Для автоматической торговли подключайте дополнительные модули исполнения.
  • Чтобы повторно получать уведомления по тем же уровням, перезапустите стратегию или измените строковые параметры — состояние не сохраняется между запусками.
  • При необходимости можно расширить ProcessCandle, добавив фильтрацию по времени торговых сессий, волатильности и т.п.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Trendline Cross Alert strategy. Uses price crossing SMA as trendline proxy.
/// </summary>
public class TrendlineCrossAlertStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maPeriod;

	private decimal? _prevClose;
	private decimal? _prevMa;

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

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

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

		_maPeriod = Param(nameof(MaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "SMA period as trendline", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = null;
		_prevMa = null;
	}

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

		_prevClose = null;
		_prevMa = null;

		var sma = new SimpleMovingAverage { Length = MaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, ProcessCandle)
			.Start();

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

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

		var close = candle.ClosePrice;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevClose = close;
			_prevMa = maVal;
			return;
		}

		if (_prevClose == null || _prevMa == null)
		{
			_prevClose = close;
			_prevMa = maVal;
			return;
		}

		if (_prevClose.Value <= _prevMa.Value && close > maVal && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevClose.Value >= _prevMa.Value && close < maVal && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevClose = close;
		_prevMa = maVal;
	}
}