Die Rapid Doji-Strategie repliziert die Logik des ursprünglichen Expertenberaters „Rapid Doji EA“. Es scannt fertige Kerzen eines konfigurierbaren Zeitrahmens (standardmäßig täglich) und platziert Stop-Entry-Orders über und unter jeder Doji-Kerze. Schutzstopps werden mithilfe eines Average True Range (ATR)-Multiplikators positioniert, während ein zusätzlicher Trailing-Stop die Risikodistanz in Rohpunkten festhält, nachdem eine Position profitabel wird.
Handelslogik
Datenabonnement – die Strategie hört auf fertige Kerzen des ausgewählten Zeitrahmens und verwaltet einen ATR-Indikator mit einem konfigurierbaren Zeitraum.
Doji-Erkennung – eine Kerze wird als Doji behandelt, wenn die absolute Körpergröße höchstens 3 % des gesamten Kerzenbereichs beträgt. Es werden nur fertige Kerzen ausgewertet.
Auftragserteilung – wenn ein gültiges Doji gefunden wird:
Beim Doji-Hoch wird eine Kauf-Stopp-Order platziert.
Beim Doji-Tief wird eine Verkaufsstopp-Order platziert.
Jeder Eintrag speichert einen schützenden Stop-Preis, der dem entgegengesetzten extremen Minus/Plus-Multiplikator ATR entspricht.
Risikomanagement – sobald eine Position eröffnet wird, wird die verbleibende ausstehende Order storniert, der gespeicherte Stop wird als Schutzstopp registriert und die Trailing-Logik übernimmt die Kontrolle.
Trailing Stop – bei jeder neuen Kerze wird das Stop-Level verschoben, um einen festen Abstand (in durch die Preisstufe des Instruments umgerechneten Punkten) zum letzten Schlusskurs einzuhalten, jedoch nur, wenn die Position bereits profitabel ist.
Die Strategie verwendet niemals Take-Profit-Ziele; Ausstiege erfolgen durch den Schutzstopp oder manuelle Eingriffe.
Parameter
Parameter
Beschreibung
CandleType
Kerzendatentyp, der zur Mustererkennung verwendet wird (standardmäßig täglicher Zeitrahmen).
AtrPeriod
Lookback-Länge des ATR-Indikators.
AtrMultiplier
Auf den ATR-Wert angewendeter Multiplikator zur Stop-Loss-Berechnung.
TrailingDistancePoints
Feste Distanz in Rohpunkten, die beim Verfolgen des Stopps verwendet wird.
Alle Parameter unterstützen die Optimierung innerhalb der StockSharp-Umgebung.
Implementierungshinweise
Der Code basiert auf dem High-Level-Kerzenabonnement API (SubscribeCandles) in Kombination mit der Indikatorbindung (Bind), um eine manuelle Verlaufsverarbeitung zu vermeiden.
Aufträge werden bis Security.ShrinkPrice normalisiert, um die Tick-Größe der Börse zu berücksichtigen.
Schutzstopps werden explizit verwaltet, um das Verhalten des ursprünglichen MetaTrader-Expertenberaters nachzuahmen.
Das Projekt verzichtet bewusst auf eine Python-Implementierung gemäß den Aufgabenanforderungen.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Rapid Doji strategy: detects doji candles and trades the breakout direction.
/// Buys on next candle if it closes above doji high, sells if below doji low.
/// Uses ATR for volatility confirmation.
/// </summary>
public class RapidDojiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _dojiThreshold;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevHigh;
private decimal _prevLow;
private bool _prevWasDoji;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal DojiThreshold { get => _dojiThreshold.Value; set => _dojiThreshold.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public RapidDojiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for volatility filter", "Indicators");
_dojiThreshold = Param(nameof(DojiThreshold), 0.15m)
.SetDisplay("Doji Threshold", "Max body/range ratio for doji detection", "Pattern");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between breakouts", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0m;
_prevLow = 0m;
_prevWasDoji = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevWasDoji = false;
_candlesSinceTrade = SignalCooldownCandles;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_prevWasDoji && atr > 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
var close = candle.ClosePrice;
if (close > _prevHigh + atr * 0.2m && Position <= 0)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (close < _prevLow - atr * 0.2m && Position >= 0)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
var range = candle.HighPrice - candle.LowPrice;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
_prevWasDoji = range > 0 && body <= DojiThreshold * range;
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class rapid_doji_strategy(Strategy):
def __init__(self):
super(rapid_doji_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for volatility filter", "Indicators")
self._doji_threshold = self.Param("DojiThreshold", 0.15) \
.SetDisplay("Doji Threshold", "Max body/range ratio for doji detection", "Pattern")
self._signal_cooldown = self.Param("SignalCooldownCandles", 6) \
.SetDisplay("Signal Cooldown", "Bars to wait between breakouts", "Trading")
self._atr = None
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_was_doji = False
self._candles_since_trade = 0
@property
def atr_period(self):
return self._atr_period.Value
@property
def doji_threshold(self):
return self._doji_threshold.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(rapid_doji_strategy, self).OnReseted()
self._atr = None
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_was_doji = False
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(rapid_doji_strategy, self).OnStarted2(time)
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self._prev_was_doji = False
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(15)))
subscription.Bind(self._atr, self._process_candle)
subscription.Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
if not self._atr.IsFormed:
return
atr_val = float(atr_value)
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
if self._prev_was_doji and atr_val > 0 and self._candles_since_trade >= self.signal_cooldown:
close = float(candle.ClosePrice)
if close > self._prev_high + atr_val * 0.2 and self.Position <= 0:
self.BuyMarket()
self._candles_since_trade = 0
elif close < self._prev_low - atr_val * 0.2 and self.Position >= 0:
self.SellMarket()
self._candles_since_trade = 0
high = float(candle.HighPrice)
low = float(candle.LowPrice)
range_size = high - low
body = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
self._prev_was_doji = range_size > 0 and body <= self.doji_threshold * range_size
self._prev_high = high
self._prev_low = low
def CreateClone(self):
return rapid_doji_strategy()