Für Max V2 handelt es sich um eine Portierung des MetaTrader 4 Expert Advisors for_max_v2.mq4. Die Strategie wartet auf spezifische Zwei-Kerzen-Verschlingungsmuster und platziert dann ein symmetrisches Paar von Kauf-Stopp- und Verkaufs-Stopp-Aufträgen um die jüngste Kerze herum. Sobald eine Breakout-Order ausgeführt wird, wird die entgegengesetzte Pending-Order entfernt und die Position wird mit festen Stopps, optionalen Take-Profit-Levels und einer Trailing-Routine verwaltet, die zunächst einen kleinen Gewinn bei Break-Even sichert und dann dem Preis folgt.
Strategielogik
Erkennung von Verschlingungsmustern
Der ursprüngliche Fachberater legt zwei Einstiegsblöcke offen und beide bleiben erhalten:
Setup Typ 1 – scannt die vorherigen Max Search-Kerzen (überspringt den aktuellen Balken) und wartet darauf, dass das niedrigste Tief innerhalb dieses Bereichs vor zwei Balken auftritt oder darauf, dass das höchste Hoch vor zwei Balken auftritt. Wenn das passiert, muss die zwei Balken zurückliegende Kerze die vorherige Kerze (höheres Hoch und niedrigeres Tief) überdecken. Das Setup stellt einen Straddle um die zuletzt fertige Kerze herum her.
Setup vom Typ 2 – scannt auch die vorherigen Max Search-Kerzen, sucht aber nach dem Extrem, das einen Balken zurückliegt. Darüber hinaus muss die Kerze vor einem Balken die Kerze vor zwei Balken umhüllen. Anschließend wird ein Straddle um die jüngste Kerze gelegt. Beide Setups können koexistieren; Jeder verwaltet seine eigenen ausstehenden Aufträge und seine Ablaufuhr.
Ausstehende Auftragserteilung
Einstiegspreise – Kauf-Stopp-Aufträge werden beim vorherigen Kerzenhoch plus Gap Points platziert, Verkaufsstopp-Aufträge beim vorherigen Kerzentief minus Gap Points.
Stop-Loss – für Typ 1 ist der Long-Stop auf dem Tiefpunkt der Kerze zwei Balken zurück (abzüglich der Lücke) und der Short-Stop auf dem Hoch dieser Kerze (plus der Lücke) verankert. Typ 2 verwendet die vorherige Kerze für beide Seiten.
Take-Profit – optional. Long-Ziele addieren Gap Points + Buy Take Profit Points zum vorherigen Hoch und Short-Ziele subtrahieren Gap Points + Sell Take Profit Points vom vorherigen Tief. Wenn Sie die Take-Profit-Eingaben auf 0 setzen, werden die entsprechenden Ziele deaktiviert.
Ablauf – jeder Straddle trägt einen Gültigkeitszeitstempel, der als Order Expiry (bars) multipliziert mit dem konfigurierten Kerzenzeitrahmen berechnet wird. Wenn die ausstehenden Bestellungen bei Erreichen des Zeitstempels noch funktionieren, werden beide Seiten storniert.
Positionsmanagement
Sobald ein Buy-Stop erfüllt ist, werden alle verbleibenden Sell-Stop-Orders aus beiden Setups storniert; Nach einem kurzen Einstieg gilt die Symmetrieregel.
Stopps und Ziele werden bei abgeschlossenen Kerzen überwacht. Wenn das Tief einer Kerze den Long-Stop erreicht (oder das Hoch den Short-Stop erreicht), wird die Position mit einer Marktorder geschlossen. Der gleiche Ansatz wird für die Take-Profit-Ebenen verwendet.
Die Break-Even-Routine (Break-even Trigger und Break-even Offset) verschiebt den Stop auf den Einstiegspreis plus/minus dem konfigurierten Offset, sobald die Position um den Triggerbetrag steigt.
Der Trailing-Block hält den Stop Long/Short Trailing Buffer Punkte von der besten Exkursion entfernt, aber erst, nachdem der Preis weit genug gestiegen ist (und optional erst, nachdem der Handel bereits profitabel ist). Trailing Step verhindert zu häufige Anpassungen, indem eine Mindestverbesserung erforderlich ist, bevor der Anschlag wieder festgezogen wird.
Parameter
Volumen – Ordervolumen für jede ausstehende Stop-Order.
Buy Take Profit (Punkte) – Distanz in Punkten, die zur Berechnung des Long Take Profit verwendet wird (zum Deaktivieren auf 0 setzen).
Verkaufs-Take-Profit (Punkte) – Distanz in Punkten, die zur Berechnung des Short-Take-Profits verwendet wird (zum Deaktivieren auf 0 setzen).
Gap (Punkte) – Puffer, der den Hochs/Tiefs hinzugefügt wird, bevor Stop-Einträge platziert werden, und in die Take-Profit-Distanz gefaltet wird.
Suchtiefe – Anzahl der fertigen Kerzen, die bei der Überprüfung auf Engulfing-Setups vom Typ 1 und Typ 2 gescannt wurden.
Auftragsablauf (Balken) – Anzahl der Kerzenlängen, die ein ausstehender Straddle aktiv bleibt, bevor beide Seiten storniert werden.
Break-Even-Trigger (Punkte) – Gewinnschwelle, die die Break-Even-Stopp-Anpassung aktiviert.
Break-Even-Offset (Punkte) – zusätzlicher Puffer, der dem Einstiegspreis hinzugefügt wird, wenn der Break-Even-Stopp platziert wird.
Long Trailing Buffer (Punkte) – Nachlaufdistanz für Long-Positionen, sobald die Gewinnschwelle erreicht ist.
Short Trailing Buffer (Punkte) – Nachlaufdistanz für Short-Positionen, sobald die Gewinnschwelle erreicht ist.
Trailing Step (Punkte) – minimale Verbesserung der Stoppposition erforderlich, bevor der Trailing Stop erneut aktualisiert wird.
Trail Only After Profit – wenn aktiviert, wartet das Trailing, bis sich die Position über den Puffer hinaus bewegt hat, bevor es aktiviert wird.
Kerzentyp – Zeitrahmen der Kerzen, die zur Mustererkennung, Auftragsablauf und Ausstiegsverarbeitung verwendet werden.
Zusätzliche Hinweise
In „Punkten“ ausgedrückte Preisversätze basieren auf dem PriceStep des Wertpapiers. Symbole mit fünf (oder drei) Dezimalstellen werden automatisch in gebrochene Pip-Größen umgewandelt, genau wie in MetaTrader.
Stop-Losses und Take-Profits werden über Marktaufträge innerhalb der Strategie ausgeführt, um das Verhalten von EA bei der Verwaltung von Levels bei geschlossenen Kerzen widerzuspiegeln.
Die Strategie implementiert nicht die ungenutzte vhod_3-Funktion aus der Originalquelle; Es wurden nur die beiden aktiven Eintrittsblöcke portiert.
Dieses Paket enthält nur die C#-Implementierung; Es wird keine Python-Version bereitgestellt.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// For Max V2: N-bar engulfing breakout with EMA filter and ATR stops.
/// </summary>
public class ForMaxV2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _lookback;
private decimal _entryPrice;
private decimal _prevHigh;
private decimal _prevLow;
private readonly decimal[] _highs = new decimal[10];
private readonly decimal[] _lows = new decimal[10];
private int _barCount;
public ForMaxV2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 30)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
_lookback = Param(nameof(Lookback), 10)
.SetDisplay("Lookback", "N-bar channel lookback.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_barCount = 0;
_prevHigh = 0;
_prevLow = 0;
Array.Clear(_highs, 0, _highs.Length);
Array.Clear(_lows, 0, _lows.Length);
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_barCount = 0;
_prevHigh = 0;
_prevLow = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
var len = Math.Min(Lookback, _highs.Length);
var idx = _barCount % len;
_highs[idx] = candle.HighPrice;
_lows[idx] = candle.LowPrice;
_barCount++;
if (_barCount < len || atrVal <= 0)
return;
var high = decimal.MinValue;
var low = decimal.MaxValue;
for (var i = 0; i < len; i++)
{
if (_highs[i] > high) high = _highs[i];
if (_lows[i] < low) low = _lows[i];
}
var close = candle.ClosePrice;
if (_prevHigh == 0 || _prevLow == 0)
{
_prevHigh = high;
_prevLow = low;
return;
}
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > _prevHigh && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < _prevLow && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevHigh = high;
_prevLow = low;
}
}
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 ExponentialMovingAverage, AverageTrueRange
class for_max_v2_strategy(Strategy):
def __init__(self):
super(for_max_v2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 30) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._lookback = self.Param("Lookback", 10) \
.SetDisplay("Lookback", "N-bar channel lookback.", "Indicators")
self._entry_price = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._bar_count = 0
self._highs = []
self._lows = []
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def Lookback(self):
return self._lookback.Value
def OnStarted2(self, time):
super(for_max_v2_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._bar_count = 0
self._prev_high = 0.0
self._prev_low = 0.0
self._highs = [0.0] * 10
self._lows = [0.0] * 10
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
length = min(self.Lookback, 10)
idx = self._bar_count % length
self._highs[idx] = float(candle.HighPrice)
self._lows[idx] = float(candle.LowPrice)
self._bar_count += 1
if self._bar_count < length or av <= 0:
return
high = max(self._highs[i] for i in range(length))
low = min(self._lows[i] for i in range(length))
close = float(candle.ClosePrice)
if self._prev_high == 0 or self._prev_low == 0:
self._prev_high = high
self._prev_low = low
return
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > self._prev_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._prev_low and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_high = high
self._prev_low = low
def OnReseted(self):
super(for_max_v2_strategy, self).OnReseted()
self._entry_price = 0.0
self._bar_count = 0
self._prev_high = 0.0
self._prev_low = 0.0
self._highs = []
self._lows = []
def CreateClone(self):
return for_max_v2_strategy()