Estrategia ATR Trailing Stops
ATR Trailing usa un múltiplo del average true range para arrastrar stops detrás de las posiciones abiertas. Las entradas ocurren cuando el precio cruza una media móvil, y el stop de seguimiento se ajusta con la volatilidad.
Las pruebas indican un rendimiento anual promedio de aproximadamente 157%. Funciona mejor en el mercado de criptomonedas.
A medida que el precio avanza, el stop se desplaza hacia arriba (o hacia abajo) basándose en la última lectura de ATR, sin retroceder nunca. Esto bloquea las ganancias mientras persiste la tendencia.
Las salidas ocurren cuando se activa el stop de seguimiento o cuando el precio cruza de vuelta a través de la media móvil.
Detalles
- Criterios de entrada: Precio por encima o por debajo de la MA.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Stop de seguimiento activado o precio cruza la MA.
- Stops: Sí.
- Valores predeterminados:
AtrPeriod= 14AtrMultiplier= 3.0mMAPeriod= 20CandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: ATR, MA
- 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>
/// Strategy that uses ATR for trailing stop management.
/// It enters positions using a simple moving average and manages exits with a dynamic
/// trailing stop calculated as a multiple of ATR.
/// </summary>
public class AtrTrailingStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _entryPrice;
private decimal _trailingStopLevel;
private int _cooldown;
/// <summary>
/// Period for ATR calculation.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier for trailing stop calculation.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Period for Moving Average calculation for entry.
/// </summary>
public int MAPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Type of candles used for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize the ATR Trailing strategy.
/// </summary>
public AtrTrailingStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "Period for ATR calculation", "Indicators")
.SetOptimize(7, 21, 7);
_atrMultiplier = Param(nameof(AtrMultiplier), 3.0m)
.SetDisplay("ATR Multiplier", "ATR multiplier for trailing stop", "Risk")
.SetOptimize(2.0m, 4.0m, 0.5m);
_maPeriod = Param(nameof(MAPeriod), 20)
.SetDisplay("MA Period", "Period for Moving Average calculation for entry", "Indicators")
.SetOptimize(10, 50, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = default;
_trailingStopLevel = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_trailingStopLevel = 0;
_cooldown = 0;
var atr = new AverageTrueRange { Length = AtrPeriod };
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var trailingStopDistance = atrValue * AtrMultiplier;
if (_cooldown > 0)
{
_cooldown--;
return;
}
if (Position == 0)
{
if (candle.ClosePrice > smaValue)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_trailingStopLevel = _entryPrice - trailingStopDistance;
_cooldown = CooldownBars;
}
else if (candle.ClosePrice < smaValue)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_trailingStopLevel = _entryPrice + trailingStopDistance;
_cooldown = CooldownBars;
}
}
else if (Position > 0)
{
var newTrailingStopLevel = candle.ClosePrice - trailingStopDistance;
if (newTrailingStopLevel > _trailingStopLevel)
_trailingStopLevel = newTrailingStopLevel;
if (candle.LowPrice <= _trailingStopLevel)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (Position < 0)
{
var newTrailingStopLevel = candle.ClosePrice + trailingStopDistance;
if (newTrailingStopLevel < _trailingStopLevel || _trailingStopLevel == 0)
_trailingStopLevel = newTrailingStopLevel;
if (candle.HighPrice >= _trailingStopLevel)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
}
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
from StockSharp.Algo.Strategies import Strategy
class atr_trailing_strategy(Strategy):
"""
Strategy that uses ATR for trailing stop management.
Enters positions using a simple moving average and manages exits with a dynamic
trailing stop calculated as a multiple of ATR.
"""
def __init__(self):
super(atr_trailing_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14).SetDisplay("ATR Period", "Period for ATR calculation", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 3.0).SetDisplay("ATR Multiplier", "ATR multiplier for trailing stop", "Risk")
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for Moving Average calculation for entry", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._entry_price = 0.0
self._trailing_stop_level = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(atr_trailing_strategy, self).OnReseted()
self._entry_price = 0.0
self._trailing_stop_level = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(atr_trailing_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._trailing_stop_level = 0.0
self._cooldown = 0
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(atr, sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, atr_val, sma_val):
if candle.State != CandleStates.Finished:
return
av = float(atr_val)
sv = float(sma_val)
close = float(candle.ClosePrice)
trailing_dist = av * float(self._atr_multiplier.Value)
cd = self._cooldown_bars.Value
if self._cooldown > 0:
self._cooldown -= 1
return
if self.Position == 0:
if close > sv:
self.BuyMarket()
self._entry_price = close
self._trailing_stop_level = self._entry_price - trailing_dist
self._cooldown = cd
elif close < sv:
self.SellMarket()
self._entry_price = close
self._trailing_stop_level = self._entry_price + trailing_dist
self._cooldown = cd
elif self.Position > 0:
new_level = close - trailing_dist
if new_level > self._trailing_stop_level:
self._trailing_stop_level = new_level
if float(candle.LowPrice) <= self._trailing_stop_level:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0:
new_level = close + trailing_dist
if new_level < self._trailing_stop_level or self._trailing_stop_level == 0:
self._trailing_stop_level = new_level
if float(candle.HighPrice) >= self._trailing_stop_level:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return atr_trailing_strategy()