← Back to Home
A Simple ETH Trend Strategy With ADOSC + ATR Trailing Stop

A Simple ETH Trend Strategy With ADOSC + ATR Trailing Stop

This script backtests a long-only strategy on ETH-USD. Signals are generated on daily data, executed next day, and performance is summarized on weekly bars with a compact plotting dashboard.

What the strategy does

It buys only when (1) price is in an uptrend and (2) volume-based accumulation is positive. It exits with an ATR trailing stop. After a stop-out, it waits a few days (cooldown) before re-entering.

Key formulas (plain text)

Trend filter:

Close Location Value (CLV):

Accumulation/Distribution (AD) line:

ADOSC (fast/slow EMA of AD):

Base entry condition:

True Range:

ATR (EWMA, n=7):

Trailing stop (peak-based):

Execution:

Costs (per position change):

Python Codes

Download data (note the required .droplevel(1, 1)):

import numpy as np
import pandas as pd
import yfinance as yf

ticker = "ETH-USD"
df = yf.download(ticker, period="5y", auto_adjust=False).droplevel(1, 1)
df = df.dropna(subset=["Open","High","Low","Close","Volume"])

Compute trend + ADOSC:

fast, slow = 7, 30
ma_len = 30

hl = (df["High"] - df["Low"]).replace(0, np.nan)
clv = ((2*df["Close"] - df["High"] - df["Low"]) / hl).fillna(0.0)
ad = (clv * df["Volume"]).cumsum()
adosc = ad.ewm(span=fast, adjust=False).mean() - ad.ewm(span=slow, adjust=False).mean()
trend = df["Close"].rolling(ma_len).mean()

base = ((df["Close"] > trend) & (adosc > 0)).fillna(False)

ATR trailing stop + cooldown (signal-time):

atr_n = 7
atr_k = 2.0
cooldown_days = 5

prev_close = df["Close"].shift(1)
tr = pd.concat([
    (df["High"] - df["Low"]),
    (df["High"] - prev_close).abs(),
    (df["Low"] - prev_close).abs()
], axis=1).max(axis=1)
atr = tr.ewm(alpha=1/atr_n, adjust=False).mean()

pos_sig = np.zeros(len(df), dtype=int)
state, peak, stop, cool = 0, 0.0, 0.0, 0

close = df["Close"].values
base_v = base.values
atr_v = atr.bfill().values

for i in range(len(df)):
    if cool > 0:
        cool -= 1

    if state == 0:
        if (cool == 0) and base_v[i]:
            state = 1
            peak = close[i]
            stop = peak - atr_k * atr_v[i]
            pos_sig[i] = 1
        else:
            pos_sig[i] = 0
    else:
        if close[i] > peak:
            peak = close[i]
        stop = max(stop, peak - atr_k * atr_v[i])

        if close[i] <= stop:
            state, peak, stop, cool = 0, 0.0, 0.0, cooldown_days
            pos_sig[i] = 0
        else:
            if base_v[i]:
                pos_sig[i] = 1
            else:
                state, peak, stop = 0, 0.0, 0.0
                pos_sig[i] = 0

pos = pd.Series(pos_sig, index=df.index).shift(1).fillna(0)

Returns + weekly aggregation:

fee_bps, slip_bps = 10, 5
week_rule = "W-FRI"

ret_d = df["Close"].pct_change().fillna(0)
turnover_d = pos.diff().abs().fillna(0)
cost_d = turnover_d * ((fee_bps + slip_bps) / 10000.0)
strat_d = (pos * ret_d - cost_d).dropna()

strat_w_all = (1 + strat_d).resample(week_rule).prod() - 1
bh_w_all = (1 + ret_d).resample(week_rule).prod() - 1
eq_w = (1 + strat_w_all.fillna(0)).cumprod()
bh_eq_w = (1 + bh_w_all.fillna(0)).cumprod()
dd_w = eq_w / eq_w.cummax() - 1

Results

Pasted image 20260210150039.png
WEEKLY PERFORMANCE SUMMARY (ATR trailing stop + cooldown)
Ticker: ETH-USD | Period: 5y
Rule: Close > MA30 AND ADOSC(7,30) > 0 (next-bar)
Stop: ATR7 k: 2.0 | Cooldown days: 5

Weeks (calendar): 262
Weeks traded (any exposure): 146 | Share: 0.5572519083969466

Strategy Return (full timeline): 2.6514895804456473
Buy&Hold Return (full timeline): 0.1583242364916655

Sharpe (computed on traded weeks only): 1.0372536514048858
Trades (daily position changes): 128
Time in Market (daily avg): 0.38040503557744937
Max Drawdown (full timeline): -0.37655867199981163

Traded weeks per year:
      traded_weeks  total_weeks  pct_traded
year                                       
2021            30           47    0.638298
2022            19           52    0.365385
2023            35           52    0.673077
2024            34           52    0.653846
2025            25           52    0.480769
2026             3            7    0.428571

WEEKLY EXPECTATIONS (TRADED WEEKS ONLY)
Count traded weeks: 146
Mean: 0.012659494020072583
Median: -0.005878062896593261
Std: 0.0880102080122433
Min: -0.19651188772753625
Max: 0.27285900405969654
Ann mean approx: 0.6582936890437743
Win%: 0.4589041095890411 | Loss%: 0.541095890410959

Percentiles:
p01: -0.1372171226200642 p05: -0.09557667231073391 p10: -0.08272669553487838 p25: -0.05020301575408151 p75: 0.05762687686437301 p90: 0.1434738138814331 p95: 0.17320910329592715 p99: 0.23330036001805157

Worst 5 traded weeks:
Date
2021-05-21   -0.196512
2021-07-09   -0.141652
2021-09-10   -0.131797
2026-01-23   -0.110539
2022-07-15   -0.103816

Best 5 traded weeks:
Date
2025-05-09    0.272859
2021-05-07    0.256570
2021-09-03    0.204860
2024-05-24    0.202732
2025-07-18    0.199849