Die Strategie repliziert den MetaTrader Expert „iMA iStochastic Custom" innerhalb des StockSharp-Frameworks. Sie kombiniert eine gleitende Durchschnittshülle mit einem stochastischen Oszillatorfilter. Der Handel findet auf abgeschlossenen Kerzen des ausgewählten Zeitrahmens (CandleType) statt. Alle Kommentare verwenden die gleiche Nomenklatur wie der ursprüngliche Advisor.
Schlüsselkomponenten:
Gleitender Durchschnittshülle – der Basis-MA wird um LevelUpPips und LevelDownPips (in Pips) verschoben, um Widerstands- und Unterstützungsbänder zu bilden. Die Mittelungsmethode entspricht MetaTrader-Optionen: Simple, Exponential, Smoothed (SMMA) und Linear Weighted (LWMA).
Stochastischer Oszillator – %K, %D und Glättungslängen folgen den Originalparametern. Zwei Schwellenwerte (StochasticLevel1 und StochasticLevel2) validieren überkaufte/überverkaufte Bedingungen.
Geldverwaltung – der ursprüngliche lot/risk-Selektor wird über den Parameter ManagementMode erhalten. Im FixedLot-Modus entspricht die Ordergröße VolumeValue. Im RiskPercent-Modus riskiert die Strategie den konfigurierten Prozentsatz des Portfolio-Eigenkapitals gegen den Stop-Loss-Abstand, reproduzierend das Verhalten von CMoneyFixedMargin.
Schutzmaßnahmen – Stop-Loss-, Take-Profit- und Trailing-Abstände werden in Pips eingegeben. Trailing wird auf abgeschlossenen Kerzen aktualisiert, was die MQL-Logik widerspiegelt und gleichzeitig mit dem Ereignismodell von StockSharp kompatibel bleibt.
Handelslogik
Long- und Short-Signale sind symmetrisch:
Kauf, wenn der Kerzen-Schlusskurs über der oberen Hülle (ma + LevelUpPips) liegt und eine der stochastischen Linien über StochasticLevel1 liegt.
Verkauf, wenn der Kerzen-Schlusskurs unter der unteren Hülle (ma + LevelDownPips) liegt und eine der stochastischen Linien unter StochasticLevel2 liegt.
Das Setzen von ReverseSignals = true tauscht die Einstiegsrichtung.
Nur eine Nettoposition ist gleichzeitig aktiv. Wenn das Signal wechselt, sendet die Strategie eine Order, die groß genug ist, um das aktuelle Exposure zu neutralisieren und eine neue Position in entgegengesetzter Richtung zu eröffnen.
Risikokontrolle und Ausstiege
Stop-Loss / Take-Profit – Abstände in Pips werden über den PriceStep des Instruments konvertiert. Sie werden auf jeder fertigen Kerze anhand von Hoch/Tief geprüft.
Trailing-Stop – beginnt, nachdem sich der Preis um TrailingStopPips zugunsten der Position bewegt hat. Es erfordert eine zusätzliche Verbesserung von TrailingStepPips vor jeder Anpassung, genau wie die MQL-Trailing-Routine.
Geldverwaltung – im Risikomodus ist die Positionsgröße equity * VolumeValue / 100 / perUnitRisk, wobei perUnitRisk der monetäre Verlust pro Lot bis zum Stop-Loss ist.
Parameter
Parameter
Beschreibung
CandleType
Zeitrahmen für Berechnungen.
StopLossPips, TakeProfitPips
Schutzabstände in Pips.
TrailingStopPips, TrailingStepPips
Trailing-Aktivierung und -Schritt (Pips). Null setzen zum Deaktivieren.
ManagementMode, VolumeValue
Feste Lot- oder Risikoprozent-Dimensionierung.
MaPeriod, MaShift, MaMethod
Gleitdurchschnittslänge, Bar-Shift und Methode (SMA/EMA/SMMA/LWMA).
LevelUpPips, LevelDownPips
Obere/untere Hüllversätze in Pips. Negative Werte sind für das untere Band erlaubt.
Kerzen, Indikatoren und Orders sind über die High-Level-API (SubscribeCandles().BindEx(...)) verbunden.
Die Pip-Größe passt sich automatisch an 3/5-stellige Forex-Symbole an, indem der PriceStep bei Bedarf multipliziert wird.
Trailing-Logik läuft auf abgeschlossenen Kerzen. Wenn Intrabar-Trailing erforderlich ist, die Logik in Tick-Daten einhängen.
Kein Python-Port bereitgestellt; der PY-Ordner ist wie gewünscht absichtlich nicht vorhanden.
Unterschiede zur MetaTrader-Version
Die Risikodimensionierung ist explizit und basiert auf StockSharp Portfolio-Metriken anstelle der CMoneyFixedMargin-Hilfsklasse. Die resultierenden Lots stimmen mit dem Originalverhalten überein, wenn Stop-Loss aktiviert ist; bei null Stop-Loss bleibt die Positionsgröße null, was die MQL-Schutzmaßnahme widerspiegelt.
Schutzprüfungen (Stop-Loss, Take-Profit, Trailing) werden auf abgeschlossenen Kerzen ausgewertet, da StockSharp-Strategien ereignisgesteuert sind. Dies hält die Logik deterministisch und entspricht Backtesting-Anforderungen.
Das Logging wird auf StockSharp-Standardausgabe vereinfacht; das verbose InpPrintLog-Flag wird nicht übernommen.
Verwenden Sie diese Strategie als direkten Drop-in-Ersatz beim Migrieren von MetaTrader zu StockSharp Designer oder Runner. Parameter an das Zielinstrument und den Zeitrahmen anpassen.
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 IMAIStochasticCustomStrategy : 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 IMAIStochasticCustomStrategy()
{
_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 imai_stochastic_custom_strategy(Strategy):
def __init__(self):
super(imai_stochastic_custom_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(imai_stochastic_custom_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(imai_stochastic_custom_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 imai_stochastic_custom_strategy()