流動性エングルフメント戦略
この戦略は、価格が最近の流動性の高値または安値に触れた後に発生する強気・弱気のエングルフィングパターンを検出します。取引はモードでフィルタリングされ、固定のストップロスとpips単位のオプションのテイクプロフィットを含みます。
詳細
- エントリー条件:
- ロング: 下方流動性タッチ後の強気エングルフィング。
- ショート: 上方流動性タッチ後の弱気エングルフィング。
- エグジット条件: 反対シグナル、ストップロスまたはテイクプロフィット。
- ロング/ショート: 設定可能(デフォルトは両方)。
- インジケーター: Highest, Lowest。
- ストップ:
StopLossPipsおよびオプションのTakeProfitPips。 - デフォルト値:
CandleType= 1分UpperLookback= 10LowerLookback= 10StopLossPips= 10TakeProfitPips= 20Mode= Both
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>
/// Liquidity Engulfment strategy.
/// Upper liquidity is the highest high of the previous UpperLookback candles and lower liquidity the lowest low of the previous
/// LowerLookback candles. Once a candle touches lower liquidity, the next bullish engulfing candle opens a long; once a candle touches
/// upper liquidity, the next bearish engulfing candle opens a short. Mode restricts the allowed side: a signal of the disabled side only
/// closes the open position. Stop loss and take profit are set in pips (price steps).
/// </summary>
public class LiquidityEngulfmentStrategy : Strategy
{
/// <summary>
/// Allowed trade directions.
/// </summary>
public enum TradeModes
{
/// <summary>
/// Long and short trades.
/// </summary>
Both,
/// <summary>
/// Long trades only.
/// </summary>
Long,
/// <summary>
/// Short trades only.
/// </summary>
Short,
}
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _upperLookback;
private readonly StrategyParam<int> _lowerLookback;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<TradeModes> _mode;
private decimal? _prevHighest;
private decimal? _prevLowest;
private ICandleMessage _prevCandle;
private bool _upperTouched;
private bool _lowerTouched;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Candles that define upper liquidity.
/// </summary>
public int UpperLookback
{
get => _upperLookback.Value;
set => _upperLookback.Value = value;
}
/// <summary>
/// Candles that define lower liquidity.
/// </summary>
public int LowerLookback
{
get => _lowerLookback.Value;
set => _lowerLookback.Value = value;
}
/// <summary>
/// Stop loss in pips.
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit in pips, 0 disables it.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Allowed trade directions.
/// </summary>
public TradeModes Mode
{
get => _mode.Value;
set => _mode.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public LiquidityEngulfmentStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_upperLookback = Param(nameof(UpperLookback), 10)
.SetGreaterThanZero()
.SetDisplay("Upper Lookback", "Candles that define upper liquidity", "Liquidity");
_lowerLookback = Param(nameof(LowerLookback), 10)
.SetGreaterThanZero()
.SetDisplay("Lower Lookback", "Candles that define lower liquidity", "Liquidity");
_stopLossPips = Param(nameof(StopLossPips), 10m)
.SetNotNegative()
.SetDisplay("Stop Loss Pips", "Stop loss in pips", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 20m)
.SetNotNegative()
.SetDisplay("Take Profit Pips", "Take profit in pips, 0 disables it", "Risk");
_mode = Param(nameof(Mode), TradeModes.Both)
.SetDisplay("Mode", "Allowed trade directions", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevHighest = null;
_prevLowest = null;
_prevCandle = null;
_upperTouched = false;
_lowerTouched = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var highest = new Highest { Length = UpperLookback };
var lowest = new Lowest { Length = LowerLookback };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(highest, lowest, ProcessCandle)
.Start();
var step = Security?.PriceStep ?? 1m;
StartProtection(
TakeProfitPips > 0m ? new Unit(TakeProfitPips * step, UnitTypes.Absolute) : new Unit(),
StopLossPips > 0m ? new Unit(StopLossPips * step, UnitTypes.Absolute) : new Unit(),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue highestValue, IIndicatorValue lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
// Liquidity levels come from the candles before this one.
var upper = _prevHighest;
var lower = _prevLowest;
var prev = _prevCandle;
_prevHighest = highestValue.IsFormed ? highestValue.GetValue<decimal>() : null;
_prevLowest = lowestValue.IsFormed ? lowestValue.GetValue<decimal>() : null;
_prevCandle = candle;
if (upper is decimal upperLevel && candle.HighPrice >= upperLevel)
_upperTouched = true;
if (lower is decimal lowerLevel && candle.LowPrice <= lowerLevel)
_lowerTouched = true;
if (prev == null)
return;
var bullishEngulfing = prev.ClosePrice < prev.OpenPrice && candle.ClosePrice > candle.OpenPrice &&
candle.OpenPrice <= prev.ClosePrice && candle.ClosePrice >= prev.OpenPrice;
var bearishEngulfing = prev.ClosePrice > prev.OpenPrice && candle.ClosePrice < candle.OpenPrice &&
candle.OpenPrice >= prev.ClosePrice && candle.ClosePrice <= prev.OpenPrice;
var longSignal = _lowerTouched && bullishEngulfing;
var shortSignal = _upperTouched && bearishEngulfing;
if (longSignal)
_lowerTouched = false;
if (shortSignal)
_upperTouched = false;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (longSignal)
{
if (Mode != TradeModes.Short && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (Mode == TradeModes.Short && Position < 0)
BuyMarket(-Position);
}
else if (shortSignal)
{
if (Mode != TradeModes.Long && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Mode == TradeModes.Long && Position > 0)
SellMarket(Position);
}
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
MODE_BOTH = 0
MODE_LONG = 1
MODE_SHORT = 2
class liquidity_engulfment_strategy(Strategy):
"""
Liquidity Engulfment strategy.
Upper liquidity is the highest high of the previous UpperLookback candles and lower liquidity the lowest low of the previous
LowerLookback candles. Once a candle touches lower liquidity, the next bullish engulfing candle opens a long; once a candle touches
upper liquidity, the next bearish engulfing candle opens a short. Mode restricts the allowed side: a signal of the disabled side only
closes the open position. Stop loss and take profit are set in pips (price steps).
"""
def __init__(self):
super(liquidity_engulfment_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._upper_lookback = self.Param("UpperLookback", 10).SetGreaterThanZero().SetDisplay("Upper Lookback", "Candles that define upper liquidity", "Liquidity")
self._lower_lookback = self.Param("LowerLookback", 10).SetGreaterThanZero().SetDisplay("Lower Lookback", "Candles that define lower liquidity", "Liquidity")
self._stop_loss_pips = self.Param("StopLossPips", 10.0).SetNotNegative().SetDisplay("Stop Loss Pips", "Stop loss in pips", "Risk")
self._take_profit_pips = self.Param("TakeProfitPips", 20.0).SetNotNegative().SetDisplay("Take Profit Pips", "Take profit in pips, 0 disables it", "Risk")
self._mode = self.Param("Mode", MODE_BOTH).SetDisplay("Mode", "0 Both, 1 Long, 2 Short", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_highest = None
self._prev_lowest = None
self._prev_candle = None
self._upper_touched = False
self._lower_touched = False
def OnReseted(self):
super(liquidity_engulfment_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(liquidity_engulfment_strategy, self).OnStarted2(time)
self._reset_state()
highest = Highest()
highest.Length = self._upper_lookback.Value
lowest = Lowest()
lowest.Length = self._lower_lookback.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(highest, lowest, 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
tp = float(self._take_profit_pips.Value)
sl = float(self._stop_loss_pips.Value)
self.StartProtection(
Unit(Decimal(tp * step), UnitTypes.Absolute) if tp > 0 else Unit(),
Unit(Decimal(sl * step), UnitTypes.Absolute) if sl > 0 else Unit(),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _process_candle(self, candle, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
# Liquidity levels come from the candles before this one.
upper = self._prev_highest
lower = self._prev_lowest
prev = self._prev_candle
self._prev_highest = highest_value.GetValue[Decimal](None) if highest_value.IsFormed else None
self._prev_lowest = lowest_value.GetValue[Decimal](None) if lowest_value.IsFormed else None
self._prev_candle = candle
if upper is not None and candle.HighPrice >= upper:
self._upper_touched = True
if lower is not None and candle.LowPrice <= lower:
self._lower_touched = True
if prev is None:
return
bullish_engulfing = prev.ClosePrice < prev.OpenPrice and candle.ClosePrice > candle.OpenPrice \
and candle.OpenPrice <= prev.ClosePrice and candle.ClosePrice >= prev.OpenPrice
bearish_engulfing = prev.ClosePrice > prev.OpenPrice and candle.ClosePrice < candle.OpenPrice \
and candle.OpenPrice >= prev.ClosePrice and candle.ClosePrice <= prev.OpenPrice
long_signal = self._lower_touched and bullish_engulfing
short_signal = self._upper_touched and bearish_engulfing
if long_signal:
self._lower_touched = False
if short_signal:
self._upper_touched = False
if not self.IsFormedAndOnlineAndAllowTrading():
return
mode = self._mode.Value
if long_signal:
if mode != MODE_SHORT and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif mode == MODE_SHORT and self.Position < 0:
self.BuyMarket(-self.Position)
elif short_signal:
if mode != MODE_LONG and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif mode == MODE_LONG and self.Position > 0:
self.SellMarket(self.Position)
def CreateClone(self):
return liquidity_engulfment_strategy()