Diese Strategie ist eine C#-Portierung des MetaTrader 4-Experten Frbestexp02_1_maloma_mod.mq4. Es kombiniert OsMA-Momentum, fraktale Umkehrungen, Bestätigung des Tick-Volumens und einen rollierenden täglichen Pivot-Filter, um erschöpfte Bewegungen im M15-Zeitrahmen auszublenden.
Handelslogik
Sitzungs-Pivot – ein rollierender Pivot-Punkt wird aus dem höchsten Hoch, dem niedrigsten Tief und dem ältesten Schlusskurs innerhalb eines konfigurierbaren Fensters berechnet (standardmäßig 96 Kerzen, entspricht einem Handelstag bei M15). Es sind nur Trades erlaubt, die mit dem Pivot-Bias übereinstimmen: Shorts über dem Pivot und Long-Positionen darunter.
Fraktales Muster – die Strategie wartet auf ein bestätigtes Bill Williams-Fraktal drei Kerzen zurück. Abwärtsfraktale (Swing-Tiefs) ermöglichen Short-Positionen, während Aufwärts-Fraktale (Swing-Hochs) Long-Positionen ermöglichen.
OsMA-Histogramm – ein MACD-Histogramm (standardmäßig schnell 12, langsam 26, Signal 9) muss für Short-Positionen weiter in den negativen Bereich und für Long-Positionen höher in den positiven Bereich abfallen. Der vorherige Histogrammwert muss ebenfalls auf der gleichen Seite von Null liegen.
Volumenfilter – das Volumen der zuvor fertigen Kerze muss einen konfigurierbaren Schwellenwert überschreiten und größer sein als das Volumen vor zwei Kerzen. Dies reproduziert die Tick-Volumen-Spitzenanforderung des ursprünglichen Experten.
Order-Timing – Trades werden durch ein Mindestintervall (standardmäßig 20 Sekunden) zwischen den Eingaben gedrosselt.
Risikomanagement – konfigurierbarer Stop-Loss, Take-Profit und optionaler Trailing-Stop werden in Punkten ausgedrückt und in Instrumentenpreise umgerechnet. Schutzanordnungen werden mit den integrierten SetStopLoss/SetTakeProfit-Helfern aktualisiert.
Parameter
Name
Beschreibung
Standard
Volume
Bestellvolumen, das für jeden Eintrag verwendet wird.
1
StopLossPoints
Stop-Loss-Distanz in Instrumentenpunkten.
1000
TakeProfitPoints
Take-Profit-Distanz in Instrumentenpunkten.
1000
TrailingStopPoints
Optionaler Trailing-Stop-Abstand in Punkten (0 deaktiviert das Trailing).
0
VolumeThreshold
Mindestvolumen der vorherigen Kerze, das erforderlich ist, um ein Signal zu aktivieren.
Der ursprüngliche Code unterstützte mit kh multiplizierte Absicherungsaufträge und eine komplexe Gewinnrecyclinglogik. Die StockSharp-Version führt eine einzelne Richtungsposition aus und schließt oder kehrt sie um, bevor ein neuer Handel eröffnet wird.
Die Handhabung von Trailing-Stops wird durch die Verwendung des standardmäßigen SetStopLoss-Helfers vereinfacht, anstatt Aufträge pro Tick manuell zu ändern.
Auf Gewinnaggregation und Wiederherstellungsblöcke im Martingal-Stil wird verzichtet. Das Exit-Management setzt auf Stop-Loss, Take-Profit oder Trailing-Stop.
Alle Indikatorberechnungen erfolgen ereignisgesteuert auf fertige Kerzen. Es gibt keine Änderung der Intrabar-Order.
Nutzungshinweise
Hängen Sie die Strategie an ein Instrument an, das Tick-Volumendaten liefert, wenn der Volumenfilter dem ursprünglichen Verhalten entsprechen soll.
Halten Sie den Zeitrahmen bei 15 Minuten, um die ursprüngliche Kalibrierung des Pivot-Fensters und des fraktalen Lookbacks zu reproduzieren.
Passen Sie die VolumeThreshold- und OsMA-Perioden an, um sie an Symbole mit unterschiedlichen Volatilitäts- oder Volumenprofilen anzupassen.
Aktivieren Sie den Trailing Stop nur, wenn ein engerer Ausstieg gewünscht ist. andernfalls belassen Sie es bei Null, um sich auf den statischen Stopp/Ziel zu verlassen.
Der Code folgt den übergeordneten StockSharp API-Richtlinien: Kerzenabonnements über SubscribeCandles, Indikatorbindung für das MACD-Histogramm und sichere Ausführung über BuyMarket/SellMarket mit automatischen Schutzanordnungen.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// FrBestExp02 Maloma Mod strategy - MACD histogram crossover with EMA trend filter.
/// Buys when MACD histogram crosses above zero while price is above EMA.
/// Sells when MACD histogram crosses below zero while price is below EMA.
/// </summary>
public class FrBestExp02MalomaModStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMacd;
private decimal _prevSignal;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FrBestExp02MalomaModStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevMacd = 0m; _prevSignal = 0m; _hasPrev = false; _currentEma = 0m; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var macd = new MovingAverageConvergenceDivergenceSignal();
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessMacd)
.Bind(ema, ProcessEma)
.Start();
}
private decimal _currentEma;
private void ProcessEma(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
_currentEma = ema;
}
private void ProcessMacd(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!value.IsFinal || value.IsEmpty)
return;
var macdVal = value as MovingAverageConvergenceDivergenceSignalValue;
if (macdVal == null)
return;
var macdLine = macdVal.Macd;
var signalLine = macdVal.Signal;
if (macdLine == null || signalLine == null)
return;
var histogram = macdLine.Value - signalLine.Value;
if (!_hasPrev)
{
_prevMacd = macdLine.Value;
_prevSignal = signalLine.Value;
_hasPrev = true;
return;
}
var prevHist = _prevMacd - _prevSignal;
var close = candle.ClosePrice;
// Histogram crosses above zero + bullish trend
if (prevHist <= 0 && histogram > 0 && close > _currentEma && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Histogram crosses below zero + bearish trend
else if (prevHist >= 0 && histogram < 0 && close < _currentEma && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevMacd = macdLine.Value;
_prevSignal = signalLine.Value;
}
}
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 MovingAverageConvergenceDivergenceSignal, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fr_best_exp02_maloma_mod_strategy(Strategy):
def __init__(self):
super(fr_best_exp02_maloma_mod_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_macd = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._current_ema = 0.0
@property
def ema_period(self):
return self._ema_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fr_best_exp02_maloma_mod_strategy, self).OnReseted()
self._prev_macd = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._current_ema = 0.0
def OnStarted2(self, time):
super(fr_best_exp02_maloma_mod_strategy, self).OnStarted2(time)
self._has_prev = False
macd = MovingAverageConvergenceDivergenceSignal()
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription \
.BindEx(macd, self.process_macd) \
.Bind(ema, self.process_ema) \
.Start()
def process_ema(self, candle, ema):
if candle.State != CandleStates.Finished:
return
self._current_ema = float(ema)
def process_macd(self, candle, value):
if candle.State != CandleStates.Finished:
return
if not value.IsFinal or value.IsEmpty:
return
if value.Macd is None or value.Signal is None:
return
macd_line = float(value.Macd)
signal_line = float(value.Signal)
histogram = macd_line - signal_line
if not self._has_prev:
self._prev_macd = macd_line
self._prev_signal = signal_line
self._has_prev = True
return
prev_hist = self._prev_macd - self._prev_signal
close = float(candle.ClosePrice)
if prev_hist <= 0 and histogram > 0 and close > self._current_ema and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_hist >= 0 and histogram < 0 and close < self._current_ema and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_macd = macd_line
self._prev_signal = signal_line
def CreateClone(self):
return fr_best_exp02_maloma_mod_strategy()