Esta estrategia convierte el asesor experto MQL4 "DojiTrader" en una muestra de StockSharp C#. Busca velas doji recientes y negocia una ruptura del rango doji durante las principales sesiones europeas y estadounidenses.
Lógica comercial
La estrategia procesa solo velas terminadas del período de tiempo seleccionado (velas de 30 minutos por defecto).
Sólo se permite operar entre las 08:00 y las 17:00, hora de la plataforma.
Aunque está plano, mira hacia atrás hasta tres velas completadas y recuerda el doji más reciente (el precio de apertura es igual al precio de cierre).
Cuando la vela que sigue inmediatamente al doji se cierra por encima del máximo del doji, se arma una ruptura larga. Si cierra por debajo del mínimo doji, se arma una breve ruptura.
Tan pronto como una vela posterior cierra más allá del precio de armado, la estrategia envía una orden de mercado en la dirección de ruptura.
Después de la entrada, el rango del doji se conserva para el control de salida. La posición se cierra cuando:
La vela anterior vuelve a cerrar dentro del rango (larga: cierra por debajo del mínimo doji, corta: cierra por encima del máximo doji).
Los extremos de las velas alcanzan niveles de stop loss sintéticos o toma de ganancias que imitan las salidas de punto fijo originales MQL4.
Parámetros
Volumen de orden – volumen utilizado para órdenes de mercado.
Take de ganancias (pasos) – distancia al objetivo de ganancias medida en pasos de precio.
Stop loss (pasos) – distancia hasta el stop de protección en pasos de precio.
Tipo de vela: período de tiempo de las velas utilizadas para la detección de señales.
Los cálculos de stop-loss y take-profit se basan en el paso del precio del valor, emulando el EA original que utilizaba distancias de pips fijas. Cuando no hay ningún doji válido dentro de las últimas tres velas, el estado de ruptura se borra y se reinicia la búsqueda.
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()