Gold & EUR/USD 流動性グラブ戦略
この戦略は、RSI、SMA、ストキャスティクスオシレーター、ATRベースのフェアバリューギャップを使用してGoldとEUR/USDの需要・供給ゾーンにおける流動性グラブを検出します。
詳細
- エントリー条件:
- ロング: 価格が直近安値を下抜ける影をつけ、市場構造が上向きに転換し、フェアバリューギャップが発生し、RSIが売られ過ぎ、価格がSMAを上回り、ストキャスティクスが売られ過ぎ。
- ショート: 価格が直近高値を上抜ける影をつけ、市場構造が下向きに転換し、フェアバリューギャップが発生し、RSIが買われ過ぎ、価格がSMAを下回り、ストキャスティクスが買われ過ぎ。
- ロング/ショート: 両方。
- エグジット条件: 逆シグナル。
- ストップ: いいえ。
- デフォルト値:
RsiLength= 14MaLength= 50StochLength= 14Overbought= 70Oversold= 30StochOverbought= 80StochOversold= 20
- フィルター:
- カテゴリ: Price action
- 方向: 両方
- インジケーター: RSI, SMA, Stochastic, ATR, Highest, Lowest
- ストップ: いいえ
- 複雑さ: 中
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Gold and EUR/USD liquidity grab strategy.
/// A bullish liquidity grab is a candle that wicks below the lowest low of the previous candles while RSI is below Oversold and the
/// Stochastic %K below StochOversold. Within the following candles a bullish fair value gap (low above the high two candles back) whose
/// candle spans more than one ATR and closes above the previous high marks the market structure shift; if the close is also above the
/// MaLength SMA the strategy goes long. Shorts mirror the rules. An opposite signal reverses the position.
/// </summary>
public class GoldEurUsdStrategy : Strategy
{
private const int _swingLength = 20;
private const int _atrLength = 14;
private const int _setupBars = 10;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<int> _stochLength;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevHighest;
private decimal? _prevLowest;
private decimal? _high1, _high2, _low1, _low2;
private int _barsSinceBullGrab;
private int _barsSinceBearGrab;
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// SMA trend filter length.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Stochastic %K length.
/// </summary>
public int StochLength
{
get => _stochLength.Value;
set => _stochLength.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
/// <summary>
/// Stochastic overbought level.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Stochastic oversold level.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GoldEurUsdStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "Indicators");
_maLength = Param(nameof(MaLength), 50)
.SetGreaterThanZero()
.SetDisplay("MA Length", "SMA trend filter length", "Indicators");
_stochLength = Param(nameof(StochLength), 14)
.SetGreaterThanZero()
.SetDisplay("Stoch Length", "Stochastic %K length", "Indicators");
_overbought = Param(nameof(Overbought), 70m)
.SetDisplay("Overbought", "RSI overbought level", "Levels");
_oversold = Param(nameof(Oversold), 30m)
.SetDisplay("Oversold", "RSI oversold level", "Levels");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Stoch Overbought", "Stochastic overbought level", "Levels");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Stoch Oversold", "Stochastic oversold level", "Levels");
_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();
ResetState();
}
private void ResetState()
{
_prevHighest = null;
_prevLowest = null;
_high1 = _high2 = _low1 = _low2 = null;
_barsSinceBullGrab = int.MaxValue;
_barsSinceBearGrab = int.MaxValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var sma = new SimpleMovingAverage { Length = MaLength };
var stochastic = new StochasticOscillator
{
K = { Length = StochLength },
D = { Length = 3 },
};
var atr = new AverageTrueRange { Length = _atrLength };
var highest = new Highest { Length = _swingLength };
var lowest = new Lowest { Length = _swingLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, sma, stochastic, atr, highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsi);
DrawIndicator(oscillators, stochastic);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiValue, IIndicatorValue smaValue, IIndicatorValue stochValue,
IIndicatorValue atrValue, IIndicatorValue highestValue, IIndicatorValue lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
// The swing extremes and the fair value gap use the candles before this one.
var swingHigh = _prevHighest;
var swingLow = _prevLowest;
var high1 = _high1;
var high2 = _high2;
var low1 = _low1;
var low2 = _low2;
if (highestValue.IsFormed && lowestValue.IsFormed)
{
_prevHighest = highestValue.GetValue<decimal>();
_prevLowest = lowestValue.GetValue<decimal>();
}
_high2 = _high1;
_low2 = _low1;
_high1 = candle.HighPrice;
_low1 = candle.LowPrice;
if (_barsSinceBullGrab < int.MaxValue)
_barsSinceBullGrab++;
if (_barsSinceBearGrab < int.MaxValue)
_barsSinceBearGrab++;
if (!rsiValue.IsFormed || !smaValue.IsFormed || !stochValue.IsFormed || !atrValue.IsFormed)
return;
if (stochValue is not IStochasticOscillatorValue { K: decimal stochK })
return;
if (swingHigh is not decimal sh || swingLow is not decimal sl || high1 is not decimal h1 || high2 is not decimal h2 || low1 is not decimal l1 || low2 is not decimal l2)
return;
var rsi = rsiValue.GetValue<decimal>();
var sma = smaValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (candle.LowPrice < sl && close > sl && rsi < Oversold && stochK < StochOversold)
_barsSinceBullGrab = 0;
if (candle.HighPrice > sh && close < sh && rsi > Overbought && stochK > StochOverbought)
_barsSinceBearGrab = 0;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var displacement = candle.HighPrice - candle.LowPrice > atr;
var bullishShift = displacement && candle.LowPrice > h2 && close > h1;
var bearishShift = displacement && candle.HighPrice < l2 && close < l1;
if (_barsSinceBullGrab <= _setupBars && bullishShift && close > sma && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_barsSinceBullGrab = int.MaxValue;
}
else if (_barsSinceBearGrab <= _setupBars && bearishShift && close < sma && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_barsSinceBearGrab = int.MaxValue;
}
}
}
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 RelativeStrengthIndex, SimpleMovingAverage, StochasticOscillator, AverageTrueRange, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class gold_eur_usd_strategy(Strategy):
"""
Gold and EUR/USD liquidity grab strategy.
A bullish liquidity grab is a candle that wicks below the lowest low of the previous candles while RSI is below Oversold and the
Stochastic %K below StochOversold. Within the following candles a bullish fair value gap (low above the high two candles back) whose
candle spans more than one ATR and closes above the previous high marks the market structure shift; if the close is also above the
MaLength SMA the strategy goes long. Shorts mirror the rules. An opposite signal reverses the position.
"""
SWING_LENGTH = 20
ATR_LENGTH = 14
SETUP_BARS = 10
NEVER = 1 << 30
def __init__(self):
super(gold_eur_usd_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "Indicators")
self._ma_length = self.Param("MaLength", 50).SetGreaterThanZero().SetDisplay("MA Length", "SMA trend filter length", "Indicators")
self._stoch_length = self.Param("StochLength", 14).SetGreaterThanZero().SetDisplay("Stoch Length", "Stochastic %K length", "Indicators")
self._overbought = self.Param("Overbought", 70.0).SetDisplay("Overbought", "RSI overbought level", "Levels")
self._oversold = self.Param("Oversold", 30.0).SetDisplay("Oversold", "RSI oversold level", "Levels")
self._stoch_overbought = self.Param("StochOverbought", 80.0).SetDisplay("Stoch Overbought", "Stochastic overbought level", "Levels")
self._stoch_oversold = self.Param("StochOversold", 20.0).SetDisplay("Stoch Oversold", "Stochastic oversold level", "Levels")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "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._high1 = None
self._high2 = None
self._low1 = None
self._low2 = None
self._bars_since_bull_grab = self.NEVER
self._bars_since_bear_grab = self.NEVER
def OnReseted(self):
super(gold_eur_usd_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(gold_eur_usd_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
sma = SimpleMovingAverage()
sma.Length = self._ma_length.Value
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_length.Value
stochastic.D.Length = 3
atr = AverageTrueRange()
atr.Length = self.ATR_LENGTH
highest = Highest()
highest.Length = self.SWING_LENGTH
lowest = Lowest()
lowest.Length = self.SWING_LENGTH
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi, sma, stochastic, atr, highest, lowest, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
self.DrawIndicator(oscillators, stochastic)
def _process_candle(self, candle, rsi_value, sma_value, stoch_value, atr_value, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
# The swing extremes and the fair value gap use the candles before this one.
swing_high = self._prev_highest
swing_low = self._prev_lowest
high1 = self._high1
high2 = self._high2
low1 = self._low1
low2 = self._low2
if highest_value.IsFormed and lowest_value.IsFormed:
self._prev_highest = highest_value.GetValue[Decimal](None)
self._prev_lowest = lowest_value.GetValue[Decimal](None)
self._high2 = self._high1
self._low2 = self._low1
self._high1 = candle.HighPrice
self._low1 = candle.LowPrice
if self._bars_since_bull_grab < self.NEVER:
self._bars_since_bull_grab += 1
if self._bars_since_bear_grab < self.NEVER:
self._bars_since_bear_grab += 1
if not rsi_value.IsFormed or not sma_value.IsFormed or not stoch_value.IsFormed or not atr_value.IsFormed:
return
stoch_k = stoch_value.K
if stoch_k is None:
return
if swing_high is None or swing_low is None or high1 is None or high2 is None or low1 is None or low2 is None:
return
rsi = rsi_value.GetValue[Decimal](None)
sma = sma_value.GetValue[Decimal](None)
atr = atr_value.GetValue[Decimal](None)
close = candle.ClosePrice
if candle.LowPrice < swing_low and close > swing_low and rsi < Decimal(self._oversold.Value) and stoch_k < Decimal(self._stoch_oversold.Value):
self._bars_since_bull_grab = 0
if candle.HighPrice > swing_high and close < swing_high and rsi > Decimal(self._overbought.Value) and stoch_k > Decimal(self._stoch_overbought.Value):
self._bars_since_bear_grab = 0
if not self.IsFormedAndOnlineAndAllowTrading():
return
displacement = candle.HighPrice - candle.LowPrice > atr
bullish_shift = displacement and candle.LowPrice > high2 and close > high1
bearish_shift = displacement and candle.HighPrice < low2 and close < low1
if self._bars_since_bull_grab <= self.SETUP_BARS and bullish_shift and close > sma and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._bars_since_bull_grab = self.NEVER
elif self._bars_since_bear_grab <= self.SETUP_BARS and bearish_shift and close < sma and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._bars_since_bear_grab = self.NEVER
def CreateClone(self):
return gold_eur_usd_strategy()