Стратегия Enhanced Doji Candle
Стратегия торгует дожи со простыми правилами подтверждения и фиксированным соотношением риск-прибыль. Вход выполняется при появлении дожи, когда текущая или предыдущая свеча подтверждает направление, закрываясь за уровнем открытия при небольших хвостах. Защитные ордера используют стоп-лосс в пунктах и тейк-профит на основе коэффициента риск-прибыль.
Детали
- Условия входа: Дожи (тело <= 30% диапазона). Если свеча бычья с нижней тенью <=1% или предыдущая свеча была бычьей — вход в лонг. Если свеча медвежья с верхней тенью <=1% или предыдущая медвежья — вход в шорт.
- Длинные/короткие позиции: Обе.
- Условия выхода: Тейк-профит или стоп-лосс, либо закрытие позиции при появлении нового дожи.
- Стопы: Да.
- Значения по умолчанию:
RiskRewardRatio= 2.0mStopLossPips= 5SmaPeriod= 20CandleType= TimeSpan.FromMinutes(1)
- Фильтры:
- Категория: Candlestick
- Направление: Both
- Индикаторы: SMA
- Стопы: Да
- Сложность: Начальный
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Низкий
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Enhanced doji candle strategy.
/// A doji has a body of at most 30% of its range. When flat, a doji goes long if it is bullish with a lower wick of at most 1% of
/// its range or the previous candle was bullish, and short if it is bearish with an upper wick of at most 1% or the previous
/// candle was bearish. While a position is open any new doji closes it. The stop lies StopLossPips price steps from the entry and
/// the take profit RiskRewardRatio times that distance. The SMA is drawn for reference.
/// </summary>
public class EnhancedDojiCandleStrategy : Strategy
{
private const decimal _dojiBodyPercent = 30m;
private const decimal _wickPercent = 1m;
private readonly StrategyParam<decimal> _riskRewardRatio;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<DataType> _candleType;
private ICandleMessage _prevCandle;
/// <summary>
/// Take profit distance in multiples of the stop distance.
/// </summary>
public decimal RiskRewardRatio
{
get => _riskRewardRatio.Value;
set => _riskRewardRatio.Value = value;
}
/// <summary>
/// Stop loss in price steps.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// SMA period.
/// </summary>
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EnhancedDojiCandleStrategy()
{
_riskRewardRatio = Param(nameof(RiskRewardRatio), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Risk Reward", "Take profit in multiples of the stop distance", "Risk");
_stopLossPips = Param(nameof(StopLossPips), 5)
.SetNotNegative()
.SetDisplay("Stop Loss Pips", "Stop loss in price steps", "Risk");
_smaPeriod = Param(nameof(SmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var stopDistance = StopLossPips * (Security?.PriceStep ?? 1m);
StartProtection(
stopDistance > 0m ? new Unit(stopDistance * RiskRewardRatio, UnitTypes.Absolute) : new Unit(),
stopDistance > 0m ? new Unit(stopDistance, UnitTypes.Absolute) : new Unit(),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal sma)
{
if (candle.State != CandleStates.Finished)
return;
var prev = _prevCandle;
_prevCandle = candle;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var range = candle.HighPrice - candle.LowPrice;
if (range <= 0m)
return;
var open = candle.OpenPrice;
var close = candle.ClosePrice;
var isDoji = Math.Abs(close - open) / range * 100m <= _dojiBodyPercent;
if (!isDoji)
return;
if (Position > 0)
{
SellMarket(Position);
return;
}
if (Position < 0)
{
BuyMarket(-Position);
return;
}
var lowerWick = (Math.Min(open, close) - candle.LowPrice) / range * 100m;
var upperWick = (candle.HighPrice - Math.Max(open, close)) / range * 100m;
var prevBullish = prev != null && prev.ClosePrice > prev.OpenPrice;
var prevBearish = prev != null && prev.ClosePrice < prev.OpenPrice;
if ((close > open && lowerWick <= _wickPercent) || prevBullish)
BuyMarket(Volume);
else if ((close < open && upperWick <= _wickPercent) || prevBearish)
SellMarket(Volume);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
DOJI_BODY_PERCENT = 30.0
WICK_PERCENT = 1.0
class enhanced_doji_candle_strategy(Strategy):
"""
Enhanced doji candle strategy.
A doji has a body of at most 30% of its range. When flat, a doji goes long if it is bullish with a lower wick of at most 1% of
its range or the previous candle was bullish, and short if it is bearish with an upper wick of at most 1% or the previous
candle was bearish. While a position is open any new doji closes it. The stop lies StopLossPips price steps from the entry and
the take profit RiskRewardRatio times that distance. The SMA is drawn for reference.
"""
def __init__(self):
super(enhanced_doji_candle_strategy, self).__init__()
self._risk_reward_ratio = self.Param("RiskRewardRatio", 2.0).SetGreaterThanZero().SetDisplay("Risk Reward", "Take profit in multiples of the stop distance", "Risk")
self._stop_loss_pips = self.Param("StopLossPips", 5).SetNotNegative().SetDisplay("Stop Loss Pips", "Stop loss in price steps", "Risk")
self._sma_period = self.Param("SmaPeriod", 20).SetGreaterThanZero().SetDisplay("SMA Period", "SMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(enhanced_doji_candle_strategy, self).OnReseted()
self._prev = None
def OnStarted2(self, time):
super(enhanced_doji_candle_strategy, self).OnStarted2(time)
self._prev = None
sma = SimpleMovingAverage()
sma.Length = self._sma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
stop_distance = self._stop_loss_pips.Value * step
rr = float(self._risk_reward_ratio.Value)
self.StartProtection(
Unit(Decimal(stop_distance * rr), UnitTypes.Absolute) if stop_distance > 0 else Unit(),
Unit(Decimal(stop_distance), UnitTypes.Absolute) if stop_distance > 0 else Unit(),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
prev = self._prev
self._prev = (o, c)
if not self.IsFormedAndOnlineAndAllowTrading():
return
rng = h - l
if rng <= 0:
return
is_doji = abs(c - o) / rng * 100.0 <= DOJI_BODY_PERCENT
if not is_doji:
return
if self.Position > 0:
self.SellMarket(self.Position)
return
if self.Position < 0:
self.BuyMarket(-self.Position)
return
lower_wick = (min(o, c) - l) / rng * 100.0
upper_wick = (h - max(o, c)) / rng * 100.0
prev_bullish = prev is not None and prev[1] > prev[0]
prev_bearish = prev is not None and prev[1] < prev[0]
if (c > o and lower_wick <= WICK_PERCENT) or prev_bullish:
self.BuyMarket(self.Volume)
elif (c < o and upper_wick <= WICK_PERCENT) or prev_bearish:
self.SellMarket(self.Volume)
def CreateClone(self):
return enhanced_doji_candle_strategy()