強化版ドージ・キャンドル戦略
この戦略は、シンプルな確認ルールと固定リスクリワード管理でドージローソク足を取引します。Dojiが出現し、そのローソク足または前のローソク足が小さいヒゲで始値より先にクローズして方向を確認したときにエントリーします。保護注文にはピップ単位のストップロスとリスクリワード比で決まるテイクプロフィットを使用します。
詳細
- エントリー条件: Dojiローソク足(実体 <= 値幅の30%)。下ヒゲが<=1%の強気またはひとつ前のローソク足が強気なら、ロング。上ヒゲが<=1%の弱気またはひとつ前のローソク足が弱気なら、ショート。
- ロング/ショート: 両方。
- エグジット条件: テイクプロフィットもしくはストップロス、またはポジションをクローズする新しいDojiの出現。
- ストップ: あり。
- デフォルト値:
RiskRewardRatio= 2.0mStopLossPips= 5SmaPeriod= 20CandleType= TimeSpan.FromMinutes(1)
- フィルター:
- カテゴリ: Candlestick
- 方向: 両方
- インジケーター: 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()