トレンドに沿ったエングルフィング戦略
この戦略は、SuperTrendフィルターと強気・弱気のエングルフィングパターンを組み合わせます。現在のトレンド方向に前のバーを飲み込むローソク足が出現したときに取引を開始します。ストップとターゲットのレベルはパターンの値幅から計算されます。
詳細
- エントリー条件: SuperTrendの方向に沿ったエングルフィングパターン。
- ロング/ショート: 両方。
- エグジット条件: ストップロスまたはテイクプロフィット。
- ストップ: あり、ローソク足の極値とATRオフセットに基づく。
- デフォルト値:
CandleType= 5分AtrPeriod= 10AtrMultiplier= 3BoringThreshold= 25EngulfingThreshold= 50StopLevel= 200
- フィルター:
- カテゴリ: パターン
- 方向: 両方
- インジケーター: SuperTrend, Candlestick
- ストップ: あり
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Engulfing with trend strategy.
/// A bullish candle whose body engulfs the body of the preceding bearish candle goes long while SuperTrend is up; a bearish
/// engulfing candle goes short while SuperTrend is down. The engulfing body must be at least EngulfingThreshold percent of its
/// range and the engulfed candle must not be a boring candle (body below BoringThreshold percent of its range). The stop is the
/// pattern extreme offset by one ATR and the target lies StopLevel percent of that risk away from the entry.
/// </summary>
public class EngulfingWithTrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _boringThreshold;
private readonly StrategyParam<decimal> _engulfingThreshold;
private readonly StrategyParam<decimal> _stopLevel;
private ICandleMessage _prevCandle;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// ATR period of SuperTrend and the stop offset.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// SuperTrend ATR multiplier.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Body percentage of the range below which a candle is boring.
/// </summary>
public decimal BoringThreshold
{
get => _boringThreshold.Value;
set => _boringThreshold.Value = value;
}
/// <summary>
/// Minimum body percentage of the range of the engulfing candle.
/// </summary>
public decimal EngulfingThreshold
{
get => _engulfingThreshold.Value;
set => _engulfingThreshold.Value = value;
}
/// <summary>
/// Target distance in percent of the risk.
/// </summary>
public decimal StopLevel
{
get => _stopLevel.Value;
set => _stopLevel.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EngulfingWithTrendStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_atrPeriod = Param(nameof(AtrPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period of SuperTrend and the stop offset", "SuperTrend");
_atrMultiplier = Param(nameof(AtrMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "SuperTrend ATR multiplier", "SuperTrend");
_boringThreshold = Param(nameof(BoringThreshold), 25m)
.SetNotNegative()
.SetDisplay("Boring Threshold %", "Body percentage of the range below which a candle is boring", "Pattern");
_engulfingThreshold = Param(nameof(EngulfingThreshold), 50m)
.SetNotNegative()
.SetDisplay("Engulfing Threshold %", "Minimum body percentage of the engulfing candle", "Pattern");
_stopLevel = Param(nameof(StopLevel), 200m)
.SetGreaterThanZero()
.SetDisplay("Stop Level %", "Target distance in percent of the risk", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
var superTrend = new SuperTrend { Length = AtrPeriod, Multiplier = AtrMultiplier };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(superTrend, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, superTrend);
DrawOwnTrades(area);
}
}
private static decimal BodyPercent(ICandleMessage candle)
{
var range = candle.HighPrice - candle.LowPrice;
return range > 0m ? Math.Abs(candle.ClosePrice - candle.OpenPrice) / range * 100m : 0m;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue superTrendValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var prev = _prevCandle;
_prevCandle = candle;
if (!superTrendValue.IsFormed || !atrValue.IsFormed || superTrendValue is not SuperTrendIndicatorValue trend)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
SellMarket(Position);
return;
}
if (Position < 0)
{
if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice)
BuyMarket(-Position);
return;
}
if (prev == null)
return;
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
var strongBody = BodyPercent(candle) >= EngulfingThreshold;
var prevNotBoring = BodyPercent(prev) >= BoringThreshold;
var bullishEngulfing = prev.ClosePrice < prev.OpenPrice && close > candle.OpenPrice
&& candle.OpenPrice <= prev.ClosePrice && close >= prev.OpenPrice;
var bearishEngulfing = prev.ClosePrice > prev.OpenPrice && close < candle.OpenPrice
&& candle.OpenPrice >= prev.ClosePrice && close <= prev.OpenPrice;
if (trend.IsUpTrend && bullishEngulfing && strongBody && prevNotBoring)
{
var stop = Math.Min(candle.LowPrice, prev.LowPrice) - atr;
var risk = close - stop;
if (risk <= 0m)
return;
_stopPrice = stop;
_takePrice = close + risk * StopLevel / 100m;
BuyMarket(Volume);
}
else if (!trend.IsUpTrend && bearishEngulfing && strongBody && prevNotBoring)
{
var stop = Math.Max(candle.HighPrice, prev.HighPrice) + atr;
var risk = stop - close;
if (risk <= 0m)
return;
_stopPrice = stop;
_takePrice = close - risk * StopLevel / 100m;
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
from StockSharp.Algo.Indicators import SuperTrend, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class engulfing_with_trend_strategy(Strategy):
"""
Engulfing with trend strategy.
A bullish candle whose body engulfs the body of the preceding bearish candle goes long while SuperTrend is up; a bearish
engulfing candle goes short while SuperTrend is down. The engulfing body must be at least EngulfingThreshold percent of its
range and the engulfed candle must not be a boring candle (body below BoringThreshold percent of its range). The stop is the
pattern extreme offset by one ATR and the target lies StopLevel percent of that risk away from the entry.
"""
def __init__(self):
super(engulfing_with_trend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_period = self.Param("AtrPeriod", 10).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period of SuperTrend and the stop offset", "SuperTrend")
self._atr_multiplier = self.Param("AtrMultiplier", 3.0).SetGreaterThanZero().SetDisplay("ATR Multiplier", "SuperTrend ATR multiplier", "SuperTrend")
self._boring_threshold = self.Param("BoringThreshold", 25.0).SetNotNegative().SetDisplay("Boring Threshold %", "Body percentage of the range below which a candle is boring", "Pattern")
self._engulfing_threshold = self.Param("EngulfingThreshold", 50.0).SetNotNegative().SetDisplay("Engulfing Threshold %", "Minimum body percentage of the engulfing candle", "Pattern")
self._stop_level = self.Param("StopLevel", 200.0).SetGreaterThanZero().SetDisplay("Stop Level %", "Target distance in percent of the risk", "Risk")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev = None
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(engulfing_with_trend_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(engulfing_with_trend_strategy, self).OnStarted2(time)
self._reset_state()
super_trend = SuperTrend()
super_trend.Length = self._atr_period.Value
super_trend.Multiplier = Decimal(float(self._atr_multiplier.Value))
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(super_trend, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, super_trend)
self.DrawOwnTrades(area)
@staticmethod
def _body_percent(o, h, l, c):
rng = h - l
return abs(c - o) / rng * 100.0 if rng > 0 else 0.0
def _process_candle(self, candle, super_trend_value, atr_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, h, l, c)
if not super_trend_value.IsFormed or not atr_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if l <= self._stop_price or h >= self._take_price:
self.SellMarket(self.Position)
return
if self.Position < 0:
if h >= self._stop_price or l <= self._take_price:
self.BuyMarket(-self.Position)
return
if prev is None:
return
po, ph, pl, pc = prev
is_up_trend = bool(super_trend_value.IsUpTrend)
atr = float(atr_value.GetValue[Decimal](None))
strong_body = self._body_percent(o, h, l, c) >= float(self._engulfing_threshold.Value)
prev_not_boring = self._body_percent(po, ph, pl, pc) >= float(self._boring_threshold.Value)
bullish_engulfing = pc < po and c > o and o <= pc and c >= po
bearish_engulfing = pc > po and c < o and o >= pc and c <= po
stop_level = float(self._stop_level.Value) / 100.0
if is_up_trend and bullish_engulfing and strong_body and prev_not_boring:
stop = min(l, pl) - atr
risk = c - stop
if risk <= 0:
return
self._stop_price = stop
self._take_price = c + risk * stop_level
self.BuyMarket(self.Volume)
elif not is_up_trend and bearish_engulfing and strong_body and prev_not_boring:
stop = max(h, ph) + atr
risk = stop - c
if risk <= 0:
return
self._stop_price = stop
self._take_price = c - risk * stop_level
self.SellMarket(self.Volume)
def CreateClone(self):
return engulfing_with_trend_strategy()