Esta estratégia converte o consultor especialista MQL4 "DojiTrader" em uma amostra StockSharp C#. Ele procura velas doji recentes e negocia um rompimento da faixa doji durante as principais sessões da Europa e dos EUA.
Lógica de negociação
A estratégia processa apenas velas finalizadas do período selecionado (velas de 30 minutos por padrão).
A negociação é permitida apenas entre 8h00 e 17h00 horário da plataforma.
Embora estável, ele analisa até três velas concluídas e lembra o doji mais recente (o preço de abertura é igual ao preço de fechamento).
Quando a vela imediatamente após o doji fecha acima da máxima do doji, um longo rompimento é armado. Se fechar abaixo da mínima doji, um pequeno rompimento será armado.
Assim que uma vela subsequente fecha além do preço de ativação, a estratégia envia uma ordem de mercado na direção do rompimento.
Após a entrada, o intervalo doji é mantido para controle de saída. A posição é fechada quando:
A vela anterior fecha dentro do intervalo (longa: fecha abaixo da mínima do doji, curta: fecha acima da máxima do doji).
Os extremos da vela atingem os níveis de stop loss sintético ou takeprofit que imitam as saídas de ponto fixo MQL4 originais.
Parâmetros
Volume de ordens – volume utilizado para ordens de mercado.
Take Profit (steps) – distância até a meta de lucro medida em etapas de preço.
Stop loss (etapas) – distância até o stop de proteção em etapas de preço.
Tipo de vela – período de tempo das velas usadas para detecção de sinal.
Os cálculos de stop-loss e take-profit baseiam-se na etapa do preço do título, emulando o EA original que usava distâncias fixas de pip. Quando nenhum doji válido estiver presente nas últimas três velas, o estado de rompimento é apagado e a busca é reiniciada.
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()