趋势线突破策略
该策略监控用户设定的两条趋势线,当价格突破这些水平时采取行动。上方趋势线代表阻力,下方趋势线代表支撑。当收盘价上穿上方趋势线时开多仓;当收盘价跌破下方趋势线时开空仓。可选的移动止损功能会随着价格移动保护已有仓位。
参数
Breakout Points– 在趋势线基础上添加的点数,用于判断是否突破。Upper Line– 看涨突破的价格水平。Lower Line– 看跌突破的价格水平。Start Hour– 交易开始时间(小时)。End Hour– 交易结束时间(小时)。Use Trailing Stop– 是否启用移动止损。Trailing Stop Points– 移动止损距离(点)。Candle Type– 用于分析的K线周期。
工作原理
- 策略订阅选定的K线序列。
- 对于每根完成的K线,检查其时间是否位于设置的交易时段内。
- 当收盘价上穿上方趋势线或下穿下方趋势线(并考虑额外点数)时判定为突破。
- 发生突破且无持仓时,将按突破方向发送市价单。
- 若启用移动止损,则止损价会随着价格移动,直到被触发。
说明
- 该策略是对MetaTrader版TrendlineAlert专家顾问的简化转换,图表上的手动趋势线被参数定义的固定价格水平所替代。
- 超出设定交易时间不会下单。
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()