Стратегия предупреждений о пересечении трендовых линий
Общее описание
Стратегия повторяет логику оригинального эксперта 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.1050 → автоматически создаётся уровень Horizontal 1 с ценой 1.1050.
Resistance|1.1180 → пользовательское имя, цвет берётся из MonitoringColor.
Breakout|Blue|1.1225 → имя и цвет заданы явно; уровень будет отслеживаться только при совпадении цвета с фильтром.
Трендовая линия должна содержать две временные отметки (2024-03-14T08:00:00Z) и соответствующие цены. Линия продолжается за пределами опорных точек, что воспроизводит поведение MetaTrader.
Последовательность работы
В OnStarted строки параметров разбираются и кэшируются в виде структурированных объектов.
Каждая закрывшаяся свеча вызывает ProcessCandle.
Если свеча открылась по одну сторону линии, а закрылась по другую, генерируется сообщение и линия помечается как обработанная.
В тексте сообщения указываются направление пересечения, расчётная цена линии и фактическая цена закрытия.
Уведомления
Стратегия не формирует всплывающих окон, все события выводятся в журнал. Хост-приложение может перенаправлять эти записи в реальные каналы связи или использовать их для построения собственной панели оповещений.
Порядок использования
Выберите инструмент и укажите соответствующий CandleType.
Заполните HorizontalLevelsInput и TrendlineDefinitions в соответствии с линиями, которые нужно отслеживать.
При первом пересечении любого уровня в журнале появится запись вида 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class trendline_cross_alert_strategy(Strategy):
"""Price crossing SMA as trendline proxy: buy on cross above, sell on cross below."""
def __init__(self):
super(trendline_cross_alert_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 20).SetGreaterThanZero().SetDisplay("MA Period", "SMA period as trendline", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(trendline_cross_alert_strategy, self).OnReseted()
self._prev_close = 0
self._prev_ma = 0
self._has_prev = False
def OnStarted2(self, time):
super(trendline_cross_alert_strategy, self).OnStarted2(time)
self._prev_close = 0
self._prev_ma = 0
self._has_prev = False
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(sma, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ma = float(ma_val)
if not self._has_prev:
self._prev_close = close
self._prev_ma = ma
self._has_prev = True
return
if self._prev_close <= self._prev_ma and close > ma and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close >= self._prev_ma and close < ma and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_ma = ma
def CreateClone(self):
return trendline_cross_alert_strategy()