Diese Strategie reproduziert das Kernverhalten des ursprünglichen Consolidation Breakout Expert Advisors für MetaTrader. Sie sucht nach engen Konsolidierungen, die durch Momentum- und MACD-Filter bestätigt werden, und eröffnet anschließend eine Position in Ausbruchsrichtung. Risiko wird über feste Take-Profit- und Stop-Loss-Distanzen in Preisschritten (Pips) gesteuert.
Funktionsweise
Der primäre Zeitrahmen wird durch den Parameter CandleType definiert. Alle Trend- und Konsolidierungsprüfungen werden auf diesen Kerzen bewertet.
Zwei linear gewichtete gleitende Durchschnitte (LWMAs), berechnet auf dem typischen Preis, liefern den Richtungsfilter. Long-Setups verlangen, dass die schnelle LWMA über der langsamen LWMA bleibt, während Short-Setups die entgegengesetzte Ausrichtung benötigen.
Eine Konsolidierung wird erkannt, wenn das Tief der Kerze vor zwei Bars unter dem Hoch der vorherigen Kerze bleibt (Long-Fall) oder wenn das vorherige Tief unter dem Hoch von vor zwei Bars liegt (Short-Fall). Dies spiegelt die Overlap-Bar-Logik der MQL-Version.
Momentum muss die Bewegung bestätigen. Der absolute Momentum-Wert (relativ zu null) muss den jeweiligen Kauf- oder Verkaufsschwellenwert überschreiten. Dies nähert den Momentum-Filter des ursprünglichen Expert Advisors um das Niveau 100 an.
Ein separater MACD, berechnet auf dem Zeitrahmen MacdCandleType, muss mit der Handelsrichtung übereinstimmen. Die Strategie prüft, ob die MACD-Linie der Signallinie sowohl auf der positiven als auch auf der negativen Seite der Achse vorausläuft, wodurch die Multi-Timeframe-Bestätigung aus dem Quellcode reproduziert wird.
Wenn alle Filter übereinstimmen und das Konto flat oder in Gegenrichtung positioniert ist, sendet die Strategie eine Marktorder mit der Größe TradeVolume. Schutzlevel werden sofort in Preisschritten neu berechnet, sodass Intrabar-Extreme Ausstiege auslösen können.
Jede abgeschlossene Kerze überwacht auch aktive Positionen. Wenn die Kerzenspanne Stop-Loss- oder Take-Profit-Niveau berührt, schließt die Strategie die Position zum Markt und setzt die Schutzziele zurück.
Indikatoren
Linear gewichteter gleitender Durchschnitt (schnell und langsam, typischer Preis)
Momentum
MACD (mit 12/26/9-Perioden auf einem höheren Zeitrahmen)
Parameter
CandleType - primärer Zeitrahmen für die Ausbruchserkennung.
MacdCandleType - Zeitrahmen für den bestätigenden MACD-Filter.
FastMaPeriod - Länge der schnellen LWMA.
SlowMaPeriod - Länge der langsamen LWMA.
MomentumLength - Rückblick für den Momentum-Filter.
MomentumBuyThreshold - minimales positives Momentum für Long-Trades.
StopLossPips - Schutz-Stop-Distanz in Preisschritten.
TakeProfitPips - Gewinnziel-Distanz in Preisschritten.
TradeVolume - Volumen, das mit jeder Marktorder gesendet wird.
Die Standardwerte spiegeln den veröffentlichten Expert Advisor: LWMA-Perioden 6 und 85, Momentum-Länge 14, Kauf-/Verkaufsschwellen 0.3, Stop-Loss 20 Pips und Take-Profit 50 Pips. Passen Sie pipbasierte Distanzen an, wenn Sie Instrumente mit anderen Preisschritten handeln.
Hinweise
Trailing Stops, Break-even-Bewegungen und Money-Management-Module aus dem MQL-Skript werden bewusst weggelassen, damit die StockSharp-Portierung auf die Kernlogik des Ausbruchs fokussiert bleibt.
Stellen Sie immer sicher, dass die ausgewählten Zeitrahmen von Ihrem Datenfeed unterstützt werden. Wenn der höhere Zeitrahmen nur spärliche Daten liefert, erwägen Sie einen niedrigeren MacdCandleType, um den MACD-Filter reaktionsfähig zu halten.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ConsolidationBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ConsolidationBreakoutStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class consolidation_breakout_strategy(Strategy):
def __init__(self):
super(consolidation_breakout_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(consolidation_breakout_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(consolidation_breakout_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return consolidation_breakout_strategy()