趋势吞没策略
该策略将 SuperTrend 方向过滤与看涨和看跌吞没形态结合。当新的蜡烛在当前趋势方向上吞没前一根蜡烛时开仓。止损和目标根据形态区间计算。
细节
- 入场条件:吞没形态与 SuperTrend 方向一致。
- 多/空:双向。
- 退出条件:止损或止盈。
- 止损:是,基于蜡烛极值和 ATR 偏移。
- 默认值:
CandleType= 5 分钟AtrPeriod= 10AtrMultiplier= 3BoringThreshold= 25EngulfingThreshold= 50StopLevel= 200
- 过滤条件:
- 类别: 形态
- 方向: 双向
- 指标: SuperTrend, K线形态
- 止损: 有
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
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()