Стратегия Trend Envelopes
Трендовая стратегия на индикаторе TrendEnvelopes. Индикатор сочетает EMA и полосы на основе ATR для поиска пробоев. Длинные позиции открываются, когда цена пробивает верхнюю полосу и появляется сигнал на покупку. Короткие позиции открываются при пробое нижней полосы с сигналом на продажу. Противоположные полосы закрывают позиции.
Подробности
- Условия входа:
- Длинная: закрытие выше верхней полосы и сигнал на покупку
- Короткая: закрытие ниже нижней полосы и сигнал на продажу
- Long/Short: оба
- Условия выхода: противоположный сигнал тренда
- Стопы: да (тейк-профит и стоп-лосс)
- Параметры по умолчанию:
MaPeriod= 14Deviation= 0.2mAtrPeriod= 15AtrSensitivity= 0.5mTakeProfit= 2000 пунктовStopLoss= 1000 пунктовCandleType= TimeSpan.FromHours(4).TimeFrame()
- Фильтры:
- Категория: Trend following
- Направление: оба
- Индикаторы: EMA, ATR
- Стопы: да
- Сложность: Средняя
- Таймфрейм: 4h
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: Средний
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 based on TrendEnvelopes indicator with ATR-based signals.
/// </summary>
public class TrendEnvelopesStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _deviation;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrSensitivity;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _buyEntry;
private readonly StrategyParam<bool> _sellEntry;
private readonly StrategyParam<bool> _buyExit;
private readonly StrategyParam<bool> _sellExit;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _stopLoss;
private ExponentialMovingAverage _ma;
private AverageTrueRange _atr;
private decimal _prevSmax;
private decimal _prevSmin;
private int _prevTrend;
private bool _initialized;
/// <summary>
/// Moving average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Percentage deviation for envelopes.
/// </summary>
public decimal Deviation
{
get => _deviation.Value;
set => _deviation.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR shift sensitivity.
/// </summary>
public decimal AtrSensitivity
{
get => _atrSensitivity.Value;
set => _atrSensitivity.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Whether long entries are allowed.
/// </summary>
public bool BuyEntry
{
get => _buyEntry.Value;
set => _buyEntry.Value = value;
}
/// <summary>
/// Whether short entries are allowed.
/// </summary>
public bool SellEntry
{
get => _sellEntry.Value;
set => _sellEntry.Value = value;
}
/// <summary>
/// Whether long positions can be closed.
/// </summary>
public bool BuyExit
{
get => _buyExit.Value;
set => _buyExit.Value = value;
}
/// <summary>
/// Whether short positions can be closed.
/// </summary>
public bool SellExit
{
get => _sellExit.Value;
set => _sellExit.Value = value;
}
/// <summary>
/// Take profit in points.
/// </summary>
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss in points.
/// </summary>
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public TrendEnvelopesStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average length", "Indicator");
_deviation = Param(nameof(Deviation), 0.2m)
.SetGreaterThanZero()
.SetDisplay("Deviation", "Percent offset for envelopes", "Indicator");
_atrPeriod = Param(nameof(AtrPeriod), 15)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation length", "Indicator");
_atrSensitivity = Param(nameof(AtrSensitivity), 0.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Sensitivity", "Multiplier for signal shift", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
_buyEntry = Param(nameof(BuyEntry), true)
.SetDisplay("Enable Long Entry", "Allow opening long positions", "Trading");
_sellEntry = Param(nameof(SellEntry), true)
.SetDisplay("Enable Short Entry", "Allow opening short positions", "Trading");
_buyExit = Param(nameof(BuyExit), true)
.SetDisplay("Enable Long Exit", "Allow closing long positions", "Trading");
_sellExit = Param(nameof(SellExit), true)
.SetDisplay("Enable Short Exit", "Allow closing short positions", "Trading");
_takeProfit = Param(nameof(TakeProfit), 2000)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Target in points", "Protection");
_stopLoss = Param(nameof(StopLoss), 1000)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Loss limit in points", "Protection");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma = default;
_atr = default;
_prevSmax = 0;
_prevSmin = 0;
_prevTrend = 0;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma = new ExponentialMovingAverage { Length = MaPeriod };
_atr = new AverageTrueRange { Length = AtrPeriod };
Indicators.Add(_ma);
Indicators.Add(_atr);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var step = Security.PriceStep ?? 1m;
StartProtection(
takeProfit: new Unit(TakeProfit * step, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss * step, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var maResult = _ma.Process(candle.ClosePrice, candle.OpenTime, true);
var atrResult = _atr.Process(candle);
if (!maResult.IsFormed || !atrResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var maValue = maResult.ToDecimal();
var atrValue = atrResult.ToDecimal();
var smax = (1m + Deviation / 100m) * maValue;
var smin = (1m - Deviation / 100m) * maValue;
var trend = _prevTrend;
if (_initialized)
{
if (candle.ClosePrice > _prevSmax)
trend = 1;
if (candle.ClosePrice < _prevSmin)
trend = -1;
}
decimal? upSignal = null;
decimal? downSignal = null;
var upTrend = false;
var downTrend = false;
if (!_initialized)
{
_prevSmax = smax;
_prevSmin = smin;
_prevTrend = 0;
_initialized = true;
return;
}
if (trend > 0)
{
if (smin < _prevSmin)
smin = _prevSmin;
upTrend = true;
if (_prevTrend <= 0)
upSignal = smin - AtrSensitivity * atrValue;
}
else if (trend < 0)
{
if (smax > _prevSmax)
smax = _prevSmax;
downTrend = true;
if (_prevTrend >= 0)
downSignal = smax + AtrSensitivity * atrValue;
}
_prevSmax = smax;
_prevSmin = smin;
_prevTrend = trend;
if (BuyExit && downTrend && Position > 0)
SellMarket();
if (SellExit && upTrend && Position < 0)
BuyMarket();
if (BuyEntry && upSignal.HasValue && Position <= 0)
BuyMarket();
if (SellEntry && downSignal.HasValue && Position >= 0)
SellMarket();
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class trend_envelopes_strategy(Strategy):
def __init__(self):
super(trend_envelopes_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 14) \
.SetDisplay("MA Period", "Moving average length", "Indicator")
self._deviation = self.Param("Deviation", 0.2) \
.SetDisplay("Deviation", "Percent offset for envelopes", "Indicator")
self._atr_period = self.Param("AtrPeriod", 15) \
.SetDisplay("ATR Period", "ATR calculation length", "Indicator")
self._atr_sensitivity = self.Param("AtrSensitivity", 0.5) \
.SetDisplay("ATR Sensitivity", "Multiplier for signal shift", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._buy_entry = self.Param("BuyEntry", True) \
.SetDisplay("Enable Long Entry", "Allow opening long positions", "Trading")
self._sell_entry = self.Param("SellEntry", True) \
.SetDisplay("Enable Short Entry", "Allow opening short positions", "Trading")
self._buy_exit = self.Param("BuyExit", True) \
.SetDisplay("Enable Long Exit", "Allow closing long positions", "Trading")
self._sell_exit = self.Param("SellExit", True) \
.SetDisplay("Enable Short Exit", "Allow closing short positions", "Trading")
self._take_profit = self.Param("TakeProfit", 2000) \
.SetDisplay("Take Profit", "Target in points", "Protection")
self._stop_loss = self.Param("StopLoss", 1000) \
.SetDisplay("Stop Loss", "Loss limit in points", "Protection")
self._ma = None
self._atr = None
self._prev_smax = 0.0
self._prev_smin = 0.0
self._prev_trend = 0
self._initialized = False
@property
def ma_period(self):
return self._ma_period.Value
@property
def deviation(self):
return self._deviation.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def atr_sensitivity(self):
return self._atr_sensitivity.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def buy_entry(self):
return self._buy_entry.Value
@property
def sell_entry(self):
return self._sell_entry.Value
@property
def buy_exit(self):
return self._buy_exit.Value
@property
def sell_exit(self):
return self._sell_exit.Value
@property
def take_profit(self):
return self._take_profit.Value
@property
def stop_loss(self):
return self._stop_loss.Value
def OnReseted(self):
super(trend_envelopes_strategy, self).OnReseted()
self._ma = None
self._atr = None
self._prev_smax = 0.0
self._prev_smin = 0.0
self._prev_trend = 0
self._initialized = False
def OnStarted2(self, time):
super(trend_envelopes_strategy, self).OnStarted2(time)
self._ma = ExponentialMovingAverage()
self._ma.Length = self.ma_period
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self.Indicators.Add(self._ma)
self.Indicators.Add(self._atr)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
step = self.Security.PriceStep
if step is None or float(step) == 0:
step = 1.0
else:
step = float(step)
self.StartProtection(
takeProfit=Unit(float(self.take_profit) * step, UnitTypes.Absolute),
stopLoss=Unit(float(self.stop_loss) * step, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ma)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
ma_result = process_float(self._ma, candle.ClosePrice, candle.OpenTime, True)
div_atr = CandleIndicatorValue(self._atr, candle)
div_atr.IsFinal = True
atr_result = self._atr.Process(div_atr)
if not ma_result.IsFormed or not atr_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ma_value = float(ma_result)
atr_value = float(atr_result)
dev = float(self.deviation)
smax = (1.0 + dev / 100.0) * ma_value
smin = (1.0 - dev / 100.0) * ma_value
trend = self._prev_trend
close = float(candle.ClosePrice)
if self._initialized:
if close > self._prev_smax:
trend = 1
if close < self._prev_smin:
trend = -1
if not self._initialized:
self._prev_smax = smax
self._prev_smin = smin
self._prev_trend = 0
self._initialized = True
return
up_signal = False
down_signal = False
up_trend = False
down_trend = False
atr_sens = float(self.atr_sensitivity)
if trend > 0:
if smin < self._prev_smin:
smin = self._prev_smin
up_trend = True
if self._prev_trend <= 0:
up_signal = True
elif trend < 0:
if smax > self._prev_smax:
smax = self._prev_smax
down_trend = True
if self._prev_trend >= 0:
down_signal = True
self._prev_smax = smax
self._prev_smin = smin
self._prev_trend = trend
if self.buy_exit and down_trend and self.Position > 0:
self.SellMarket()
if self.sell_exit and up_trend and self.Position < 0:
self.BuyMarket()
if self.buy_entry and up_signal and self.Position <= 0:
self.BuyMarket()
if self.sell_entry and down_signal and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return trend_envelopes_strategy()