Стратегия отката SMA с выходом по ATR
Стратегия входит при откатах, когда краткосрочная SMA располагается выше или ниже долгосрочной трендовой SMA. Длинная позиция открывается, когда цена опускается ниже быстрой SMA при сохранении её выше медленной. Короткая позиция открывается, когда цена поднимается выше быстрой SMA при нахождении её ниже медленной. Выход выполняется по значениям ATR от цены входа.
Подробности
- Условия входа:
- Лонг: закрытие < быстрой SMA и быстрая SMA > медленной SMA.
- Шорт: закрытие > быстрой SMA и быстрая SMA < медленной SMA.
- Направление: Лонг и шорт.
- Условия выхода:
- Цена достигает ATR-стопа или ATR-профита.
- Стопы: Используются множители ATR для стоп-лосса и тейк-профита.
- Параметры по умолчанию:
FastSmaLength= 8SlowSmaLength= 30AtrLength= 14AtrMultiplierSl= 1.2AtrMultiplierTp= 2.0
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: SMA, ATR
- Стопы: Да
- Сложность: Низкая
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// SMA Pullback with ATR Exits Strategy.
/// Buys on pullbacks in uptrend and sells on pullbacks in downtrend.
/// Exits are based on ATR multiples.
/// </summary>
public class SmaPullbackAtrExitsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastSmaLength;
private readonly StrategyParam<int> _slowSmaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrMultiplierSl;
private readonly StrategyParam<decimal> _atrMultiplierTp;
private readonly StrategyParam<int> _cooldownBars;
private decimal _entryPrice;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastSmaLength { get => _fastSmaLength.Value; set => _fastSmaLength.Value = value; }
public int SlowSmaLength { get => _slowSmaLength.Value; set => _slowSmaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
public decimal AtrMultiplierSl { get => _atrMultiplierSl.Value; set => _atrMultiplierSl.Value = value; }
public decimal AtrMultiplierTp { get => _atrMultiplierTp.Value; set => _atrMultiplierTp.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public SmaPullbackAtrExitsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_fastSmaLength = Param(nameof(FastSmaLength), 8)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "Fast SMA length", "Indicators");
_slowSmaLength = Param(nameof(SlowSmaLength), 30)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Slow SMA length", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR calculation length", "Indicators");
_atrMultiplierSl = Param(nameof(AtrMultiplierSl), 1.2m)
.SetDisplay("ATR SL Mult", "ATR multiplier for stop-loss", "Risk");
_atrMultiplierTp = Param(nameof(AtrMultiplierTp), 2.0m)
.SetDisplay("ATR TP Mult", "ATR multiplier for take-profit", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastSma = new SimpleMovingAverage { Length = FastSmaLength };
var slowSma = new SimpleMovingAverage { Length = SlowSmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastSma, slowSma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastSma);
DrawIndicator(area, slowSma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastSmaValue, decimal slowSmaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Check stop/TP exits first
if (Position > 0 && _entryPrice > 0)
{
var stop = _entryPrice - atrValue * AtrMultiplierSl;
var target = _entryPrice + atrValue * AtrMultiplierTp;
if (candle.LowPrice <= stop || candle.HighPrice >= target)
{
SellMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
var stop = _entryPrice + atrValue * AtrMultiplierSl;
var target = _entryPrice - atrValue * AtrMultiplierTp;
if (candle.HighPrice >= stop || candle.LowPrice <= target)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
return;
}
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
var currentPrice = candle.ClosePrice;
// Buy: pullback in uptrend (price below fast SMA, fast > slow)
if (currentPrice < fastSmaValue && fastSmaValue > slowSmaValue && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = currentPrice;
_cooldownRemaining = CooldownBars;
}
// Sell: pullback in downtrend (price above fast SMA, fast < slow)
else if (currentPrice > fastSmaValue && fastSmaValue < slowSmaValue && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = currentPrice;
_cooldownRemaining = CooldownBars;
}
}
}
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, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class sma_pullback_atr_exits_strategy(Strategy):
"""SMA Pullback ATR Exits Strategy."""
def __init__(self):
super(sma_pullback_atr_exits_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._fast_sma_length = self.Param("FastSmaLength", 8) \
.SetDisplay("Fast SMA", "Fast SMA length", "Indicators")
self._slow_sma_length = self.Param("SlowSmaLength", 30) \
.SetDisplay("Slow SMA", "Slow SMA length", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR calculation length", "Indicators")
self._atr_multiplier_sl = self.Param("AtrMultiplierSl", 1.2) \
.SetDisplay("ATR SL Mult", "ATR multiplier for stop-loss", "Risk")
self._atr_multiplier_tp = self.Param("AtrMultiplierTp", 2.0) \
.SetDisplay("ATR TP Mult", "ATR multiplier for take-profit", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._entry_price = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(sma_pullback_atr_exits_strategy, self).OnReseted()
self._entry_price = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(sma_pullback_atr_exits_strategy, self).OnStarted2(time)
fast_sma = SimpleMovingAverage()
fast_sma.Length = int(self._fast_sma_length.Value)
slow_sma = SimpleMovingAverage()
slow_sma.Length = int(self._slow_sma_length.Value)
atr = AverageTrueRange()
atr.Length = int(self._atr_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_sma, slow_sma, atr, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_sma)
self.DrawIndicator(area, slow_sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_sma_value, slow_sma_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
atr_v = float(atr_value)
atr_sl = float(self._atr_multiplier_sl.Value)
atr_tp = float(self._atr_multiplier_tp.Value)
cooldown = int(self._cooldown_bars.Value)
# Check stop/TP exits first
if self.Position > 0 and self._entry_price > 0:
stop = self._entry_price - atr_v * atr_sl
target = self._entry_price + atr_v * atr_tp
if float(candle.LowPrice) <= stop or float(candle.HighPrice) >= target:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
return
elif self.Position < 0 and self._entry_price > 0:
stop = self._entry_price + atr_v * atr_sl
target = self._entry_price - atr_v * atr_tp
if float(candle.HighPrice) >= stop or float(candle.LowPrice) <= target:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
close = float(candle.ClosePrice)
fast_v = float(fast_sma_value)
slow_v = float(slow_sma_value)
# Buy: pullback in uptrend
if close < fast_v and fast_v > slow_v and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
# Sell: pullback in downtrend
elif close > fast_v and fast_v < slow_v and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
def CreateClone(self):
return sma_pullback_atr_exits_strategy()