Стратегия Trendline Alert
Стратегия отслеживает две заданные пользователем линии тренда и реагирует на их пробой. Верхняя линия обозначает сопротивление, нижняя – поддержку. При закрытии свечи выше верхней линии открывается длинная позиция. При закрытии ниже нижней линии открывается короткая позиция. Дополнительный трейлинг-стоп позволяет защитить открытую позицию, двигая уровень стопа вслед за ценой.
Параметры
Breakout Points– количество пунктов, добавляемое к уровням трендовых линий для определения пробоя.Upper Line– уровень цены для бычьего пробоя.Lower Line– уровень цены для медвежьего пробоя.Start Hour– час начала работы стратегии.End Hour– час окончания работы стратегии.Use Trailing Stop– использование трейлинг-стопа.Trailing Stop Points– расстояние трейлинг-стопа в пунктах.Candle Type– таймфрейм свечей для анализа.
Принцип работы
- Стратегия подписывается на выбранные свечи.
- Для каждой завершённой свечи проверяется попадание времени в рабочий интервал.
- Пробой фиксируется, когда закрытие свечи пересекает верхнюю или нижнюю линию с учётом дополнительного порога.
- При пробое и отсутствии позиции отправляется рыночная заявка в направлении пробоя.
- При включённом трейлинг-стопе уровень стопа следует за ценой до его срабатывания.
Примечания
- Стратегия является упрощённой конверсией советника TrendlineAlert для MetaTrader. Ручное построение линий заменено параметрами с фиксированными уровнями цены.
- Вне заданных часов стратегия не совершает сделок.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that reacts to price breakouts above or below predefined trendlines.
/// </summary>
public class TrendlineAlertStrategy : Strategy
{
private readonly StrategyParam<int> _breakoutPoints;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _endHour;
private readonly StrategyParam<bool> _useTrailingStop;
private readonly StrategyParam<int> _trailingStopPoints;
private readonly StrategyParam<decimal> _upperLine;
private readonly StrategyParam<decimal> _lowerLine;
private readonly StrategyParam<DataType> _candleType;
private decimal _lastPrice;
private decimal _stopPrice;
private bool _upAlerted;
private bool _downAlerted;
/// <summary>
/// Breakout threshold in points.
/// </summary>
public int BreakoutPoints { get => _breakoutPoints.Value; set => _breakoutPoints.Value = value; }
/// <summary>
/// Trading start hour (0-23).
/// </summary>
public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }
/// <summary>
/// Trading end hour (0-24).
/// </summary>
public int EndHour { get => _endHour.Value; set => _endHour.Value = value; }
/// <summary>
/// Enable trailing stop logic.
/// </summary>
public bool UseTrailingStop { get => _useTrailingStop.Value; set => _useTrailingStop.Value = value; }
/// <summary>
/// Trailing stop distance in points.
/// </summary>
public int TrailingStopPoints { get => _trailingStopPoints.Value; set => _trailingStopPoints.Value = value; }
/// <summary>
/// Price level of the upper trendline.
/// </summary>
public decimal UpperLine { get => _upperLine.Value; set => _upperLine.Value = value; }
/// <summary>
/// Price level of the lower trendline.
/// </summary>
public decimal LowerLine { get => _lowerLine.Value; set => _lowerLine.Value = value; }
/// <summary>
/// Type of candles to subscribe.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize <see cref="TrendlineAlertStrategy"/>.
/// </summary>
public TrendlineAlertStrategy()
{
_breakoutPoints = Param(nameof(BreakoutPoints), 0)
.SetDisplay("Breakout Points", "Additional points for breakout", "General");
_startHour = Param(nameof(StartHour), 0)
.SetDisplay("Start Hour", "Strategy start hour", "General");
_endHour = Param(nameof(EndHour), 24)
.SetDisplay("End Hour", "Strategy end hour", "General");
_useTrailingStop = Param(nameof(UseTrailingStop), false)
.SetDisplay("Use Trailing Stop", "Enable trailing stop", "Protection");
_trailingStopPoints = Param(nameof(TrailingStopPoints), 5)
.SetDisplay("Trailing Stop Points", "Trailing stop distance", "Protection");
_upperLine = Param(nameof(UpperLine), 68000m)
.SetDisplay("Upper Line", "Upper trendline level", "Levels");
_lowerLine = Param(nameof(LowerLine), 62000m)
.SetDisplay("Lower Line", "Lower trendline level", "Levels");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastPrice = 0;
_stopPrice = 0;
_upAlerted = false;
_downAlerted = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
new Unit(2, UnitTypes.Percent),
new Unit(2, UnitTypes.Percent));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.OpenTime.Hour;
if (hour < StartHour || hour >= EndHour)
return;
var step = Security.PriceStep ?? 1m;
var threshold = BreakoutPoints * step;
var upper = UpperLine + threshold;
var lower = LowerLine - threshold;
var price = candle.ClosePrice;
if (!_upAlerted && price > upper && _lastPrice <= upper)
{
_upAlerted = true;
if (Position <= 0)
BuyMarket();
}
else if (!_downAlerted && price < lower && _lastPrice >= lower)
{
_downAlerted = true;
if (Position >= 0)
SellMarket();
}
if (UseTrailingStop)
UpdateTrailingStop(candle);
_lastPrice = price;
}
private void UpdateTrailingStop(ICandleMessage candle)
{
var step = Security.PriceStep ?? 1m;
var trail = TrailingStopPoints * step;
if (Position > 0)
{
_stopPrice = Math.Max(_stopPrice, candle.ClosePrice - trail);
if (candle.LowPrice <= _stopPrice)
SellMarket();
}
else if (Position < 0)
{
_stopPrice = Math.Min(_stopPrice, candle.ClosePrice + trail);
if (candle.HighPrice >= _stopPrice)
BuyMarket();
}
else
{
_stopPrice = 0;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Strategies import Strategy
class trendline_alert_strategy(Strategy):
def __init__(self):
super(trendline_alert_strategy, self).__init__()
self._breakout_points = self.Param("BreakoutPoints", 0) \
.SetDisplay("Breakout Points", "Additional points for breakout", "General")
self._start_hour = self.Param("StartHour", 0) \
.SetDisplay("Start Hour", "Strategy start hour", "General")
self._end_hour = self.Param("EndHour", 24) \
.SetDisplay("End Hour", "Strategy end hour", "General")
self._use_trailing_stop = self.Param("UseTrailingStop", False) \
.SetDisplay("Use Trailing Stop", "Enable trailing stop", "Protection")
self._trailing_stop_points = self.Param("TrailingStopPoints", 5) \
.SetDisplay("Trailing Stop Points", "Trailing stop distance", "Protection")
self._upper_line = self.Param("UpperLine", 68000.0) \
.SetDisplay("Upper Line", "Upper trendline level", "Levels")
self._lower_line = self.Param("LowerLine", 62000.0) \
.SetDisplay("Lower Line", "Lower trendline level", "Levels")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._last_price = 0.0
self._stop_price = 0.0
self._up_alerted = False
self._down_alerted = False
@property
def BreakoutPoints(self):
return self._breakout_points.Value
@BreakoutPoints.setter
def BreakoutPoints(self, value):
self._breakout_points.Value = value
@property
def StartHour(self):
return self._start_hour.Value
@StartHour.setter
def StartHour(self, value):
self._start_hour.Value = value
@property
def EndHour(self):
return self._end_hour.Value
@EndHour.setter
def EndHour(self, value):
self._end_hour.Value = value
@property
def UseTrailingStop(self):
return self._use_trailing_stop.Value
@UseTrailingStop.setter
def UseTrailingStop(self, value):
self._use_trailing_stop.Value = value
@property
def TrailingStopPoints(self):
return self._trailing_stop_points.Value
@TrailingStopPoints.setter
def TrailingStopPoints(self, value):
self._trailing_stop_points.Value = value
@property
def UpperLine(self):
return self._upper_line.Value
@UpperLine.setter
def UpperLine(self, value):
self._upper_line.Value = value
@property
def LowerLine(self):
return self._lower_line.Value
@LowerLine.setter
def LowerLine(self, value):
self._lower_line.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _update_trailing_stop(self, candle):
step_raw = self.Security.PriceStep
step = float(step_raw) if step_raw is not None else 1.0
trail = self.TrailingStopPoints * step
if self.Position > 0:
self._stop_price = max(self._stop_price, float(candle.ClosePrice) - trail)
if float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
elif self.Position < 0:
self._stop_price = min(self._stop_price, float(candle.ClosePrice) + trail)
if float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
else:
self._stop_price = 0.0
def OnStarted2(self, time):
super(trendline_alert_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(2, UnitTypes.Percent))
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
hour = candle.OpenTime.Hour
if hour < self.StartHour or hour >= self.EndHour:
return
step_raw = self.Security.PriceStep
step = float(step_raw) if step_raw is not None else 1.0
threshold = self.BreakoutPoints * step
upper = float(self.UpperLine) + threshold
lower = float(self.LowerLine) - threshold
price = float(candle.ClosePrice)
if not self._up_alerted and price > upper and self._last_price <= upper:
self._up_alerted = True
if self.Position <= 0:
self.BuyMarket()
elif not self._down_alerted and price < lower and self._last_price >= lower:
self._down_alerted = True
if self.Position >= 0:
self.SellMarket()
if self.UseTrailingStop:
self._update_trailing_stop(candle)
self._last_price = price
def OnReseted(self):
super(trendline_alert_strategy, self).OnReseted()
self._last_price = 0.0
self._stop_price = 0.0
self._up_alerted = False
self._down_alerted = False
def CreateClone(self):
return trendline_alert_strategy()