Estrategia de Rango Estrecho
Esta estrategia opera rupturas después de una barra interior donde el rango de la vela más reciente es más estrecho que el de la barra de referencia Length períodos atrás. Se colocan órdenes stop en el máximo y mínimo de referencia con un take profit igual al rango de referencia y un stop loss establecido como porcentaje de dicho rango.
Detalles
- Criterios de entrada:
- Largo: el precio supera el máximo de referencia tras una barra de rango estrecho
- Corto: el precio rompe por debajo del mínimo de referencia tras una barra de rango estrecho
- Largo/Corto: Ambos
- Criterios de salida:
- Take profit en el rango de referencia
- Stop loss como porcentaje del rango
- Stops: Sí
- Valores predeterminados:
Length= 4StopLossPercent= 0.35mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: Ninguno
- Stops: Sí
- Complejidad: Básico
- Marco temporal: Corto plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Narrow range breakout strategy.
/// A setup appears on an inside bar whose range is narrower than the range of the reference bar Length periods ago.
/// The reference bar's high and low become breakout levels: a close above the high buys, a close below the low sells.
/// The take profit is the reference range away from the entry level and the stop loss is StopLossPercent of that range.
/// </summary>
public class NarrowRangeStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _history = [];
private decimal? _breakoutHigh;
private decimal? _breakoutLow;
private decimal _setupRange;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Bars back to the reference bar.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Stop loss as a fraction of the reference range.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public NarrowRangeStrategy()
{
_length = Param(nameof(Length), 4)
.SetGreaterThanZero()
.SetDisplay("Length", "Bars back to the reference bar", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 0.35m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss as a fraction of the reference range", "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();
}
private void ResetState()
{
_history.Clear();
_breakoutHigh = null;
_breakoutLow = null;
_setupRange = 0m;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (IsFormedAndOnlineAndAllowTrading())
Trade(candle);
_history.Add(candle);
if (_history.Count > Length + 1)
_history.RemoveAt(0);
if (Position != 0 || _history.Count <= Length)
return;
var previous = _history[^2];
var reference = _history[0];
var range = candle.HighPrice - candle.LowPrice;
var referenceRange = reference.HighPrice - reference.LowPrice;
var insideBar = candle.HighPrice < previous.HighPrice && candle.LowPrice > previous.LowPrice;
if (insideBar && range < referenceRange && referenceRange > 0)
{
_breakoutHigh = reference.HighPrice;
_breakoutLow = reference.LowPrice;
_setupRange = referenceRange;
}
}
private void Trade(ICandleMessage candle)
{
if (Position > 0)
{
if ((StopLossPercent > 0 && candle.LowPrice <= _stopPrice) || candle.HighPrice >= _takePrice)
SellMarket(Position);
return;
}
if (Position < 0)
{
if ((StopLossPercent > 0 && candle.HighPrice >= _stopPrice) || candle.LowPrice <= _takePrice)
BuyMarket(-Position);
return;
}
if (_breakoutHigh is not decimal high || _breakoutLow is not decimal low)
return;
if (candle.ClosePrice > high)
{
BuyMarket(Volume);
_takePrice = high + _setupRange;
_stopPrice = high - _setupRange * StopLossPercent;
_breakoutHigh = _breakoutLow = null;
}
else if (candle.ClosePrice < low)
{
SellMarket(Volume);
_takePrice = low - _setupRange;
_stopPrice = low + _setupRange * StopLossPercent;
_breakoutHigh = _breakoutLow = null;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class narrow_range_strategy(Strategy):
"""
Narrow range breakout strategy.
A setup appears on an inside bar whose range is narrower than the range of the reference bar Length periods ago.
The reference bar's high and low become breakout levels: a close above the high buys, a close below the low sells.
The take profit is the reference range away from the entry level and the stop loss is StopLossPercent of that range.
"""
def __init__(self):
super(narrow_range_strategy, self).__init__()
self._length = self.Param("Length", 4).SetGreaterThanZero().SetDisplay("Length", "Bars back to the reference bar", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 0.35).SetNotNegative().SetDisplay("Stop Loss", "Stop loss as a fraction of the reference range", "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 GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def _reset_state(self):
self._history = []
self._breakout_high = None
self._breakout_low = None
self._setup_range = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(narrow_range_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(narrow_range_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self.IsFormedAndOnlineAndAllowTrading():
self._trade(candle)
length = self._length.Value
high = float(candle.HighPrice)
low = float(candle.LowPrice)
self._history.append((high, low))
if len(self._history) > length + 1:
self._history.pop(0)
if self.Position != 0 or len(self._history) <= length:
return
prev_high, prev_low = self._history[-2]
ref_high, ref_low = self._history[0]
rng = high - low
ref_range = ref_high - ref_low
inside_bar = high < prev_high and low > prev_low
if inside_bar and rng < ref_range and ref_range > 0:
self._breakout_high = ref_high
self._breakout_low = ref_low
self._setup_range = ref_range
def _trade(self, candle):
sl = float(self._stop_loss_percent.Value)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0:
if (sl > 0 and low <= self._stop_price) or high >= self._take_price:
self.SellMarket(self.Position)
return
if self.Position < 0:
if (sl > 0 and high >= self._stop_price) or low <= self._take_price:
self.BuyMarket(-self.Position)
return
if self._breakout_high is None or self._breakout_low is None:
return
close = float(candle.ClosePrice)
if close > self._breakout_high:
self.BuyMarket(self.Volume)
self._take_price = self._breakout_high + self._setup_range
self._stop_price = self._breakout_high - self._setup_range * sl
self._breakout_high = None
self._breakout_low = None
elif close < self._breakout_low:
self.SellMarket(self.Volume)
self._take_price = self._breakout_low - self._setup_range
self._stop_price = self._breakout_low + self._setup_range * sl
self._breakout_high = None
self._breakout_low = None
def CreateClone(self):
return narrow_range_strategy()