Die FX-CHAOS Scalp MT4-Strategie ist eine direkte Portierung des Expert Advisors MetaTrader 4, der einen Awesome Oscillator-Filter mit auf Fraktalen basierenden ZigZag-Levels kombiniert. Die StockSharp-Version behält das Multi-Timeframe-Design des ursprünglichen Systems bei: Stündliche Kerzen erzeugen Handelssignale, während tägliche Kerzen eine stärkere Zeitrahmen-Tendenz bieten. Zwei eingebettete Tracker rekonstruieren den Indikator „ZigZag on Fractals“, indem sie Fünf-Kerzen-Muster scannen und abwechselnde Swing-Hochs und -Tiefs aufzeichnen.
Handelsablauf
Datenerfassung
Stündliche Kerzen versorgen die primäre Ausführungslogik und Risikokontrolle.
Tägliche Kerzen aktualisieren den als Trendfilter verwendeten langfristigen ZigZag-Swing.
Der Awesome Oscillator (5, 34) wird in der stündlichen Reihe durch den High-Level-Indikator API bewertet.
Zick-Zack-Rekonstruktion
Jede fertige Kerze wird in einem verschiebbaren Fenster mit fünf Elementen aufbewahrt.
Wenn die mittlere Kerze ein Aufwärts-Fraktal bildet, speichert der Tracker das Hoch der Kerze als letzten Schwung und ändert die Richtung auf „Aufwärts“. Ein Down-Fraktal bewirkt dasselbe für Tiefs.
Aufeinanderfolgende Schwankungen in die gleiche Richtung werden nur dann ersetzt, wenn das neue Extrem ausgeprägter ist, was die Pufferlogik des MT4-Indikators nachahmt.
Signalerkennung
Der Breakout-Puffer fügt zwei Preisschritt-Offsets zum Hoch/Tief der vorherigen Stunde hinzu und spiegelt die 2*Point-Auffüllung im Originalcode wider.
Bei Long-Einstiegen muss die Kerze unterhalb des gepufferten Hochs öffnen, darüber schließen, unter dem letzten stündlichen ZigZag-Schwung bleiben, über dem letzten Tagesschwung schließen und den Awesome Oscillator negativ halten.
Kurze Einträge spiegeln die Bedingungen unter Verwendung der gepufferten niedrigen, oberen ZigZag-Ebene und positiven Oszillatorwerte wider.
Auftragsausführung und Konfliktlösung
Gegensätzliche Positionen werden geschlossen, bevor ein neuer Auftrag gesendet wird, sodass die Strategie niemals gleichzeitige Long- und Short-Trades vorsieht.
Der ausgeführte Schlusskurs wird gespeichert, um Stop-Loss- und Take-Profit-Abstände bei nachfolgenden Kerzen abzuleiten.
Risikomanagement
Stop-Loss- und Take-Profit-Schwellenwerte sind optional; ein Wert von 0 deaktiviert die entsprechende Regel.
Am Ende jeder fertigen Kerze prüft die Strategie, ob die Kerzenspanne den konfigurierten Stopp oder das konfigurierte Ziel berührt hat und schließt die Position, wenn das Niveau durchbrochen wurde.
Wenn ein entgegengesetzter Ausbruch auftritt, wird die Position zuerst liquidiert, dann wird der neue Handel auf derselben Kerze gesendet, um die Einzelpositionsregel beizubehalten.
Parameter
Name
Beschreibung
Volume
Handelsvolumen in Lots, angewendet auf jede Marktorder.
Stop Loss (pts)
Abstand in Punkten für den Schutzstopp. Multipliziert mit der Wertpapierpreisstufe. Zum Deaktivieren auf 0 setzen.
Take Profit (pts)
Abstand in Punkten für das Gewinnziel. Multipliziert mit der Preisstufe. Zum Deaktivieren auf 0 setzen.
Breakout Buffer
Zusätzliche Punkte, die zum vorherigen Kerzenextremum hinzugefügt wurden, bevor Ausbrüche getestet wurden. Der Standardwert reproduziert das in MT4 verwendete Polster 2*Point.
Spread (pts)
Durchschnittlicher Spread in Punkten, der bei Kaufsignalen zur Ausbruchsschwelle addiert wird, sodass der Einstieg 2*Point + spread von MT4 widerspiegelt.
Trading Candle
Primärer Zeitrahmen für Einträge (standardmäßig eine Stunde).
Daily Candle
Für den ZigZag-Filter wird ein höherer Zeitrahmen verwendet (standardmäßig ein Tag).
Implementierungshinweise
Die Strategie basiert auf den übergeordneten SubscribeCandles API und BindEx, um die direkte Arbeit mit Indikatorpuffern zu vermeiden und dabei die Repository-Richtlinien zu respektieren.
Der von Security.PriceStep abgerufene Preisschritt wird verwendet, um in Punkten ausgedrückte Parameterwerte in absolute Preisabstände umzuwandeln. Wenn dem Instrument ein Schritt fehlt, fällt der Code auf 1 zurück.
Beide ZigZag-Tracker werden am OnReseted zurückgesetzt und unterbrechen den Handel, bis sie genügend Kerzen angesammelt haben, um den ersten Schwung zu bestimmen. Dies verhindert vorzeitige Einträge, wenn historischer Kontext fehlt.
Bei der Diagrammdarstellung werden die stündlichen Kerzen, der Awesome Oscillator und die Strategie-Trades gezeichnet, um den Vergleich der StockSharp-Implementierung mit der MT4-Vorlage zu erleichtern.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class FxChaosScalpMt4Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMom;
private bool _hasPrev;
private int _cooldownRemaining;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FxChaosScalpMt4Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA filter", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 10).SetDisplay("Momentum", "Momentum period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMom = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var mom = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, mom, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal mom)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevMom = mom; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevMom = mom;
return;
}
if (close > ema && _prevMom <= 0 && mom > 0 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (close < ema && _prevMom >= 0 && mom < 0 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevMom = mom;
}
}
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, Momentum
from StockSharp.Algo.Strategies import Strategy
class fx_chaos_scalp_mt4_strategy(Strategy):
def __init__(self):
super(fx_chaos_scalp_mt4_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14).SetDisplay("EMA Period", "EMA filter", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 10).SetDisplay("Momentum", "Momentum period", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200).SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_mom = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def ema_period(self): return self._ema_period.Value
@property
def momentum_period(self): return self._momentum_period.Value
@property
def cooldown_candles(self): return self._cooldown_candles.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(fx_chaos_scalp_mt4_strategy, self).OnReseted()
self._prev_mom = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(fx_chaos_scalp_mt4_strategy, self).OnStarted2(time)
self._prev_mom = 0.0
self._has_prev = False
self._cooldown_remaining = 0
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
mom = Momentum()
mom.Length = self.momentum_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, mom, self.process_candle).Start()
def process_candle(self, candle, ema, mom):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
mom_val = float(mom)
if not self._has_prev:
self._prev_mom = mom_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_mom = mom_val
return
if close > ema_val and self._prev_mom <= 0 and mom_val > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif close < ema_val and self._prev_mom >= 0 and mom_val < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_mom = mom_val
def CreateClone(self):
return fx_chaos_scalp_mt4_strategy()