Die JK Synchro-Strategie ist ein StockSharp-Port des MetaTrader 5-Expertenberaters "JK synchro" (MQL-ID 2415). Der ursprüngliche Roboter zählt, wie viele der zuletzt abgeschlossenen Kerzen tiefer oder höher als die Eröffnung geschlossen haben, und öffnet dann eine Position in der dominierenden Richtung. Dieser Port repliziert das Verhalten und fügt stark typisierte Parameter, integrierte Risikomanagement-Hooks und umfangreiches Logging über StockSharp hinzu.
Handelslogik
Abonnieren der durch CandleType definierten Kerzenquelle und Warten auf abgeschlossene Kerzen.
Pflegen eines gleitenden Fensters von AnalysisPeriod Kerzen. Für jede Kerze:
Erhöhen des bärischen Zählers wenn Open > Close.
Erhöhen des bullischen Zählers wenn Open < Close.
Ignorieren von Doji-Kerzen wenn Open == Close.
Sobald das Fenster gefüllt ist, die Dominanz prüfen:
Wenn bärische Kerzen die bullischen überwiegen, eine Long-Eröffnung vorbereiten.
Wenn bullische Kerzen die bärischen überwiegen, eine Short-Eröffnung vorbereiten.
Vor dem Einstieg in einen Trade überprüft die Strategie:
Die Strategie ist online und darf handeln (IsFormedAndOnlineAndAllowTrading).
Die aktuelle Stunde liegt zwischen StartHour und EndHour (einschließlich).
Die durch PauseBetweenTradesSeconds definierte Abkühlzeit seit dem letzten Einstieg verstrichen ist.
Das Hinzufügen eines weiteren Lots das Netto-Exposure innerhalb von MaxPositions * OrderVolume hält.
Wenn ein Signal erscheint, während eine entgegengesetzte Position gehalten wird, schließt die Strategie zuerst diese Position und wartet auf die nächste Kerze, bevor sie möglicherweise in die neue Richtung einsteigt.
Schützende Stop-Loss-, Take-Profit- und Trailing-Stop-Niveaus werden in Pips ausgedrückt und automatisch in Preis-Offsets basierend auf der Tick-Größe des Instruments umgerechnet.
Risikomanagement
Stop Loss / Take Profit: Optionale Niveaus in Pips. Sie werden bei jeder Positionsänderung neu berechnet und bei jeder abgeschlossenen Kerze geprüft.
Trailing Stop: Aktiviert wenn sowohl TrailingStopPips als auch TrailingStepPips positiv sind. Sobald sich der Trade um mindestens TrailingStop + TrailingStep bewegt hat, folgt der Stop dem Preis mit dem konfigurierten Schritt.
Positionslimit: Die absolute Nettoposition kann MaxPositions * OrderVolume nicht überschreiten.
Einstiegspause: Die Strategie erfasst den Zeitstempel jeder Ausführung und erzwingt eine Pause bevor ein weiterer Trade eröffnet wird.
Parameter
Parameter
Standard
Beschreibung
OrderVolume
0.1
Volumen jeder Marktorder.
MaxPositions
10
Maximale Anzahl erlaubter Lots pro Richtung.
AnalysisPeriod
18
Anzahl der abgeschlossenen Kerzen beim Zählen bullischer versus bärischer Bewegungen.
PauseBetweenTradesSeconds
540
Abkühlzeit in Sekunden nach einem Einstieg bevor ein neuer eröffnet werden kann.
StartHour
3
Startzeit (einschließlich) des Handelsfensters, Serverzeit.
EndHour
6
Endzeit (einschließlich) des Handelsfensters, Serverzeit.
StopLossPips
50
Stop-Loss-Abstand in Pips. Auf 0 setzen zum Deaktivieren.
TakeProfitPips
150
Take-Profit-Abstand in Pips. Auf 0 setzen zum Deaktivieren.
TrailingStopPips
15
Trailing-Stop-Abstand in Pips. Auf 0 setzen zum Deaktivieren.
TrailingStepPips
5
Zusätzlicher Abstand in Pips vor der Aktualisierung des Trailing Stops. Muss positiv sein wenn Trailing aktiviert ist.
CandleType
15-Minuten-Zeitrahmen
Kerzenquelle für alle Berechnungen.
Implementierungshinweise
Die High-Level-StockSharp-API wird durchgängig verwendet (SubscribeCandles, .Bind, BuyMarket, SellMarket).
Einstiegszeitstempel werden innerhalb von OnPositionChanged erfasst, um die Pausenlogik genau wie der ursprüngliche EA zu implementieren, der nach jedem Einstieg eine feste Zeit wartete.
Die Pip-Größe wird aus Security.PriceStep und Security.Decimals abgeleitet; für 3- oder 5-stellige Instrumente wird der Multiplikator automatisch angepasst.
Ausstiege werden bei abgeschlossenen Kerzen durch Vergleich von Hoch/Tief mit den berechneten Stop- und Zielniveaus verwaltet.
Trailing Stops imitieren die MetaTrader-Logik: Sie beginnen sich erst zu bewegen, wenn der Gewinn TrailingStop + TrailingStep übersteigt und kehren sich nie um.
Verwendungstipps
OrderVolume und MaxPositions an die Kontraktgröße Ihres Brokers anpassen, um das Exposure unter Kontrolle zu halten.
AnalysisPeriod entsprechend dem Kerzen-Zeitrahmen wählen. Kürzere Zeitrahmen erfordern meist größere Fenster um Rauschen zu vermeiden.
Das Handelsfenster an die aktiven Stunden des Instruments anpassen (z. B. europäische Session für EUR-basierte Paare).
Verschiedene Kombinationen von Stop, Ziel und Trailing-Einstellungen backtesten – der ursprüngliche EA lief oft entweder mit festen Zielen oder Trailing Stops abhängig von den Marktbedingungen.
Unterschiede zur MQL-Version
Der StockSharp-Port verwendet ein Netto-Exposure-Modell. Beim Wechsel der Richtung wird die bestehende Position zuerst geschlossen, während die MetaTrader-Version abgesicherte Positionen halten konnte.
Logging und Parameterverwaltung nutzen StockSharp-Funktionen, was Optimierung und UI-Integration erleichtert.
Der Trailing Stop wird bei abgeschlossenen Kerzen ausgewertet, was mit anderen StockSharp-Strategie-Ports konsistent ist und das Reagieren auf unvollständige Balken vermeidet.
Mit diesen Überlegungen kann die JK Synchro-Strategie direkt innerhalb des StockSharp-Ökosystems gehandelt, analysiert und optimiert werden.
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 JkSynchroStrategy : 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 JkSynchroStrategy()
{
_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 jk_synchro_strategy(Strategy):
def __init__(self):
super(jk_synchro_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(jk_synchro_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(jk_synchro_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 jk_synchro_strategy()