Einfacher ATR Stop Take
Überblick
Basic ATR Stop Take portiert den MetaTrader 4 Expert Advisor „Basic ATR stop_take Expert Advisor“ zur StockSharp High-Level-Strategie API. Das System ist bewusst minimal: Es eröffnet genau eine Marktposition in der gewählten Richtung, berechnet einen Average True Range (ATR) für die Arbeitskerzen und fügt schützende Stop-Loss- und Take-Profit-Levels hinzu, die von ATR-Multiplikatoren abgeleitet werden. Sobald der Handel auf einer der beiden Ebenen abgeschlossen ist, bereitet sich die Strategie sofort auf den nächsten Aufbau in die gleiche Richtung vor.
Strategielogik
Indikatorfundament
- Durchschnittliche wahre Reichweite (ATR) – berechnet für den abonnierten Kerzentyp mit einem konfigurierbaren Lookback. Der Indikator misst die aktuelle Volatilität und skaliert sowohl die Stopp- als auch die Zielentfernung.
Einreisebestimmungen
- Wird beim Schließen jeder fertigen Kerze ausgeführt, nachdem ATR vollständig gebildet wurde.
- Wenn keine Position offen ist und der Richtungsparameter auf Kaufen eingestellt ist, wird eine Marktkauforder mit dem konfigurierten Volumen gesendet.
- Wenn keine Position offen ist und der Richtungsparameter auf Verkaufen eingestellt ist, wird ein Marktverkaufsauftrag mit dem konfigurierten Volumen gesendet.
- Wenn Sie Keine wählen, werden neue Einträge deaktiviert, während bestehende Positionen bis zu ihrer Schließung verwaltet bleiben.
Ausgangsregeln
- ATR Stop-Loss – Distanz entspricht
ATR × Stop Factor. Bei Long-Positionen wird der Stop unterhalb des Einstiegs platziert; Bei Shorts wird es oberhalb des Einstiegs platziert. Wenn das Extrem der Kerze das Niveau überschreitet, wird die Position zum Marktwert geschlossen.
- ATR Take-Profit – Distanz entspricht
ATR × Take Factor. Bei Long-Positionen liegt das Gewinnziel über dem Einstiegspunkt; Bei Shorts sitzt es unten. Bei Erreichen dieses Niveaus wird der Handel zum Marktwert geschlossen.
- Wenn einer der Multiplikatoren auf
0 gesetzt ist, ist die entsprechende Stufe deaktiviert; Die Strategie überwacht weiterhin den verbleibenden Füllstand, falls vorhanden.
Positionsmanagement
- Es ist jeweils nur eine Position zulässig. Nach einem Ausstieg wartet die Strategie auf den nächsten Kerzenschluss, bevor sie in die gleiche Richtung wieder einsteigt.
StartProtection() wird beim Start aufgerufen, damit externe manuelle Positionen vom Schutzsubsystem StockSharp überwacht werden.
Parameter
- Handelsrichtung – Seite des Marktes, auf der gehandelt werden soll (
None, Buy oder Sell).
- Handelsvolumen – Auftragsvolumen für den Einzelmarkteintritt.
- ATR Zeitraum – Anzahl der Kerzen, die in der ATR-Berechnung verwendet werden.
- Stop-Faktor – ATR-Multiplikator, angewendet auf die Stop-Loss-Distanz. Null deaktiviert den Schutzstopp.
- Take-Faktor – ATR-Multiplikator, angewendet auf die Take-Profit-Distanz. Null deaktiviert das Gewinnziel.
- Kerzentyp – Zeitrahmen der Kerzen, die für die ATR-Berechnung und Handelsverwaltung verwendet werden.
Zusätzliche Hinweise
- Die Standardparameter replizieren das Verhalten von EA (Long-Only-Modus, 0,01 Lot-Volumen, ATR-Periode 14, Stoppfaktor 1,5, Take-Faktor 2,0).
- Bei Preisvergleichen werden Kerzenhochs und -tiefs verwendet, was bedeutet, dass Stop-Loss- und Take-Profit-Auslöser auftreten, sobald das Niveau innerhalb der Kerzenspanne durchbrochen wird.
- Bei der Strategie werden keine Positionen gestapelt oder umgekehrt; Stattdessen flacht es immer ab und wartet auf den nächsten Barschluss, bevor es eine neue Bestellung aufgibt.
- In diesem Paket wird nur die C#-Implementierung bereitgestellt; Für diese Strategie gibt es keine Python-Version.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Basic ATR Stop Take strategy: EMA trend with ATR-based stop/take levels.
/// Enters on EMA direction, manages position with ATR-distance stops.
/// </summary>
public class BasicAtrStopTakeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _stopFactor;
private readonly StrategyParam<decimal> _takeFactor;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
private bool _prevAboveEma;
private bool _hasPrevSignal;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal StopFactor { get => _stopFactor.Value; set => _stopFactor.Value = value; }
public decimal TakeFactor { get => _takeFactor.Value; set => _takeFactor.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public BasicAtrStopTakeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_stopFactor = Param(nameof(StopFactor), 1.5m)
.SetDisplay("Stop Factor", "ATR multiplier for stop loss", "Risk");
_takeFactor = Param(nameof(TakeFactor), 2.0m)
.SetDisplay("Take Factor", "ATR multiplier for take profit", "Risk");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
_prevAboveEma = false;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_hasPrevSignal = false;
_candlesSinceTrade = SignalCooldownCandles;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var aboveEma = close > ema;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (Position > 0 && _entryPrice > 0)
{
if (close <= _stopPrice || close >= _takePrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
_candlesSinceTrade = 0;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (close >= _stopPrice || close <= _takePrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
_candlesSinceTrade = 0;
}
}
if (Position == 0 && atr > 0 && _hasPrevSignal && aboveEma != _prevAboveEma && _candlesSinceTrade >= SignalCooldownCandles)
{
if (aboveEma)
{
BuyMarket();
_entryPrice = close;
_stopPrice = close - atr * StopFactor;
_takePrice = close + atr * TakeFactor;
_candlesSinceTrade = 0;
}
else
{
SellMarket();
_entryPrice = close;
_stopPrice = close + atr * StopFactor;
_takePrice = close - atr * TakeFactor;
_candlesSinceTrade = 0;
}
}
_prevAboveEma = aboveEma;
_hasPrevSignal = true;
}
}
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 ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class basic_atr_stop_take_strategy(Strategy):
def __init__(self):
super(basic_atr_stop_take_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA trend period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period", "Indicators")
self._stop_factor = self.Param("StopFactor", 1.5) \
.SetDisplay("Stop Factor", "ATR multiplier for stop loss", "Risk")
self._take_factor = self.Param("TakeFactor", 2.0) \
.SetDisplay("Take Factor", "ATR multiplier for take profit", "Risk")
self._signal_cooldown = self.Param("SignalCooldownCandles", 6) \
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading")
self._ema = None
self._atr = None
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._prev_above_ema = False
self._has_prev_signal = False
self._candles_since_trade = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def stop_factor(self):
return self._stop_factor.Value
@property
def take_factor(self):
return self._take_factor.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(basic_atr_stop_take_strategy, self).OnReseted()
self._ema = None
self._atr = None
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._prev_above_ema = False
self._has_prev_signal = False
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(basic_atr_stop_take_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_period
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self._entry_price = 0.0
self._has_prev_signal = False
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(15)))
subscription.Bind(self._ema, self._atr, self._process_candle)
subscription.Start()
def _process_candle(self, candle, ema_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed or not self._atr.IsFormed:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
atr_val = float(atr_value)
above_ema = close > ema_val
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
if self.Position > 0 and self._entry_price > 0:
if close <= self._stop_price or close >= self._take_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._candles_since_trade = 0
elif self.Position < 0 and self._entry_price > 0:
if close >= self._stop_price or close <= self._take_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._candles_since_trade = 0
if self.Position == 0 and atr_val > 0 and self._has_prev_signal and above_ema != self._prev_above_ema and self._candles_since_trade >= self.signal_cooldown:
if above_ema:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - atr_val * self.stop_factor
self._take_price = close + atr_val * self.take_factor
self._candles_since_trade = 0
else:
self.SellMarket()
self._entry_price = close
self._stop_price = close + atr_val * self.stop_factor
self._take_price = close - atr_val * self.take_factor
self._candles_since_trade = 0
self._prev_above_ema = above_ema
self._has_prev_signal = True
def CreateClone(self):
return basic_atr_stop_take_strategy()