Diese Strategie wandelt den MQL4 „DojiTrader“ Expert Advisor in ein StockSharp C#-Beispiel um. Es sucht nach aktuellen Doji-Kerzen und handelt mit einem Ausbruch aus der Doji-Range während der wichtigsten Sitzungen in Europa und den USA.
Handelslogik
Die Strategie verarbeitet nur fertige Kerzen aus dem ausgewählten Zeitrahmen (standardmäßig 30-Minuten-Kerzen).
Der Handel ist nur zwischen 08:00 und 17:00 Uhr Plattformzeit gestattet.
Während er flach ist, blickt er auf drei abgeschlossene Kerzen zurück und merkt sich den letzten Doji (Eröffnungspreis entspricht Schlusspreis).
Wenn die Kerze unmittelbar nach dem Doji über dem Doji-Hoch schließt, ist ein langer Ausbruch möglich. Wenn es unter dem Doji-Tief schließt, ist ein kurzer Ausbruch möglich.
Sobald eine nachfolgende Kerze über dem Aktivierungspreis schließt, sendet die Strategie eine Marktorder in Ausbruchsrichtung.
Nach dem Eintritt wird der Doji-Bereich zur Austrittskontrolle beibehalten. Die Position wird geschlossen, wenn:
Die vorherige Kerze schließt wieder innerhalb der Spanne (Long: knapp unter dem Doji-Tief, Short: knapp über dem Doji-Hoch).
Die Candle-Extreme erreichen die synthetischen Stop-Loss- oder Take-Profit-Niveaus, die die ursprünglichen MQL4-Fixkomma-Ausstiege nachahmen.
Parameter
Auftragsvolumen – für Marktaufträge verwendetes Volumen.
Take Profit (Schritte) – Abstand zum Gewinnziel gemessen in Preisschritten.
Stop-Loss (Schritte) – Abstand zum Schutzstopp in Preisschritten.
Kerzentyp – Zeitrahmen der Kerzen, die zur Signalerkennung verwendet werden.
Die Stop-Loss- und Take-Profit-Berechnungen basieren auf dem Wertpapierpreisschritt und emulieren den ursprünglichen EA, der feste Pip-Abstände verwendete. Wenn innerhalb der letzten drei Kerzen kein gültiger Doji vorhanden ist, wird der Ausbruchsstatus gelöscht und die Suche beginnt erneut.
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>
/// Doji Trader breakout strategy.
/// Detects doji candles and enters on breakout of doji range.
/// Uses SMA as trend filter for direction.
/// </summary>
public class DojiTraderBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<decimal> _dojiRatio;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private bool _prevWasDoji;
private bool _hasPrev;
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public decimal DojiRatio { get => _dojiRatio.Value; set => _dojiRatio.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DojiTraderBreakoutStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 20)
.SetDisplay("SMA Period", "SMA period for trend filter", "Indicators");
_dojiRatio = Param(nameof(DojiRatio), 0.25m)
.SetDisplay("Doji Ratio", "Max body/range ratio for doji detection", "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(); _prevHigh = 0m; _prevLow = 0m; _prevWasDoji = false; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
_prevWasDoji = false;
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sma)
{
if (candle.State != CandleStates.Finished)
return;
var range = candle.HighPrice - candle.LowPrice;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var isDoji = range > 0 && body / range < DojiRatio;
if (_hasPrev && _prevWasDoji)
{
// Bullish breakout above doji high with SMA confirmation
if (candle.ClosePrice > _prevHigh && candle.ClosePrice > sma && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Bearish breakout below doji low with SMA confirmation
else if (candle.ClosePrice < _prevLow && candle.ClosePrice < sma && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevWasDoji = isDoji;
_hasPrev = true;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class doji_trader_breakout_strategy(Strategy):
def __init__(self):
super(doji_trader_breakout_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 20) \
.SetDisplay("SMA Period", "SMA period for trend filter", "Indicators")
self._doji_ratio = self.Param("DojiRatio", 0.25) \
.SetDisplay("Doji Ratio", "Max body/range ratio for doji detection", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_was_doji = False
self._has_prev = False
@property
def sma_period(self):
return self._sma_period.Value
@property
def doji_ratio(self):
return self._doji_ratio.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(doji_trader_breakout_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_was_doji = False
self._has_prev = False
def OnStarted2(self, time):
super(doji_trader_breakout_strategy, self).OnStarted2(time)
self._has_prev = False
self._prev_was_doji = False
sma = SimpleMovingAverage()
sma.Length = self.sma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self.process_candle).Start()
def process_candle(self, candle, sma):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma)
rng = float(candle.HighPrice) - float(candle.LowPrice)
body = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
is_doji = rng > 0 and body / rng < self.doji_ratio
if self._has_prev and self._prev_was_doji:
close = float(candle.ClosePrice)
if close > self._prev_high and close > sma_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < self._prev_low and close < sma_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_was_doji = is_doji
self._has_prev = True
def CreateClone(self):
return doji_trader_breakout_strategy()