Esta estrategia detecta fases de mercado tranquilas donde tres medias móviles convergen dentro de una banda estrecha. Cuando el precio finalmente rompe por encima o por debajo de este rango, la estrategia entra en la dirección de la ruptura y busca capturar la tendencia emergente.
El sistema observa la diferencia entre las SMA Rápida, Media y Lenta. Si la diferencia máxima entre estas medias permanece por debajo del umbral configurado durante un número específico de barras, el mercado se considera "lateral". El máximo más alto y el mínimo más bajo de ese período definen los niveles de ruptura.
Las operaciones se abren cuando el precio cierra más allá de estos extremos. Las posiciones se protegen con condiciones inversas: si el precio regresa al rango o alcanza un múltiplo del ancho del rango en beneficio, la posición se cierra.
Detalles
Criterios de entrada:
Largo: Tras un rango de RangeLength barras donde la diferencia de SMA esté por debajo de ShakeThreshold, entrar cuando el precio cierre por encima del máximo más alto del rango.
Corto: Bajo las mismas condiciones de rango, entrar cuando el precio cierre por debajo del mínimo más bajo del rango.
Largo/Corto: Ambas direcciones.
Criterios de salida:
Largo: Cerrar si el precio regresa por debajo del mínimo del rango o el beneficio supera 4 * (máximo del rango - mínimo del rango).
Corto: Cerrar si el precio regresa por encima del máximo del rango o el beneficio supera 4 * (máximo del rango - mínimo del rango).
Stops: Salidas implícitas basadas en los límites del rango y el múltiplo de beneficio.
Valores predeterminados:
FastSma = 38
MidSma = 140
SlowSma = 210
ShakeThreshold = 250
RangeLength = 200
CandleType = TimeSpan.FromMinutes(1)
Filtros:
Categoría: Ruptura
Dirección: Ambos
Indicadores: SMA, Highest, Lowest
Stops: Sí
Complejidad: Básico
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>
/// Breakout strategy that waits for converging SMAs and trades on breakout.
/// </summary>
public class BreakTheRangeBoundStrategy : Strategy
{
private readonly StrategyParam<int> _fastSma;
private readonly StrategyParam<int> _slowSma;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _prevClose;
private bool _hasPrev;
public int FastSma { get => _fastSma.Value; set => _fastSma.Value = value; }
public int SlowSma { get => _slowSma.Value; set => _slowSma.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public BreakTheRangeBoundStrategy()
{
_fastSma = Param(nameof(FastSma), 10)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "Fast moving average period", "Parameters");
_slowSma = Param(nameof(SlowSma), 50)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Slow moving average period", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_prevClose = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastSma };
var slow = new ExponentialMovingAverage { Length = SlowSma };
SubscribeCandles(CandleType).Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevFast = fastValue;
_prevSlow = slowValue;
_prevClose = close;
_hasPrev = true;
return;
}
// Cross above slow SMA => buy breakout
if (_prevClose <= _prevSlow && close > slowValue && fastValue > slowValue && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Cross below slow SMA => sell breakout
else if (_prevClose >= _prevSlow && close < slowValue && fastValue < slowValue && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevFast = fastValue;
_prevSlow = slowValue;
_prevClose = close;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class break_the_range_bound_strategy(Strategy):
def __init__(self):
super(break_the_range_bound_strategy, self).__init__()
self._fast_sma = self.Param("FastSma", 10) \
.SetDisplay("Fast SMA", "Fast moving average period", "Parameters")
self._slow_sma = self.Param("SlowSma", 50) \
.SetDisplay("Slow SMA", "Slow moving average period", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_close = 0.0
self._has_prev = False
@property
def fast_sma(self):
return self._fast_sma.Value
@property
def slow_sma(self):
return self._slow_sma.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(break_the_range_bound_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_close = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(break_the_range_bound_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self.fast_sma
slow = ExponentialMovingAverage()
slow.Length = self.slow_sma
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if not self._has_prev:
self._prev_fast = fast_value
self._prev_slow = slow_value
self._prev_close = close
self._has_prev = True
return
# Cross above slow SMA => buy breakout
if self._prev_close <= self._prev_slow and close > slow_value and fast_value > slow_value and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Cross below slow SMA => sell breakout
elif self._prev_close >= self._prev_slow and close < slow_value and fast_value < slow_value and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fast_value
self._prev_slow = slow_value
self._prev_close = close
def CreateClone(self):
return break_the_range_bound_strategy()