Estrategia de Ruptura de Pendiente ATR
La estrategia de Ruptura de Pendiente ATR observa la tasa de cambio del ATR. Una pendiente inusualmente pronunciada sugiere que se está formando una nueva tendencia.
Las pruebas indican un rendimiento anual promedio de aproximadamente 148%. Funciona mejor en el mercado de divisas.
Las entradas ocurren cuando la pendiente supera su nivel típico en un múltiplo de la desviación estándar, tomando operaciones en la dirección de la aceleración con un stop protector.
Atrae a los traders activos que buscan una exposición temprana a la tendencia. Las posiciones se cierran cuando la pendiente regresa a lecturas normales. AtrPeriod predeterminado = 14.
Detalles
- Criterios de entrada: El indicador supera la media por el multiplicador de desviación.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: El indicador revierte a la media.
- Stops: Sí.
- Valores predeterminados:
AtrPeriod= 14SlopePeriod= 20BreakoutMultiplier= 2.0mStopLossAtrMultiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: ATR
- Stops: Sí
- Complejidad: Intermedio
- 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.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// ATR slope breakout.
/// Enters when the ATR slope exceeds its average by a standard deviation multiplier,
/// in the direction of the breakout candle. Exits when the slope returns to its average
/// or the ATR-based stop is hit.
/// </summary>
public class AtrSlopeBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _slopePeriod;
private readonly StrategyParam<decimal> _breakoutMultiplier;
private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _slopeAverage;
private StandardDeviation _slopeStdDev;
private decimal? _prevAtr;
private decimal _stopPrice;
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Period for slope statistics.
/// </summary>
public int SlopePeriod
{
get => _slopePeriod.Value;
set => _slopePeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for breakout detection.
/// </summary>
public decimal BreakoutMultiplier
{
get => _breakoutMultiplier.Value;
set => _breakoutMultiplier.Value = value;
}
/// <summary>
/// Stop-loss distance in ATR multiples.
/// </summary>
public decimal StopLossAtrMultiplier
{
get => _stopLossAtrMultiplier.Value;
set => _stopLossAtrMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="AtrSlopeBreakoutStrategy"/>.
/// </summary>
public AtrSlopeBreakoutStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for ATR", "Indicators");
_slopePeriod = Param(nameof(SlopePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Slope Period", "Period for slope statistics", "Strategy");
_breakoutMultiplier = Param(nameof(BreakoutMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Breakout Multiplier", "Standard deviation multiplier for breakout", "Strategy");
_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 2.0m)
.SetNotNegative()
.SetDisplay("Stop ATR Multiplier", "Stop-loss distance in ATR multiples", "Risk Management");
_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();
_slopeAverage = null;
_slopeStdDev = null;
_prevAtr = null;
_stopPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
_slopeAverage = new SimpleMovingAverage { Length = SlopePeriod };
_slopeStdDev = new StandardDeviation { Length = SlopePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevAtr is not decimal prev)
{
_prevAtr = atrValue;
return;
}
_prevAtr = atrValue;
var slope = atrValue - prev;
var avgSlope = _slopeAverage.Process(slope, candle.ServerTime, true).ToDecimal();
var stdSlope = _slopeStdDev.Process(slope, candle.ServerTime, true).ToDecimal();
if (!_slopeAverage.IsFormed || !_slopeStdDev.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (CheckStop(candle))
return;
var close = candle.ClosePrice;
var stopDistance = StopLossAtrMultiplier * atrValue;
// ATR has no direction, so the breakout candle decides the side.
if (slope > avgSlope + BreakoutMultiplier * stdSlope)
{
if (close > candle.OpenPrice && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = stopDistance > 0 ? close - stopDistance : 0m;
return;
}
if (close < candle.OpenPrice && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = stopDistance > 0 ? close + stopDistance : 0m;
return;
}
}
if (Position != 0 && slope < avgSlope)
ExitPosition();
}
private bool CheckStop(ICandleMessage candle)
{
if (_stopPrice == 0m)
return false;
if ((Position > 0 && candle.LowPrice <= _stopPrice) || (Position < 0 && candle.HighPrice >= _stopPrice))
{
ExitPosition();
return true;
}
return false;
}
private void ExitPosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
_stopPrice = 0m;
}
}
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 AverageTrueRange, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class atr_slope_breakout_strategy(Strategy):
"""
ATR slope breakout.
Enters when the ATR slope exceeds its average by a standard deviation multiplier,
in the direction of the breakout candle. Exits when the slope returns to its average
or the ATR-based stop is hit.
"""
def __init__(self):
super(atr_slope_breakout_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "Period for ATR", "Indicators")
self._slope_period = self.Param("SlopePeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Slope Period", "Period for slope statistics", "Strategy")
self._breakout_multiplier = self.Param("BreakoutMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Breakout Multiplier", "Standard deviation multiplier for breakout", "Strategy")
self._stop_loss_atr_multiplier = self.Param("StopLossAtrMultiplier", 2.0) \
.SetNotNegative() \
.SetDisplay("Stop ATR Multiplier", "Stop-loss distance in ATR multiples", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._slope_average = None
self._slope_std_dev = None
self._prev_atr = None
self._stop_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(atr_slope_breakout_strategy, self).OnReseted()
self._slope_average = None
self._slope_std_dev = None
self._prev_atr = None
self._stop_price = 0.0
def OnStarted2(self, time):
super(atr_slope_breakout_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
self._slope_average = SimpleMovingAverage()
self._slope_average.Length = self._slope_period.Value
self._slope_std_dev = StandardDeviation()
self._slope_std_dev.Length = self._slope_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
atr_value = float(atr_value)
if self._prev_atr is None:
self._prev_atr = atr_value
return
slope = atr_value - self._prev_atr
self._prev_atr = atr_value
avg_slope = float(process_float(self._slope_average, slope, candle.ServerTime, True))
std_slope = float(process_float(self._slope_std_dev, slope, candle.ServerTime, True))
if not self._slope_average.IsFormed or not self._slope_std_dev.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._check_stop(candle):
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
stop_distance = float(self._stop_loss_atr_multiplier.Value) * atr_value
# ATR has no direction, so the breakout candle decides the side.
if slope > avg_slope + float(self._breakout_multiplier.Value) * std_slope:
if close > open_price and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - stop_distance if stop_distance > 0 else 0.0
return
if close < open_price and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + stop_distance if stop_distance > 0 else 0.0
return
if self.Position != 0 and slope < avg_slope:
self._exit_position()
def _check_stop(self, candle):
if self._stop_price == 0.0:
return False
if (self.Position > 0 and float(candle.LowPrice) <= self._stop_price) or \
(self.Position < 0 and float(candle.HighPrice) >= self._stop_price):
self._exit_position()
return True
return False
def _exit_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
self._stop_price = 0.0
def CreateClone(self):
return atr_slope_breakout_strategy()