Estrategia de Captura de Liquidez en Gold y EUR/USD
Esta estrategia detecta capturas de liquidez en zonas de oferta y demanda en Gold y EUR/USD usando RSI, SMA, Oscilador Estocástico y brechas de valor razonable basadas en ATR.
Detalles
- Criterios de entrada:
- Largo: El precio pincha por debajo del mínimo reciente, la estructura de mercado se desplaza hacia arriba, se produce una brecha de valor razonable, RSI sobrevendido, precio por encima de la SMA, Estocástico sobrevendido.
- Corto: El precio pincha por encima del máximo reciente, la estructura de mercado se desplaza hacia abajo, se produce una brecha de valor razonable, RSI sobrecomprado, precio por debajo de la SMA, Estocástico sobrecomprado.
- Largo/Corto: Ambos.
- Criterios de salida: Señal inversa.
- Stops: No.
- Valores predeterminados:
RsiLength= 14MaLength= 50StochLength= 14Overbought= 70Oversold= 30StochOverbought= 80StochOversold= 20
- Filtros:
- Categoría: Price action
- Dirección: Ambos
- Indicadores: RSI, SMA, Stochastic, ATR, Highest, Lowest
- Stops: No
- Complejidad: Medio
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()