Konvertierung des MetaTrader-Expertenberaters exp_Amstell-SL. Das System eröffnet sowohl Long- als auch Short-Positionen sofort, fügt jedes Mal neue Orders hinzu, wenn sich der Preis gegenüber dem letzten Eintrag um eine feste Anzahl von Punkten bewegt, und verlässt sich auf virtuelle (softwaregesteuerte) Take-Profit- und Stop-Loss-Levels, um jedes Ticket einzeln zu verlassen.
Strategielogik
Ersteingaben: Wenn die Strategie startet und keine offenen Geschäfte vorhanden sind, werden ein Marktkauf (bei Brief) und ein Marktverkauf (bei Geldkurs) übermittelt.
Pyramidenbildung beim Drawdown:
Long-Seite: Immer wenn der aktuelle Briefkurs ReentryPoints (Standard 10 Punkte) unter dem letzten Long-Einstiegspreis liegt, wird eine neue Kauforder im gleichen Volumen gesendet.
Short-Seite: Immer wenn das aktuelle Gebot ReentryPoints über dem letzten Short-Einstiegspreis liegt, wird eine neue Verkaufsorder mit demselben Volumen eröffnet.
Exit-Regeln (virtuelle Verwaltung):
Für jedes Long-Ticket überwacht die Strategie den besten Geld- und Briefkurs. Steigt der Bid um TakeProfitPoints gegenüber dem Orderpreis oder sinkt der Ask um StopLossPoints, wird die Position zum Marktwert geschlossen.
Für jedes Short-Ticket wird geprüft, ob der Brief um TakeProfitPoints niedriger oder das Gebot um StopLossPoints höher ist; In beiden Fällen wird der Verkaufsauftrag zum Marktwert gedeckt.
Verarbeitungsreihenfolge: Exits werden vor allen neuen Einträgen ausgewertet und replizieren das MetaTrader-Skript, das weitere Aktionen stoppt, nachdem eine Position am aktuellen Tick geschlossen wurde.
Parameter
TakeProfitPoints – Distanz (in Preisschritten), die zum Schließen profitabler Positionen verwendet wird. Standard: 30.
StopLossPoints – Entfernung (in Preisschritten) für Schutzausgänge. Standard: 30.
Volume – Losgröße für jede neu eröffnete Bestellung. Standard: 0.01.
ReentryPoints – Gegenbewegung (in Preisschritten), die erforderlich ist, um eine zusätzliche Order auf der entsprechenden Seite zu stapeln. Standard: 10.
Zusätzliche Hinweise
Der Punktwert wird von Security.PriceStep abgeleitet; Wenn er nicht von der Börse bereitgestellt wird, wird der Wert 1 verwendet.
Die Strategie kann gleichzeitig Long- und Short-Strategie sein, da sie Kauf- und Verkaufstickets unabhängig verfolgt und so dem Absicherungsverhalten des ursprünglichen Fachberaters entspricht.
Take-Profit- und Stop-Loss-Level werden virtuell durch Marktaufträge ausgeführt; Sie werden nicht in das Börsenauftragsbuch aufgenommen.
Das Risiko steigt schnell, wenn der Preis stark in eine Richtung tendiert, da zusätzliche Aufträge eröffnet werden, ohne das vorherige Risiko zu verringern.
Funktioniert am besten bei Symbolen, bei denen der Begriff „Punkt“ einem minimalen Preisanstieg entspricht, zum Beispiel bei großen Forex-Paaren mit Preisen im MetaTrader-Stil.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Amstell SL: Grid averaging strategy with ATR-based take profit and stop loss.
/// Adds positions on adverse moves and exits on profit/stop targets.
/// </summary>
public class AmstellSlStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _emaLength;
private decimal _entryPrice;
private decimal _prevEma;
private int _gridCount;
private int _cooldown;
public AmstellSlStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "EMA trend filter.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevEma = 0;
_gridCount = 0;
_cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_prevEma = 0;
_gridCount = 0;
_cooldown = 0;
var atr = new AverageTrueRange { Length = AtrLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal, decimal emaVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0 || _prevEma == 0)
{
_prevEma = emaVal;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevEma = emaVal;
if (Position == 0) return;
}
var close = candle.ClosePrice;
// Position management with grid and stops
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
_cooldown = 10;
}
else if (close <= _entryPrice - atrVal * 4m)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
_cooldown = 10;
}
else if (_gridCount < 1 && close <= _entryPrice - atrVal * 2m)
{
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
BuyMarket();
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
_cooldown = 10;
}
else if (close >= _entryPrice + atrVal * 4m)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
_cooldown = 10;
}
else if (_gridCount < 1 && close >= _entryPrice + atrVal * 2m)
{
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
SellMarket();
}
}
// Entry on EMA trend
if (Position == 0 && _cooldown == 0)
{
if (close > emaVal && emaVal > _prevEma)
{
_entryPrice = close;
_gridCount = 0;
BuyMarket();
}
else if (close < emaVal && emaVal < _prevEma)
{
_entryPrice = close;
_gridCount = 0;
SellMarket();
}
}
_prevEma = emaVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import AverageTrueRange, ExponentialMovingAverage
class amstell_sl_strategy(Strategy):
def __init__(self):
super(amstell_sl_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "EMA trend filter.", "Indicators")
self._entry_price = 0.0
self._prev_ema = 0.0
self._grid_count = 0
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
def OnStarted2(self, time):
super(amstell_sl_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._prev_ema = 0.0
self._grid_count = 0
self._cooldown = 0
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._atr, self._ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, atr_val, ema_val):
if candle.State != CandleStates.Finished:
return
av = float(atr_val)
ev = float(ema_val)
if av <= 0 or self._prev_ema == 0:
self._prev_ema = ev
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_ema = ev
if self.Position == 0:
return
close = float(candle.ClosePrice)
# Position management with grid and stops
if self.Position > 0:
if close >= self._entry_price + av * 2.5:
self.SellMarket()
self._entry_price = 0.0
self._grid_count = 0
self._cooldown = 10
elif close <= self._entry_price - av * 4.0:
self.SellMarket()
self._entry_price = 0.0
self._grid_count = 0
self._cooldown = 10
elif self._grid_count < 1 and close <= self._entry_price - av * 2.0:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.BuyMarket()
elif self.Position < 0:
if close <= self._entry_price - av * 2.5:
self.BuyMarket()
self._entry_price = 0.0
self._grid_count = 0
self._cooldown = 10
elif close >= self._entry_price + av * 4.0:
self.BuyMarket()
self._entry_price = 0.0
self._grid_count = 0
self._cooldown = 10
elif self._grid_count < 1 and close >= self._entry_price + av * 2.0:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.SellMarket()
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_ema = ev
return
# Entry on EMA trend
if self.Position == 0 and self._cooldown == 0:
if close > ev and ev > self._prev_ema:
self._entry_price = close
self._grid_count = 0
self.BuyMarket()
elif close < ev and ev < self._prev_ema:
self._entry_price = close
self._grid_count = 0
self.SellMarket()
self._prev_ema = ev
def OnReseted(self):
super(amstell_sl_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_ema = 0.0
self._grid_count = 0
self._cooldown = 0
def CreateClone(self):
return amstell_sl_strategy()