Die Three Typical Candles-Strategie recreiert den MetaTrader Expert Advisor „Three Typical Candles" innerhalb der StockSharp High-Level-API. Das System beobachtet den typischen Preis der letzten drei abgeschlossenen Kerzen und handelt, wenn es eine streng monotone Sequenz erkennt. Der typische Preis ist definiert als das arithmetische Mittel aus Hoch, Tief und Schlusskurs einer Kerze. Wenn die drei zuletzt abgeschlossenen Kerzen eine steigende Sequenz typischer Preise bilden, geht die Strategie long. Umgekehrt löst eine fallende Sequenz einen Short-Einstieg aus.
Der Port folgt eng der ursprünglichen MQL5-Logik:
Signale werden nur einmal pro abgeschlossener Kerze ausgewertet, um Intrabar-Rauschen zu vermeiden.
Ein konfigurierbares Handelsfenster kann den Handel außerhalb ausgewählter Stunden deaktivieren und zwingt die Strategie bei aktivem Filter in eine flache Position.
Gegenpositionen werden geschlossen, bevor eine neue eröffnet wird, sodass die Strategie nie beide Richtungen gleichzeitig hält.
Das Order-Volumen spiegelt den Quell-EA wider, indem eine feste Lotgröße verwendet wird, während der Börsenvolumsschritt sowie die vom Wertpapier gemeldeten Mindest- und Höchstvolumenbeschränkungen eingehalten werden.
Die zwei vorherigen typischen Werte verfolgen. Sobald drei Werte verfügbar sind, auf eine streng steigende oder streng fallende Sequenz prüfen.
Long-Einstieg
Wenn Tp[-2] < Tp[-1] < Tp[0] (drei steigende typische Preise) und die aktuelle Position nicht long ist, schließt die Strategie jedes Short-Exposure und sendet eine Market-Kauforder.
Short-Einstieg
Wenn Tp[-2] > Tp[-1] > Tp[0] (drei fallende typische Preise) und die aktuelle Position nicht short ist, schließt die Strategie jedes Long-Exposure und sendet eine Market-Verkaufsorder.
Zeitkontrolle
Wenn der optionale Zeitfilter aktiviert ist, wertet die Strategie das Signal nur aus, wenn die Kerzen-Eröffnungszeit innerhalb der konfigurierten Handelssitzung liegt. Außerhalb dieses Fensters wird jede offene Position sofort liquidiert und keine neuen Trades platziert.
Positionsmanagement
Die Strategie hat keine expliziten Stop-Loss- oder Take-Profit-Niveaus. Das Risikomanagement sollte extern gehandhabt werden (z.B. über Schutzstrategien oder manuelle Überwachung).
Parameter
Name
Typ
Standard
Beschreibung
Volume
decimal
1
Festes Order-Volumen (Lots oder Kontrakte). Die Strategie rundet den Wert automatisch auf den nächsten gültigen Volumensschritt und erzwingt die Mindest-/Höchstgrenzen des Instruments.
UseTimeControl
bool
true
Aktiviert den Intraday-Handelsfensterfilter. Wenn deaktiviert, werden Signale rund um die Uhr ausgewertet.
StartHour
int
11
Inklusiver Start-Stunde (0-23) des Handelsfensters, wenn UseTimeControl wahr ist.
EndHour
int
17
Exklusive End-Stunde (0-23) des Handelsfensters, wenn UseTimeControl wahr ist. Wenn die Endstunde kleiner als die Startstunde ist, überspannt das Fenster Mitternacht.
CandleType
DataType
TimeFrame(1h)
Kerzentyp für die Analyse. Wählen Sie einen Zeitrahmen, der mit Ihrem Datenfeed kompatibel ist.
Implementierungshinweise
Die StockSharp-Basisklasse Strategy übernimmt Abonnements und Order-Routing. Signale werden in ProcessCandle ausgewertet, das abgeschlossene Kerzen über die High-Level-Binding-API empfängt.
Market-Orders werden über BuyMarket und SellMarket ausgegeben. Bei einer Umkehrung schließt die Strategie zuerst das bestehende Exposure mit einer entgegengesetzten Market-Order, bevor der neue Einstieg gesendet wird.
StartProtection() wird während der Initialisierung aufgerufen, um das optionale Anhängen von Schutzmechanismen zu ermöglichen.
Der Helper GetTradeVolume repliziert MetaTraders Lot-Normalisierung, indem das konfigurierte Volumen an Börsenbeschränkungen angepasst wird (Volumensschritt, Minimum und Maximum).
Die Strategie speichert nur zwei historische typische Preise, was ausreicht, um das Drei-Kerzen-Muster auszuwerten, ohne große Sammlungen zu pflegen.
Nutzungshinweise
Hängen Sie die Strategie an ein Instrument mit ausreichender Liquidität. Der ursprüngliche EA verwendete Intraday-Forex-Daten, aber jeder Markt mit OHLC-Kerzen kann genutzt werden.
Wählen Sie einen Kerzen-Zeitrahmen, der zu Ihrem Handelshorizont passt. Die Standard-Einstunden-Kerzen replizieren das Verhalten des Quell-EA, kürzere oder längere Intervalle können durch Parameter-Optimierung erkundet werden.
Erwägen Sie, die Strategie mit Risikokontrollen wie maximalen Drawdown-Grenzen oder portfolioweitem Stop-Loss über das StockSharp-Schutzstrategien-Framework zu kombinieren.
Backtesten Sie über mehrere Instrumente und Handelssitzungen, um zu bestätigen, dass das streng monotone Muster unter Ihren Marktbedingungen umsetzbare Signale liefert.
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 ThreeTypicalCandlesStrategy : 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 ThreeTypicalCandlesStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 15).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 60).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 three_typical_candles_strategy(Strategy):
def __init__(self):
super(three_typical_candles_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 15) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 60) \
.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(three_typical_candles_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(three_typical_candles_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 three_typical_candles_strategy()