0bps

Strategy vault / Breakout

Adaptive ATR Trailing Stop Loss Strategy

This strategy is named "Adaptive ATR Trailing Stop Loss Strategy". It uses the ATR indicator to set stop loss levels, and switches from a tight stop to a loose stop after entry to follow trends while controlling…

Author
ChaoZhang
Complexity
Intermediate · 140 lines
Parameters
10
Backtest bar
1h · intraday
Tested on
BTC/USDT · futures

This strategy is named "Adaptive ATR Trailing Stop Loss Strategy". It uses the ATR indicator to set stop loss levels, and switches from a tight stop to a loose stop after entry to follow trends while controlling risk.

The specific logic is:

  1. Calculate the range of highest and lowest prices over a certain period as entry signal. Entries are triggered when prices break out of the range.

  2. After entry, a tighter ATR stop is initially used, fixed at 1.5 times the ATR value, to limit post-entry loss.

  3. During trade holding, the stop is switched to a looser 4 times ATR. The stop keeps trailing prices but allows more space for trends to extend.

  4. The stop level always tracks the lowest price (long trade) or highest price (short trade) and adjusts with price fluctuations, achieving a trailing stop effect.

  5. When price drops below stop level (long) or rises above it (short), the stop loss is triggered.

The advantage of this strategy is using an adaptive stop loss mechanism to ensure risk control while avoiding premature stop outs. But ATR parameters and multiples need optimization, and stops should be used with trend analysis.

In conclusion, dynamic trailing stops are important means to improve profitability. Flexible stop loss application can better maintain trend profits and control risks.

Parameters

NameDefaultWhat it controls
v_input_1100(?Entry strategy)Entry band bar count
v_input_224(?ATR SL)Trailing stop ATR Length
v_input_31.5Trailing stop ATR Multiple on tight
v_input_44Trailing stop ATR Multiple on slack
v_input_52016(?Backtesting range)From Year
v_input_6trueFrom Month
v_input_7trueFrom Day
v_input_82021To Year
v_input_94To Month
v_input_105To Day

Source Pine Script

/*backtest
start: 2023-08-13 00:00:00
end: 2023-09-12 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//@version=4
//@author=Takazudo

strategy("ATR trailing SL tight to slack [Takazudo]",
  overlay=true,
  default_qty_type=strategy.fixed,
  initial_capital=0,
  currency=currency.USD)


posSize = strategy.position_size
hasNoPos = posSize == 0
hasLongPos = posSize > 0
hasShortPos = posSize < 0

//============================================================================
// consts, inputs
//============================================================================

// colors

var COLOR_SL_LINE = color.new(#e0f64d, 20)
var COLOR_SL_LINE_THIN = color.new(#e0f64d, 90)
var COLOR_ENTRY_BAND = color.new(#43A6F5, 30)
var COLOR_TRANSPARENT = color.new(#000000, 100)

// Entry strategy

_g1 = 'Entry strategy'
var config_entryBandBars = input(defval = 100, title = "Entry band bar count",  minval=1, group=_g1)

_g2 = 'ATR SL'
var config_slAtr_length = input(24, title = "Trailing stop ATR Length", group=_g2)
var config_slAtr_multi1 = input(1.5, title = "Trailing stop ATR Multiple on tight", type=input.float, step=0.1, group=_g2)
var config_slAtr_multi2 = input(4, title = "Trailing stop ATR Multiple on slack", type=input.float, step=0.1, group=_g2)

_g3 = 'Backtesting range'
var config_fromYear  = input(defval = 2016, title = "From Year",  minval = 1970, group=_g3)
var config_fromMonth = input(defval = 1,    title = "From Month", minval = 1, maxval = 12, group=_g3)
var config_fromDay   = input(defval = 1,    title = "From Day",   minval = 1, maxval = 31, group=_g3)
var config_toYear  = input(defval = 2021, title = "To Year",  minval = 1970, group=_g3)
var config_toMonth = input(defval = 4,    title = "To Month", minval = 1, maxval = 12, group=_g3)
var config_toDay   = input(defval = 5,    title = "To Day",   minval = 1, maxval = 31, group=_g3)

//============================================================================
// Range Edge calculation
//============================================================================

f_calcEntryBand_high() =>
    _highest = max(open[3], close[3])
    for i = 4 to (config_entryBandBars - 1)
        _highest := max(_highest, open[i], close[i])
    _highest

f_calcEntryBand_low() =>
    _lowest = min(open[3], close[3])
    for i = 4 to (config_entryBandBars - 1)
        _lowest := min(_lowest, open[i], close[i])
    _lowest

entryBand_high = f_calcEntryBand_high()
entryBand_low = f_calcEntryBand_low()
entryBand_height = entryBand_high - entryBand_low

plot(entryBand_high, color=COLOR_ENTRY_BAND, linewidth=1)
plot(entryBand_low, color=COLOR_ENTRY_BAND, linewidth=1)

rangeBreakDetected_long = entryBand_high < close
rangeBreakDetected_short = entryBand_low > close

shouldMakeEntryLong = (strategy.position_size == 0) and rangeBreakDetected_long
shouldMakeEntryShort = (strategy.position_size == 0) and rangeBreakDetected_short

//============================================================================
// ATR based stuff
//============================================================================

sl_atrHeight_tight = atr(config_slAtr_length) * config_slAtr_multi1
sl_atrHeight_slack = atr(config_slAtr_length) * config_slAtr_multi2

sl_tight_bull = min(open, close) - sl_atrHeight_tight
sl_tight_bear = max(open, close) + sl_atrHeight_tight
sl_slack_bull = min(open, close) - sl_atrHeight_slack
sl_slack_bear = max(open, close) + sl_atrHeight_slack

plot(sl_tight_bull, color=COLOR_SL_LINE_THIN, transp=0, linewidth=1)
plot(sl_tight_bear, color=COLOR_SL_LINE_THIN, transp=0, linewidth=1)
plot(sl_slack_bull, color=COLOR_SL_LINE_THIN, transp=0, linewidth=1)
plot(sl_slack_bear, color=COLOR_SL_LINE_THIN, transp=0, linewidth=1)

//============================================================================
// Sl
//============================================================================

var trailingSl_long = hl2
var trailingSl_short = hl2

trailingSl_long := if hasLongPos
    max(trailingSl_long, sl_slack_bull)
else
    sl_tight_bull

trailingSl_short := if hasShortPos
    min(trailingSl_short, sl_slack_bear)
else
    sl_tight_bear

color_sl_long = hasLongPos ? COLOR_SL_LINE : COLOR_TRANSPARENT
color_sl_short = hasShortPos ? COLOR_SL_LINE : COLOR_TRANSPARENT

plot(trailingSl_long, color=color_sl_long, transp=0, linewidth=2)
plot(trailingSl_short, color=color_sl_short, transp=0, linewidth=2)


//============================================================================
// make entries
//============================================================================

// Calculate start/end date and time condition
startDate  = timestamp(config_fromYear, config_fromMonth, config_fromDay, 00, 00)
finishDate = timestamp(config_toYear,   config_toMonth,   config_toDay,   00, 00)

if (true)
    if shouldMakeEntryLong
        strategy.entry(id="Long", long=true, stop=close)
    if shouldMakeEntryShort
        strategy.entry(id="Short", long=false, stop=close)

strategy.exit('Long-SL/TP', 'Long', stop=trailingSl_long)
strategy.exit('Short-SL/TP', 'Short', stop=trailingSl_short)

Published by ChaoZhang on the FMZ strategy square · last modified 2023-09-13. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.

Build this with AgenKit

A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/Adaptive-ATR-Trailing-Stop-Loss-Strategy.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. Get tests, backtest, risk controls and a paper-trading switch

Understand it first

Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.

Related specs

esc

Try , , or .

↑↓ move↵ open