Стратегия представляет собой конвертацию эксперта MQL4 "DojiTrader" в пример на C# для StockSharp. Она ищет свежие свечи-доджи и торгует пробой их диапазона в основные европейские и американские часы.
Логика торговли
Обрабатываются только завершённые свечи выбранного таймфрейма (по умолчанию 30 минут).
Открытие сделок разрешено только с 08:00 до 17:00 по времени площадки.
Находясь вне позиции, стратегия анализирует до трёх последних завершённых свечей и запоминает самую свежую свечу-доджи (открытие равно закрытию).
Если следующая за доджи свеча закрывается выше максимума доджи, готовится пробой вверх. Если она закрывается ниже минимума доджи, готовится пробой вниз.
Когда последующая свеча закрывается за ценой пробоя, отправляется рыночная заявка в сторону пробоя.
После входа диапазон доджи сохраняется для контроля выхода. Позиция закрывается, когда:
Предыдущая свеча закрывается обратно в диапазоне (для лонга – закрытие ниже минимума доджи, для шорта – выше максимума).
Экстремумы свечи достигают синтетических уровней стоп-лосса или тейк-профита, имитирующих фиксированные пункты оригинального советника.
Параметры
Order volume – объём рыночных заявок.
Take profit (steps) – расстояние до тейк-профита в шагах цены.
Stop loss (steps) – расстояние до стоп-лосса в шагах цены.
Candle type – таймфрейм свечей для расчётов.
Стоп-лосс и тейк-профит рассчитываются через шаг цены инструмента, повторяя логику исходного MQL4-советника с фиксированным количеством пунктов. Если в последних трёх свечах нет подходящего доджи, состояние пробоя сбрасывается и поиск начинается заново.
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()