Die Strategie erstellt den Expertenberater „Adaptive Grid Mt4“ für StockSharps hohes Niveau API neu. Es lässt ein symmetrisches Gitter fallen
Kauf-Stopp- und Verkaufsstopp-Aufträge rund um den aktuellen Kerzenschluss. Gitterentfernungen werden aus der durchschnittlichen wahren Reichweite (ATR) und abgeleitet
sind daher anpassungsfähig an die Marktvolatilität. Jede ausstehende Bestellung läuft nach einer konfigurierbaren Anzahl von Kerzen ab und behält die
Ordentliches Auftragsbuch in Seitwärtsmärkten.
Wenn eine Einstiegsorder ausgeführt wird, registriert die Strategie sofort die passenden Take-Profit- und Stop-Loss-Orders zu den berechneten Preisen
aus dem ATR-Snapshot, der das Raster erstellt hat. Schutzbefehle sind eins zu eins mit dem ausgefüllten Eintrag und bleiben bestehen, bis sie ausgeführt werden
oder manuell storniert werden.
Parameter
Parameter
Beschreibung
GridLevels
Anzahl der Stop-Orders über und unter dem Markt. Entspricht dem nGrid-Eingang des EA.
TimerBars
Anzahl der abgeschlossenen Kerzen, nach denen ein eventuell ausstehender Eintrag storniert wird (MT4 nBars).
PriceOffsetMultiplier
ATR-Multiplikator, der auf den anfänglichen Offset vom aktuellen Preis (Poffset) angewendet wird.
GridStepMultiplier
ATR-Multiplikator, der für den Abstand zwischen aufeinanderfolgenden Rasterebenen verwendet wird (Pstep).
StopLossMultiplier
ATR-Multiplikator, der den Abstand des Stop-Loss definiert, der jeder Order zugeordnet ist (StopLoss).
TakeProfitMultiplier
ATR-Multiplikator, der die Entfernung des Take-Profits (TakeProfit) definiert.
AtrPeriod
ATR Mittelungszeitraum. Spiegelt den hartcodierten Wert 14 aus dem Skript wider.
OrderVolume
Für alle ausstehenden Aufträge verwendetes Volumen (MT4 Lot).
CandleType
Zeitrahmen, der die Neuberechnung des Rasters steuert (Wtf).
Handelslogik
Abonnieren Sie Kerzen des konfigurierten CandleType und füttern Sie ein ATR(14).
Auf jeder fertigen Kerze:
Schalten Sie die interne Bartheke vor und stornieren Sie ausstehende Rasterbestellungen, die TimerBars überschreiten.
Überspringen Sie die weitere Verarbeitung, wenn der ATR nicht gebildet wurde, eine Rasterreihenfolge noch aktiv ist oder die Strategie bereits eine Position hält.
Berechnen Sie den Breakout-Offset, den Rasterabstand, die Stop-Loss- und Take-Profit-Abstände als ATR * multiplier-Werte.
Platzieren Sie GridLevels Paare von Kauf-Stopp- und Verkaufs-Stopp-Orders rund um den Kerzenschluss und normalisieren Sie die Preise mit
Security.ShrinkPrice, um die Tick-Größe des Instruments zu berücksichtigen.
Wenn ein Eintrag gefüllt ist, entfernen Sie ihn aus der verfolgten Rasterliste und erzeugen Sie die entsprechenden Schutzbefehle:
Long-Einträge erhalten einen Stop-Loss von SellStop und einen Take-Profit von SellLimit.
Short-Einträge erhalten einen Stop-Loss von BuyStop und einen Take-Profit von BuyLimit.
Schutzaufträge werden über OnOrderChanged überwacht, sodass abgeschlossene oder stornierte Einträge aus der Nachverfolgung entfernt werden
Listen.
Notizen
Das Raster wird nur dann neu erstellt, wenn keine offenen Positionen vorhanden sind und alle vorhandenen Rasteraufträge abgelaufen sind, entsprechend der What()-Logik von
das Original EA.
Die Preise werden anhand des Kerzenschlusses und nicht anhand des rohen Bid/Ask-Ticks berechnet. Dadurch bleibt die Implementierung kerzengesteuert
und gleichzeitig das gleiche symmetrische Layout auf dem Markt erzeugen.
Der für das Raster verwendete ATR-Snapshot wird auch für Schutzaufträge verwendet, um den Stop und Take-Profit pro Ticket von MetaTrader nachzuahmen
Werte.
Es gibt noch keine Python-Übersetzung, die der Anfrage entspricht.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Adaptive grid strategy using ATR-based breakout levels.
/// Simplified from the "Adaptive Grid Mt4" expert advisor to use market orders.
/// </summary>
public class AdaptiveGridMt4Strategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _breakoutMultiplier;
private readonly StrategyParam<DataType> _candleType;
private AverageTrueRange _atr;
private decimal? _prevClose;
private decimal? _prevAtr;
private decimal _stopPrice;
private decimal _takeProfitPrice;
/// <summary>
/// ATR averaging period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Breakout threshold in ATR multiples.
/// </summary>
public decimal BreakoutMultiplier
{
get => _breakoutMultiplier.Value;
set => _breakoutMultiplier.Value = value;
}
/// <summary>
/// Candle type used to drive calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public AdaptiveGridMt4Strategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Number of candles used for ATR smoothing", "Indicators");
_breakoutMultiplier = Param(nameof(BreakoutMultiplier), 2.5m)
.SetGreaterThanZero()
.SetDisplay("Breakout Multiplier", "ATR multiplier for breakout threshold", "Grid");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle type used to trigger grid recalculation", "General");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
_prevAtr = null;
_stopPrice = 0;
_takeProfitPrice = 0;
_atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_atr.IsFormed || atrValue <= 0)
{
_prevClose = candle.ClosePrice;
_prevAtr = atrValue;
return;
}
// Check protective stops
if (Position > 0)
{
if (_stopPrice > 0 && candle.LowPrice <= _stopPrice)
{
SellMarket(Math.Abs(Position));
_stopPrice = 0;
_takeProfitPrice = 0;
}
else if (_takeProfitPrice > 0 && candle.HighPrice >= _takeProfitPrice)
{
SellMarket(Math.Abs(Position));
_stopPrice = 0;
_takeProfitPrice = 0;
}
}
else if (Position < 0)
{
if (_stopPrice > 0 && candle.HighPrice >= _stopPrice)
{
BuyMarket(Math.Abs(Position));
_stopPrice = 0;
_takeProfitPrice = 0;
}
else if (_takeProfitPrice > 0 && candle.LowPrice <= _takeProfitPrice)
{
BuyMarket(Math.Abs(Position));
_stopPrice = 0;
_takeProfitPrice = 0;
}
}
if (_prevClose is not decimal prevClose || _prevAtr is not decimal prevAtr || prevAtr <= 0)
{
_prevClose = candle.ClosePrice;
_prevAtr = atrValue;
return;
}
var threshold = prevAtr * BreakoutMultiplier;
var volume = Volume;
if (volume <= 0)
volume = 1;
// Breakout up
if (candle.ClosePrice > prevClose + threshold && Position <= 0)
{
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
_stopPrice = candle.ClosePrice - atrValue * 3;
_takeProfitPrice = candle.ClosePrice + atrValue * 4;
}
// Breakout down
else if (candle.ClosePrice < prevClose - threshold && Position >= 0)
{
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
_stopPrice = candle.ClosePrice + atrValue * 3;
_takeProfitPrice = candle.ClosePrice - atrValue * 4;
}
_prevClose = candle.ClosePrice;
_prevAtr = atrValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_atr = null;
_prevClose = null;
_prevAtr = null;
_stopPrice = 0;
_takeProfitPrice = 0;
base.OnReseted();
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class adaptive_grid_mt4_strategy(Strategy):
def __init__(self):
super(adaptive_grid_mt4_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._atr_period = self.Param("AtrPeriod", 20)
self._breakout_multiplier = self.Param("BreakoutMultiplier", 2.5)
self._prev_close = 0.0
self._prev_atr = 0.0
self._has_prev = False
self._stop_price = 0.0
self._take_profit_price = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def BreakoutMultiplier(self):
return self._breakout_multiplier.Value
@BreakoutMultiplier.setter
def BreakoutMultiplier(self, value):
self._breakout_multiplier.Value = value
def OnReseted(self):
super(adaptive_grid_mt4_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_atr = 0.0
self._has_prev = False
self._stop_price = 0.0
self._take_profit_price = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(adaptive_grid_mt4_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_atr = 0.0
self._has_prev = False
self._stop_price = 0.0
self._take_profit_price = 0.0
self._entry_price = 0.0
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
atr_val = float(atr_value)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if atr_val <= 0:
self._prev_close = close
self._prev_atr = atr_val
self._has_prev = True
return
# Check protective stops
if self.Position > 0:
if self._stop_price > 0 and low <= self._stop_price:
self.SellMarket()
self._stop_price = 0.0
self._take_profit_price = 0.0
elif self._take_profit_price > 0 and high >= self._take_profit_price:
self.SellMarket()
self._stop_price = 0.0
self._take_profit_price = 0.0
elif self.Position < 0:
if self._stop_price > 0 and high >= self._stop_price:
self.BuyMarket()
self._stop_price = 0.0
self._take_profit_price = 0.0
elif self._take_profit_price > 0 and low <= self._take_profit_price:
self.BuyMarket()
self._stop_price = 0.0
self._take_profit_price = 0.0
if not self._has_prev or self._prev_atr <= 0:
self._prev_close = close
self._prev_atr = atr_val
self._has_prev = True
return
threshold = self._prev_atr * float(self.BreakoutMultiplier)
# Breakout up
if close > self._prev_close + threshold and self.Position <= 0:
self.BuyMarket()
self._stop_price = close - atr_val * 3.0
self._take_profit_price = close + atr_val * 4.0
self._entry_price = close
# Breakout down
elif close < self._prev_close - threshold and self.Position >= 0:
self.SellMarket()
self._stop_price = close + atr_val * 3.0
self._take_profit_price = close - atr_val * 4.0
self._entry_price = close
self._prev_close = close
self._prev_atr = atr_val
def CreateClone(self):
return adaptive_grid_mt4_strategy()