Doji トレーディング戦略
この戦略は、指数移動平均線の上方に現れるDoji ローソク足を探します。そのようなパターンが発生すると、ロングポジションに入ります。ストップロスは直近のバーの最安値に設定され、価格が十分に有利な方向へ動いた後、トレーリングストップで利益を守ります。
詳細
- エントリー条件: EMAより上で終値があるDojiローソク足。
- ロング/ショート: ロングのみ。
- エグジット条件: 最安値ストップとトレーリングストップ。
- ストップ: はい、固定とトレーリング。
- デフォルト値:
CandleType= 5分EmaLength= 60Tolerance= 0.05StopBars= 450TrailTriggerPercent= 1TrailOffsetPercent= 0.5
- フィルター:
- カテゴリ: パターン
- 方向: ロング
- インジケーター: EMA、ローソク足
- ストップ: はい
- 複雑さ: 初心者
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Doji trading strategy.
/// A doji is a candle whose body is at most Tolerance of its range. A doji closing above the EMA opens a long.
/// The stop sits at the lowest low of the last StopBars candles at entry. Once price has risen TrailTriggerPercent above the entry,
/// a trailing stop follows the highest high at TrailOffsetPercent below it.
/// </summary>
public class DojiTradingStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<decimal> _tolerance;
private readonly StrategyParam<int> _stopBars;
private readonly StrategyParam<decimal> _trailTriggerPercent;
private readonly StrategyParam<decimal> _trailOffsetPercent;
private decimal? _entryPrice;
private decimal? _stopPrice;
private decimal? _highestSinceEntry;
private bool _trailActive;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// EMA length.
/// </summary>
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
/// <summary>
/// Largest body as a fraction of the candle range that still counts as a doji.
/// </summary>
public decimal Tolerance { get => _tolerance.Value; set => _tolerance.Value = value; }
/// <summary>
/// Candles whose lowest low sets the stop.
/// </summary>
public int StopBars { get => _stopBars.Value; set => _stopBars.Value = value; }
/// <summary>
/// Profit percent that activates the trailing stop.
/// </summary>
public decimal TrailTriggerPercent { get => _trailTriggerPercent.Value; set => _trailTriggerPercent.Value = value; }
/// <summary>
/// Trailing stop distance in percent below the highest high.
/// </summary>
public decimal TrailOffsetPercent { get => _trailOffsetPercent.Value; set => _trailOffsetPercent.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public DojiTradingStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_emaLength = Param(nameof(EmaLength), 60)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA length", "Indicators");
_tolerance = Param(nameof(Tolerance), 0.05m)
.SetNotNegative()
.SetDisplay("Tolerance", "Largest body as a fraction of the range for a doji", "Pattern");
_stopBars = Param(nameof(StopBars), 450)
.SetGreaterThanZero()
.SetDisplay("Stop Bars", "Candles whose lowest low sets the stop", "Risk");
_trailTriggerPercent = Param(nameof(TrailTriggerPercent), 1m)
.SetNotNegative()
.SetDisplay("Trail Trigger %", "Profit percent that activates the trailing stop", "Risk");
_trailOffsetPercent = Param(nameof(TrailOffsetPercent), 0.5m)
.SetNotNegative()
.SetDisplay("Trail Offset %", "Trailing stop distance below the highest high", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ClearTrade();
}
private void ClearTrade()
{
_entryPrice = null;
_stopPrice = null;
_highestSinceEntry = null;
_trailActive = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ClearTrade();
var ema = new ExponentialMovingAverage { Length = EmaLength };
var lowest = new Lowest { Length = StopBars };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ema, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue, IIndicatorValue lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!emaValue.IsFormed || !lowestValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0 && _entryPrice is decimal entry)
{
_highestSinceEntry = _highestSinceEntry is decimal h ? Math.Max(h, candle.HighPrice) : candle.HighPrice;
if (!_trailActive && _highestSinceEntry >= entry * (1 + TrailTriggerPercent / 100m))
_trailActive = true;
var stop = _stopPrice;
if (_trailActive)
{
var trail = _highestSinceEntry.Value * (1 - TrailOffsetPercent / 100m);
stop = stop is decimal s ? Math.Max(s, trail) : trail;
}
if (stop is decimal exit && candle.LowPrice <= exit)
{
SellMarket(Position);
ClearTrade();
}
return;
}
var range = candle.HighPrice - candle.LowPrice;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var isDoji = range > 0m && body <= range * Tolerance;
if (Position == 0 && isDoji && candle.ClosePrice > emaValue.GetValue<decimal>())
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = lowestValue.GetValue<decimal>();
_highestSinceEntry = null;
_trailActive = false;
}
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage, Lowest
from StockSharp.Algo.Strategies import Strategy
class doji_trading_strategy(Strategy):
"""
Doji trading strategy.
A doji is a candle whose body is at most Tolerance of its range. A doji closing above the EMA opens a long.
The stop sits at the lowest low of the last StopBars candles at entry. Once price has risen TrailTriggerPercent above the entry,
a trailing stop follows the highest high at TrailOffsetPercent below it.
"""
def __init__(self):
super(doji_trading_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._ema_length = self.Param("EmaLength", 60).SetGreaterThanZero().SetDisplay("EMA Length", "EMA length", "Indicators")
self._tolerance = self.Param("Tolerance", 0.05).SetNotNegative().SetDisplay("Tolerance", "Largest body as a fraction of the range for a doji", "Pattern")
self._stop_bars = self.Param("StopBars", 450).SetGreaterThanZero().SetDisplay("Stop Bars", "Candles whose lowest low sets the stop", "Risk")
self._trail_trigger_percent = self.Param("TrailTriggerPercent", 1.0).SetNotNegative().SetDisplay("Trail Trigger %", "Profit percent that activates the trailing stop", "Risk")
self._trail_offset_percent = self.Param("TrailOffsetPercent", 0.5).SetNotNegative().SetDisplay("Trail Offset %", "Trailing stop distance below the highest high", "Risk")
self._clear_trade()
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def _clear_trade(self):
self._entry_price = None
self._stop_price = None
self._highest_since_entry = None
self._trail_active = False
def OnReseted(self):
super(doji_trading_strategy, self).OnReseted()
self._clear_trade()
def OnStarted2(self, time):
super(doji_trading_strategy, self).OnStarted2(time)
self._clear_trade()
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
lowest = Lowest()
lowest.Length = self._stop_bars.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ema, lowest, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_value, lowest_value):
if candle.State != CandleStates.Finished:
return
if not ema_value.IsFormed or not lowest_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
open_price = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self.Position > 0 and self._entry_price is not None:
entry = self._entry_price
self._highest_since_entry = high if self._highest_since_entry is None else max(self._highest_since_entry, high)
if not self._trail_active and self._highest_since_entry >= entry * (1 + float(self._trail_trigger_percent.Value) / 100.0):
self._trail_active = True
stop = self._stop_price
if self._trail_active:
trail = self._highest_since_entry * (1 - float(self._trail_offset_percent.Value) / 100.0)
stop = trail if stop is None else max(stop, trail)
if stop is not None and low <= stop:
self.SellMarket(self.Position)
self._clear_trade()
return
candle_range = high - low
body = abs(close - open_price)
is_doji = candle_range > 0 and body <= candle_range * float(self._tolerance.Value)
if self.Position == 0 and is_doji and close > float(ema_value.GetValue[Decimal](None)):
self.BuyMarket()
self._entry_price = close
self._stop_price = float(lowest_value.GetValue[Decimal](None))
self._highest_since_entry = None
self._trail_active = False
def CreateClone(self):
return doji_trading_strategy()