Enhanced Doji Candle 策略
该策略基于十字星形态配合简单的确认规则和固定的风险收益比。出现十字星时,如果本根或上一根K线收盘突破开盘价且影线较短,则顺势开仓。止损以点数设定,止盈按风险收益系数计算。
详情
- 入场条件: 十字星(实体 <= 区间的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()