Estrategia IU de Negociación en Rango
La estrategia IU Range Trading identifica zonas de consolidación donde el rango de precios durante un período de lookback se mantiene dentro de un multiplicador ATR. Las operaciones de ruptura se activan cuando el precio supera los límites del rango. Las posiciones están protegidas por un stop trailing basado en ATR que se mueve con la acción favorable del precio.
Detalles
- Criterios de entrada: El precio rompe por encima o por debajo de un rango estrecho definido por ATR.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Stop trailing basado en ATR.
- Stops: Sí.
- Valores predeterminados:
RangeLength= 10AtrLength= 14AtrTargetFactor= 2.0mAtrRangeFactor= 1.75mCandleType= TimeSpan.FromMinutes(1)
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: ATR, Highest, Lowest
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- 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>
/// Range breakout strategy using ATR-based thresholds and trailing stop.
/// </summary>
public class IuRangeTradingStrategy : Strategy
{
private readonly StrategyParam<int> _rangeLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrTargetFactor;
private readonly StrategyParam<decimal> _atrRangeFactor;
private readonly StrategyParam<int> _cooldownDays;
private readonly StrategyParam<DataType> _candleType;
private bool _previousRangeCond;
private decimal _rangeHigh;
private decimal _rangeLow;
private decimal? _sl0;
private decimal? _trailingSl;
private decimal _entryPrice;
private DateTime _nextEntryDate;
/// <summary>
/// Lookback period for range detection.
/// </summary>
public int RangeLength
{
get => _rangeLength.Value;
set => _rangeLength.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// Multiplier for trailing stop step.
/// </summary>
public decimal AtrTargetFactor
{
get => _atrTargetFactor.Value;
set => _atrTargetFactor.Value = value;
}
/// <summary>
/// ATR multiplier to validate range.
/// </summary>
public decimal AtrRangeFactor
{
get => _atrRangeFactor.Value;
set => _atrRangeFactor.Value = value;
}
/// <summary>
/// Minimum days between entries.
/// </summary>
public int CooldownDays
{
get => _cooldownDays.Value;
set => _cooldownDays.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="IuRangeTradingStrategy"/>.
/// </summary>
public IuRangeTradingStrategy()
{
_rangeLength = Param(nameof(RangeLength), 10)
.SetDisplay("Range Length", "Lookback period for range detection.", "Parameters");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Parameters");
_atrTargetFactor = Param(nameof(AtrTargetFactor), 2.0m)
.SetDisplay("ATR Target Factor", "Multiplier for trailing stop step.", "Parameters");
_atrRangeFactor = Param(nameof(AtrRangeFactor), 1.75m)
.SetDisplay("ATR Range Factor", "ATR multiplier to validate range.", "Parameters");
_cooldownDays = Param(nameof(CooldownDays), 3)
.SetDisplay("Cooldown Days", "Minimum days between entries.", "Parameters");
_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();
_previousRangeCond = false;
_rangeHigh = 0m;
_rangeLow = 0m;
_sl0 = null;
_trailingSl = null;
_entryPrice = 0m;
_nextEntryDate = DateTime.MinValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousRangeCond = false;
_rangeHigh = 0m;
_rangeLow = 0m;
_sl0 = null;
_trailingSl = null;
_entryPrice = 0m;
_nextEntryDate = DateTime.MinValue;
var highest = new Highest { Length = RangeLength };
var lowest = new Lowest { Length = RangeLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var rangeCond = (highestValue - lowestValue) <= atrValue * AtrRangeFactor;
// Track range boundaries
if (rangeCond && !_previousRangeCond && Position == 0)
{
_rangeHigh = highestValue;
_rangeLow = lowestValue;
}
else if (rangeCond && _previousRangeCond && Position == 0)
{
_rangeHigh = Math.Max(_rangeHigh, highestValue);
_rangeLow = Math.Min(_rangeLow, lowestValue);
}
// Entry logic: breakout from range
if (Position == 0 && _rangeHigh != 0 && _rangeLow != 0 && candle.OpenTime.Date >= _nextEntryDate)
{
if (candle.ClosePrice > _rangeHigh)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_sl0 = _entryPrice - atrValue * AtrTargetFactor;
_trailingSl = _entryPrice + atrValue * AtrTargetFactor;
_nextEntryDate = candle.OpenTime.Date.AddDays(CooldownDays);
}
else if (candle.ClosePrice < _rangeLow)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_sl0 = _entryPrice + atrValue * AtrTargetFactor;
_trailingSl = _entryPrice - atrValue * AtrTargetFactor;
_nextEntryDate = candle.OpenTime.Date.AddDays(CooldownDays);
}
}
// Exit logic with trailing stop
if (Position > 0 && _sl0.HasValue && _trailingSl.HasValue)
{
if (candle.HighPrice > _trailingSl.Value)
{
var step = atrValue * AtrTargetFactor;
_sl0 = _trailingSl - step;
_trailingSl += step;
}
if (candle.LowPrice <= _sl0.Value)
{
SellMarket();
_sl0 = _trailingSl = null;
_rangeHigh = 0m;
_rangeLow = 0m;
}
}
else if (Position < 0 && _sl0.HasValue && _trailingSl.HasValue)
{
if (candle.LowPrice < _trailingSl.Value)
{
var step = atrValue * AtrTargetFactor;
_sl0 = _trailingSl + step;
_trailingSl -= step;
}
if (candle.HighPrice >= _sl0.Value)
{
BuyMarket();
_sl0 = _trailingSl = null;
_rangeHigh = 0m;
_rangeLow = 0m;
}
}
_previousRangeCond = rangeCond;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class iu_range_trading_strategy(Strategy):
def __init__(self):
super(iu_range_trading_strategy, self).__init__()
self._range_length = self.Param("RangeLength", 10) \
.SetDisplay("Range Length", "Lookback period for range detection", "Parameters")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Parameters")
self._atr_target_factor = self.Param("AtrTargetFactor", 2.0) \
.SetDisplay("ATR Target Factor", "Multiplier for trailing stop step", "Parameters")
self._atr_range_factor = self.Param("AtrRangeFactor", 1.75) \
.SetDisplay("ATR Range Factor", "ATR multiplier to validate range", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_range_cond = False
self._range_high = 0.0
self._range_low = 0.0
self._sl0 = None
self._trailing_sl = None
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(iu_range_trading_strategy, self).OnReseted()
self._prev_range_cond = False
self._range_high = 0.0
self._range_low = 0.0
self._sl0 = None
self._trailing_sl = None
self._entry_price = 0.0
def OnStarted2(self, time):
super(iu_range_trading_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self._range_length.Value
lowest = Lowest()
lowest.Length = self._range_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnProcess(self, candle, highest_val, lowest_val, atr_val):
if candle.State != CandleStates.Finished:
return
h_val = float(highest_val)
l_val = float(lowest_val)
atr_v = float(atr_val)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
rf = float(self._atr_range_factor.Value)
tf = float(self._atr_target_factor.Value)
range_cond = (h_val - l_val) <= atr_v * rf
if range_cond and not self._prev_range_cond and self.Position == 0:
self._range_high = h_val
self._range_low = l_val
elif range_cond and self._prev_range_cond and self.Position == 0:
if h_val > self._range_high:
self._range_high = h_val
if l_val < self._range_low:
self._range_low = l_val
if self.Position == 0 and self._range_high != 0 and self._range_low != 0:
if close > self._range_high:
self.BuyMarket()
self._entry_price = close
self._sl0 = self._entry_price - atr_v * tf
self._trailing_sl = self._entry_price + atr_v * tf
elif close < self._range_low:
self.SellMarket()
self._entry_price = close
self._sl0 = self._entry_price + atr_v * tf
self._trailing_sl = self._entry_price - atr_v * tf
if self.Position > 0 and self._sl0 is not None and self._trailing_sl is not None:
if high > self._trailing_sl:
step = atr_v * tf
self._sl0 = self._trailing_sl - step
self._trailing_sl += step
if low <= self._sl0:
self.SellMarket()
self._sl0 = None
self._trailing_sl = None
self._range_high = 0.0
self._range_low = 0.0
elif self.Position < 0 and self._sl0 is not None and self._trailing_sl is not None:
if low < self._trailing_sl:
step = atr_v * tf
self._sl0 = self._trailing_sl + step
self._trailing_sl -= step
if high >= self._sl0:
self.BuyMarket()
self._sl0 = None
self._trailing_sl = None
self._range_high = 0.0
self._range_low = 0.0
self._prev_range_cond = range_cond
def CreateClone(self):
return iu_range_trading_strategy()