Divergencia RSI de Vela Grande
Identifica velas inusualmente grandes en relación con las cinco barras anteriores y compara los valores de RSI rápido y lento. Las operaciones siguen la dirección de la vela y usan un stop trailing retrasado que se activa solo después de que el precio se mueva un número determinado de ticks en beneficio.
El stop trailing comienza una vez alcanzado el umbral de beneficio y luego sigue el precio a una distancia fija, mientras que un stop fijo inicial protege la operación desde el inicio.
Detalles
- Criterios de entrada:
- Largo: El cuerpo de la vela actual es mayor que los cinco anteriores y cierra al alza.
- Corto: El cuerpo de la vela actual es mayor que los cinco anteriores y cierra a la baja.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Stop inicial o stop trailing alcanzado.
- Stops: Sí, stop trailing retrasado.
- Valores predeterminados:
TrailStartTicks= 200TrailDistanceTicks= 150InitialStopLossTicks= 200CandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Reversión
- Dirección: Ambos
- Indicadores: RSI
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: Sí
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Big Candle RSI Divergence strategy.
/// When flat, a candle whose body is bigger than each of the previous five bodies opens a trade in its direction. An initial stop
/// InitialStopLossTicks price steps away protects the trade; once price has moved TrailStartTicks steps in profit a trailing stop
/// follows the best price at TrailDistanceTicks steps. Fast RSI(5) and slow RSI(14) are plotted for comparison.
/// </summary>
public class BigCandleRsiDivergenceStrategy : Strategy
{
private const int _bodyLookback = 5;
private readonly StrategyParam<int> _trailStartTicks;
private readonly StrategyParam<int> _trailDistanceTicks;
private readonly StrategyParam<int> _initialStopLossTicks;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _bodies = new();
private decimal _entryPrice;
private decimal _bestPrice;
private bool _trailingActive;
/// <summary>
/// Profit in price steps that activates the trailing stop.
/// </summary>
public int TrailStartTicks
{
get => _trailStartTicks.Value;
set => _trailStartTicks.Value = value;
}
/// <summary>
/// Trailing stop distance in price steps.
/// </summary>
public int TrailDistanceTicks
{
get => _trailDistanceTicks.Value;
set => _trailDistanceTicks.Value = value;
}
/// <summary>
/// Initial stop loss in price steps.
/// </summary>
public int InitialStopLossTicks
{
get => _initialStopLossTicks.Value;
set => _initialStopLossTicks.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BigCandleRsiDivergenceStrategy()
{
_trailStartTicks = Param(nameof(TrailStartTicks), 200)
.SetNotNegative()
.SetDisplay("Trail Start Ticks", "Profit in price steps that activates the trailing stop", "Risk");
_trailDistanceTicks = Param(nameof(TrailDistanceTicks), 150)
.SetNotNegative()
.SetDisplay("Trail Distance Ticks", "Trailing stop distance in price steps", "Risk");
_initialStopLossTicks = Param(nameof(InitialStopLossTicks), 200)
.SetNotNegative()
.SetDisplay("Initial Stop Loss Ticks", "Initial stop loss in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsiFast = new RelativeStrengthIndex { Length = 5 };
var rsiSlow = new RelativeStrengthIndex { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsiFast, rsiSlow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsiFast);
DrawIndicator(oscillators, rsiSlow);
}
}
}
private void ResetState()
{
_bodies.Clear();
_entryPrice = 0;
_bestPrice = 0;
_trailingActive = false;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiFastValue, IIndicatorValue rsiSlowValue)
{
if (candle.State != CandleStates.Finished)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var isBig = _bodies.Count == _bodyLookback && _bodies.All(b => body > b);
_bodies.Enqueue(body);
while (_bodies.Count > _bodyLookback)
_bodies.Dequeue();
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
ManageStops(candle);
return;
}
if (!isBig)
return;
if (candle.ClosePrice > candle.OpenPrice)
{
BuyMarket(Volume);
StartTrade(candle.ClosePrice);
}
else if (candle.ClosePrice < candle.OpenPrice)
{
SellMarket(Volume);
StartTrade(candle.ClosePrice);
}
}
private void StartTrade(decimal price)
{
_entryPrice = price;
_bestPrice = price;
_trailingActive = false;
}
private void ManageStops(ICandleMessage candle)
{
if (_entryPrice <= 0)
return;
var step = Security?.PriceStep ?? 1m;
var initialStop = InitialStopLossTicks * step;
var trailStart = TrailStartTicks * step;
var trailDistance = TrailDistanceTicks * step;
if (Position > 0)
{
decimal? stop = InitialStopLossTicks > 0 ? _entryPrice - initialStop : null;
if (_trailingActive)
stop = Math.Max(stop ?? decimal.MinValue, _bestPrice - trailDistance);
if (stop is decimal s && candle.LowPrice <= s)
{
SellMarket(Position);
_entryPrice = 0;
return;
}
_bestPrice = Math.Max(_bestPrice, candle.HighPrice);
if (TrailDistanceTicks > 0 && _bestPrice - _entryPrice >= trailStart)
_trailingActive = true;
}
else if (Position < 0)
{
decimal? stop = InitialStopLossTicks > 0 ? _entryPrice + initialStop : null;
if (_trailingActive)
stop = Math.Min(stop ?? decimal.MaxValue, _bestPrice + trailDistance);
if (stop is decimal s && candle.HighPrice >= s)
{
BuyMarket(-Position);
_entryPrice = 0;
return;
}
_bestPrice = Math.Min(_bestPrice, candle.LowPrice);
if (TrailDistanceTicks > 0 && _entryPrice - _bestPrice >= trailStart)
_trailingActive = true;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from collections import deque
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
BODY_LOOKBACK = 5
class big_candle_rsi_divergence_strategy(Strategy):
"""
Big Candle RSI Divergence strategy.
When flat, a candle whose body is bigger than each of the previous five bodies opens a trade in its direction. An initial stop
InitialStopLossTicks price steps away protects the trade; once price has moved TrailStartTicks steps in profit a trailing stop
follows the best price at TrailDistanceTicks steps. Fast RSI(5) and slow RSI(14) are plotted for comparison.
"""
def __init__(self):
super(big_candle_rsi_divergence_strategy, self).__init__()
self._trail_start_ticks = self.Param("TrailStartTicks", 200).SetNotNegative().SetDisplay("Trail Start Ticks", "Profit in price steps that activates the trailing stop", "Risk")
self._trail_distance_ticks = self.Param("TrailDistanceTicks", 150).SetNotNegative().SetDisplay("Trail Distance Ticks", "Trailing stop distance in price steps", "Risk")
self._initial_stop_loss_ticks = self.Param("InitialStopLossTicks", 200).SetNotNegative().SetDisplay("Initial Stop Loss Ticks", "Initial stop loss in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).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._bodies = deque()
self._entry_price = Decimal(0)
self._best_price = Decimal(0)
self._trailing_active = False
def OnReseted(self):
super(big_candle_rsi_divergence_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(big_candle_rsi_divergence_strategy, self).OnStarted2(time)
self._reset_state()
rsi_fast = RelativeStrengthIndex()
rsi_fast.Length = 5
rsi_slow = RelativeStrengthIndex()
rsi_slow.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi_fast, rsi_slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi_fast)
self.DrawIndicator(oscillators, rsi_slow)
def _process_candle(self, candle, rsi_fast_value, rsi_slow_value):
if candle.State != CandleStates.Finished:
return
body = abs(candle.ClosePrice - candle.OpenPrice)
is_big = len(self._bodies) == BODY_LOOKBACK and all(body > b for b in self._bodies)
self._bodies.append(body)
while len(self._bodies) > BODY_LOOKBACK:
self._bodies.popleft()
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
self._manage_stops(candle)
return
if not is_big:
return
if candle.ClosePrice > candle.OpenPrice:
self.BuyMarket(self.Volume)
self._start_trade(candle.ClosePrice)
elif candle.ClosePrice < candle.OpenPrice:
self.SellMarket(self.Volume)
self._start_trade(candle.ClosePrice)
def _start_trade(self, price):
self._entry_price = price
self._best_price = price
self._trailing_active = False
def _manage_stops(self, candle):
if self._entry_price <= 0:
return
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
stop_ticks = self._initial_stop_loss_ticks.Value
trail_ticks = self._trail_distance_ticks.Value
initial_stop = Decimal(stop_ticks) * step
trail_start = Decimal(self._trail_start_ticks.Value) * step
trail_distance = Decimal(trail_ticks) * step
if self.Position > 0:
stop = self._entry_price - initial_stop if stop_ticks > 0 else None
if self._trailing_active:
trail = self._best_price - trail_distance
stop = trail if stop is None else max(stop, trail)
if stop is not None and candle.LowPrice <= stop:
self.SellMarket(self.Position)
self._entry_price = Decimal(0)
return
self._best_price = max(self._best_price, candle.HighPrice)
if trail_ticks > 0 and self._best_price - self._entry_price >= trail_start:
self._trailing_active = True
elif self.Position < 0:
stop = self._entry_price + initial_stop if stop_ticks > 0 else None
if self._trailing_active:
trail = self._best_price + trail_distance
stop = trail if stop is None else min(stop, trail)
if stop is not None and candle.HighPrice >= stop:
self.BuyMarket(-self.Position)
self._entry_price = Decimal(0)
return
self._best_price = min(self._best_price, candle.LowPrice)
if trail_ticks > 0 and self._entry_price - self._best_price >= trail_start:
self._trailing_active = True
def CreateClone(self):
return big_candle_rsi_divergence_strategy()