0bps

Strategy vault / Trend following

Adaptive Price Channel Strategy

This strategy is an adaptive price channel strategy based on the Average True Range (ATR) indicator and Average Directional Index (ADX). It aims to identify sideways markets and trends in price movements and make…

Author
ChaoZhang
Complexity
Starter · 64 lines
Parameters
5
Backtest bar
1h · intraday
Tested on
BTC/USDT · futures

IMG

Overview

This strategy is an adaptive price channel strategy based on the Average True Range (ATR) indicator and Average Directional Index (ADX). It aims to identify sideways markets and trends in price movements and make trades accordingly.

Strategy Logic

  1. Calculate the highest high (HH) and lowest low (LL) over a given length. Also calculate ATR over the same length.

  2. Calculate +DI and -DI based on upward and downward price moves. Then calculate ADX.

  3. If ADX < 25, the market is considered sideways. If close > upper channel (HH - ATR multiplier * ATR), go long. If close < lower channel (LL + ATR multiplier * ATR), go short.

  4. If ADX >= 25 and +DI > -DI, market is bullish. If close > upper channel, go long.

  5. If ADX >= 25 and +DI < -DI, market is bearish. If close < lower channel, go short.

  6. Exit position after exit_length bars since entry.

Advantage Analysis

  1. The strategy adapts automatically based on market conditions, using channel strategy in sideways market and trend following in trending market.

  2. Using ATR and ADX ensures adaptiveness. ATR adjusts channel width, ADX determines trend.

  3. Forced exit adds stability.

Risk Analysis

  1. ADX can generate false signals frequently.

  2. Poor ATR and ADX parameters lead to bad performance.

  3. Unable to effectively guard against black swan events.

Optimization Directions

  1. Optimize parameters for ATR and ADX to improve adaptability.

  2. Add stop loss to limit losses.

  3. Add filters to avoid false signals.

Conclusion

The strategy combines indicators and mechanisms to adapt across market conditions. But misjudgements can happen due to indicator limitations. Future optimizations on parameters and risk control.

Parameters

NameDefaultWhat it controls
v_input_120Length
v_input_210Exit After X Periods
v_input_33.2ATR Multiplier
v_input_4timestamp(2019-01-15T08:15:15+00:00)Start Date
v_input_5timestamp(2033-04-01T08:15:00+00:00)End Date

Source Pine Script

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

//@version=5
strategy("Adaptive Price Channel Strategy", overlay=true)

length = input(20, title="Length")
exit_length = input(10, title="Exit After X Periods")
atr_multiplier = input(3.2, title="ATR Multiplier")

startDate = input(defval = timestamp("2019-01-15T08:15:15+00:00"), title = "Start Date")
endDate = input(defval = timestamp("2033-04-01T08:15:00+00:00"), title = "End Date")

hh = ta.highest(high, length)
ll = ta.lowest(low, length)
atr = ta.atr(length)

// calculate +DI and -DI
upMove = high - high[1]
downMove = low[1] - low
plusDM = na(upMove[1]) ? na : (upMove > downMove and upMove > 0 ? upMove : 0)
minusDM = na(downMove[1]) ? na : (downMove > upMove and downMove > 0 ? downMove : 0)
plusDI = ta.rma(plusDM, length) / atr * 100
minusDI = ta.rma(minusDM, length) / atr * 100

// calculate ADX
dx = math.abs(plusDI - minusDI) / (plusDI + minusDI) * 100
adx = ta.rma(dx, length)

var int barSinceEntry = na

if (not na(close[length]) )
    if (adx < 25) // Sideways market
        if (close > hh - atr_multiplier * atr)
            strategy.entry("PChLE", strategy.long, comment="PChLE")
            barSinceEntry := 0
        else if (close < ll + atr_multiplier * atr)
            strategy.entry("PChSE", strategy.short, comment="PChSE")
            barSinceEntry := 0
    else if (adx >= 25 and plusDI > minusDI) // Bullish market
        if (close > hh - atr_multiplier * atr)
            strategy.entry("PChLE", strategy.long, comment="PChLE")
            barSinceEntry := 0
    else if (adx >= 25 and plusDI < minusDI) // Bearish market
        if (close < ll + atr_multiplier * atr)
            strategy.entry("PChSE", strategy.short, comment="PChSE")
            barSinceEntry := 0

if (na(barSinceEntry))
    barSinceEntry := barSinceEntry[1] + 1
else if (barSinceEntry >= exit_length)
    strategy.close("PChLE")
    strategy.close("PChSE")
    barSinceEntry := na

plot(hh, title="Highest High", color=color.green, linewidth=2)
plot(ll, title="Lowest Low", color=color.red, linewidth=2)

Published by ChaoZhang on the FMZ strategy square · last modified 2023-12-04. 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-Price-Channel-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