Udy Ivan Madumere Strategie
Überblick
Der Fachberater von Udy Ivan Madumere eröffnet einmal täglich eine einzelne Marktposition, wenn eine bestimmte stündliche Kerze erscheint. Der Port StockSharp hält dieses Verhalten aufrecht, indem er die konfigurierte Kerzenserie beobachtet, historische Eröffnungspreise vergleicht und unmittelbar nach dem Schließen des Zielbalkens reagiert. Alle Ausführungsentscheidungen, die Positionsverwaltung und die Volumenverwaltung werden reproduziert, sodass sich die Strategie in der StockSharp-Umgebung wie das Original von MetaTrader 4 verhält.
Hauptmerkmale:
- Bewertet eine fertige Kerze pro Tag bei
TradeHour und übermittelt nie mehr als eine gleichzeitige Position.
- Misst die Differenz zwischen den Eröffnungspreisen
Open[FirstLookback] und Open[SecondLookback], um zu entscheiden, ob Short- oder Long-Positionen eingegangen werden sollen.
- Spiegelt die Saldoleiter von MetaTrader wider, um die Basislosgröße automatisch anzupassen, wenn
UseAutoVolume aktiviert ist.
- Wendet asymmetrische Stop-Loss- und Take-Profit-Abstände (getrennt für Long und Short) sowie einen Trailing Stop an, der nur Short-Positionen betrifft.
- Erzwingt die Schließung jedes Handels nach einer konfigurierbaren Anzahl von Stunden, auch wenn die Schutzniveaus nicht erreicht wurden.
Handelsablauf
- Abonnieren Sie den ausgewählten Kerzentyp (
CandleType) und warten Sie, bis die Balken vollständig fertiggestellt sind, um vorzeitige Signale zu verhindern.
- Verfolgen Sie den Verlauf der offenen Preise, damit die Unterschiede
Open[FirstLookback] - Open[SecondLookback] (kurze Einrichtung) und Open[SecondLookback] - Open[FirstLookback] (lange Einrichtung) genau wie in MetaTrader ausgewertet werden können.
- Wenn die letzte Kerze um
TradeHour öffnet:
- Wenn die rückläufige Differenz größer als
ShortDeltaPoints * PriceStep ist, senden Sie einen Marktverkaufsauftrag.
- Andernfalls, wenn die bullische Differenz
LongDeltaPoints * PriceStep überschreitet, senden Sie eine Marktkauforder.
- Es ist nur eine Bestellung pro Tag zulässig. Das Flag
canTrade wird nach Ablauf der konfigurierten Stunde zurückgesetzt, um einen weiteren Versuch in der nächsten Sitzung zu ermöglichen.
- Bei der Auftragseingabe berechnet die Strategie das Basislos neu:
UseAutoVolume = true aktiviert die Legacy-Leiter, die die Losgröße erhöht, wenn der Kontostand vordefinierte Schwellenwerte überschreitet.
- Wenn der aktuelle Saldo unter dem Snapshot des vorherigen Handels liegt, wird das Ergebnis mit
BigLotMultiplier multipliziert, was dem „Big Lot“-Erholungsverhalten des EA entspricht.
- Während die Position offen ist, wird bei jeder abgeschlossenen Kerze die folgende Exit-Logik ausgeführt:
- Harter Take-Profit und Stop-Loss werden anhand des erfassten Einstiegspreises bewertet.
- Auch Short-Trades folgen dem Stop, sobald sich der beste Preis um mindestens
TrailingStopPoints verbessert hat.
- Die Position wird zwangsweise geschlossen, sobald sie
MaxHoldingHours aktiv war.
Parameter
| Name |
Typ |
Standard |
Beschreibung |
CandleType |
DataType |
H1 |
Von der Strategie verarbeitete Kerzenserie. |
TradeHour |
int |
18 |
Stunde des Tages (0-23), zu der das Tagessignal ausgewertet wird. |
FirstLookback |
int |
6 |
Anzahl der abgeschlossenen Kerzen, referenziert als Open[FirstLookback]. |
SecondLookback |
int |
2 |
Anzahl der abgeschlossenen Kerzen, referenziert als Open[SecondLookback]. |
LongDeltaPoints |
decimal |
6 |
Minimale bullische Eröffnungspreisdifferenz (in MetaTrader Punkten), die für den Einstieg in eine Long-Position erforderlich ist. |
ShortDeltaPoints |
decimal |
21 |
Minimale bärische Eröffnungspreisdifferenz (in MetaTrader Punkten), die für den Einstieg in einen Short erforderlich ist. |
TakeProfitLongPoints |
decimal |
39 |
Take-Profit-Distanz, ausgedrückt in Punkten, für Long-Positionen. |
StopLossLongPoints |
decimal |
147 |
Stop-Loss-Distanz in Punkten für Long-Positionen. |
TakeProfitShortPoints |
decimal |
200 |
Take-Profit-Distanz in Punkten für Short-Positionen. |
StopLossShortPoints |
decimal |
267 |
Stop-Loss-Distanz in Punkten für Short-Positionen. |
TrailingStopPoints |
decimal |
30 |
Trailing-Stop-Distanz (Punkte), gilt nur für Short-Positionen. |
BaseVolume |
decimal |
0.01 |
Anfängliche Losgröße vor Anpassungen des Geldmanagements. |
UseAutoVolume |
bool |
true |
Aktivieren Sie die MetaTrader-Guthabenleiter, die BaseVolume überschreibt. |
BigLotMultiplier |
decimal |
1 |
Ein zusätzlicher Multiplikator wird angewendet, wenn der Kontostand seit dem vorherigen Handel gesunken ist. |
MaxHoldingHours |
int |
504 |
Maximale Haltezeit in Stunden. Null deaktiviert den Timer. |
Hinweise zur Implementierung
- Preisschwellenwerte werden mithilfe der
PriceStep des Instruments von MetaTrader „Punkten“ in tatsächliche Preisabstände umgewandelt.
- Der offene Preispuffer wird auf
max(FirstLookback, SecondLookback) + 1 Einträge reduziert, wodurch unnötige Zuteilungen vermieden werden und gleichzeitig die erforderliche Historie erhalten bleibt.
- Der Trailing-Stop für Short-Trades speichert den besten erreichten Tiefststand und aktualisiert das Schutzniveau nur, wenn der neue Kandidat näher am aktuellen Preis liegt.
- Kontostand-Snapshots basieren auf
Portfolio.CurrentValue (Rückfall auf BeginValue), sodass sich Demo-, Live- und Backtest-Umgebungen konsistent verhalten.
- Jeder Kommentar im Code ist wie gewünscht in Englisch verfasst, sodass die Logik leicht überprüft oder erweitert werden kann.
Anwendungstipps
- Ordnen Sie
CandleType dem Zeitrahmen zu, der vom historischen EA verwendet wird (die ursprüngliche Vorlage erwartet einstündige Kerzen).
- Wenn Sie auf Symbolen laufen, die Mikrolots verwenden, passen Sie
BaseVolume und die Werte der Auto-Lot-Leiter an die Vertragsspezifikationen des Veranstaltungsortes an.
- Kombinieren Sie die Strategie mit StockSharp-Diagrammen über die integrierten Helfer (
DrawCandles, DrawOwnTrades), um sicherzustellen, dass Bestellungen nur einmal pro Tag zur konfigurierten Stunde erscheinen.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Udy Ivan Madumere: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class UdyIvanMadumereStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public UdyIvanMadumereStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 20)
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 50)
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; _cooldown = 10; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; _cooldown = 10; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 55) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 45) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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, RelativeStrengthIndex, AverageTrueRange
class udy_ivan_madumere_strategy(Strategy):
def __init__(self):
super(udy_ivan_madumere_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 20) \
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 50) \
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(udy_ivan_madumere_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fv < sv and self._prev_fast >= self._prev_slow) or close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 10
elif self.Position < 0:
if (fv > sv and self._prev_fast <= self._prev_slow) or close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 10
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 55:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv < 45:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(udy_ivan_madumere_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def CreateClone(self):
return udy_ivan_madumere_strategy()