2026-07-26 23:17:41 +00:00

Bitcoin Accumulation Zone Monitor

Bitcoin on-chain metrics dashboard with classic equal-weight scoring, ML-optimized scoring, historical backtesting, and click-to-select metric context for long-term BTC accumulation decisions.

Dashboard

What It Does

Monitors Bitcoin accumulation conditions using 16 scored market/on-chain indicators plus optional informational cycle metrics. Each scored metric receives a 0-10 score and rolls into a 0-100 accumulation score.

The dashboard now supports two scoring modes:

  • Classic — transparent equal-weight scoring across every active metric.
  • ML — feature-importance weights trained against historical 365-day forward returns, with displayed per-metric weights and point contributions.

Historical backtests show score-vs-price behavior, score bracket performance, major signal events, and current-score context. Metric cards are clickable: selecting a metric overlays its historical series on the score chart and shows comparable historical periods with forward returns.

Screenshots

Main Dashboard — ML mode + metric context

Main Dashboard Live BTC price, Classic/ML scoring toggle, 16 active scored metrics, ML weights/contributions, metric sparklines, and click-to-select historical context.

Historical Backtest

Backtest Current signal percentile, comparable historical periods by cycle, score-vs-BTC chart, bracket performance, and major signal events.

Settings

Settings LLM provider configuration for optional AI-powered signal commentary and local/cloud model selection.

Feature Highlights

  • 16 scored metrics from market sentiment, miner stress, valuation, holder behavior, network activity, and velocity signals.
  • Classic vs ML scoring toggle on the dashboard and backtest API.
  • ML score explainability: metric cards show learned weight and contribution in points.
  • Leakage-resistant ML validation: training uses purged time-series splits so 365-day forward-return labels do not overlap validation windows.
  • Historical context panel: compares the current composite score against historical periods and forward returns.
  • Clickable metric cards: select any metric to see percentile, similar historical levels, forward returns, example dates by market cycle, and highlighted chart periods.
  • Score history chart with BTC price overlay, range controls, and selected-metric overlay.
  • Backtest dashboard with current signal context, score bracket performance, and signal-crossing events.
  • Quick vs full refresh: quick refresh updates BTC price and Fear & Greed; full refresh re-scrapes on-chain sources.
  • LLM settings UI for Ollama, LM Studio, OpenAI, Anthropic, and OpenRouter.

Metrics

# Metric Source Accumulation Signal
1 Fear & Greed Index alternative.me API Extreme fear / capitulation sentiment
2 Puell Multiple LookIntoBitcoin Miner revenue stress
3 MVRV Z-Score LookIntoBitcoin Market near/below realized value
4 Drawdown from ATH Calculated from BTC price Deep correction from cycle high
5 Price vs 200W SMA LookIntoBitcoin + BTC price Price near/below long-term trend
6 Reserve Risk LookIntoBitcoin High holder confidence relative to price
7 RHODL Ratio LookIntoBitcoin Long-term holder dominance
8 Net Unrealized Profit/Loss (NUPL) LookIntoBitcoin Capitulation / early recovery zones
9 LTH Realized Price LookIntoBitcoin Price near long-term holder cost basis
10 Hash Ribbons LookIntoBitcoin Miner capitulation/recovery signal
11 SOPR CheckOnChain Spent outputs near loss / reset territory
12 Sell-side Risk Ratio CheckOnChain Low realized profit/loss pressure
13 Active Address Momentum CheckOnChain Network activity momentum extremes
14 Transaction Count Momentum CheckOnChain Transaction activity momentum extremes
15 NVT Price CheckOnChain Network-value valuation discount/premium
16 VDD Multiple CheckOnChain Coin-days/velocity reset conditions

Informational cards may also appear when data is available, such as Long-Term Holder Supply and Pi Cycle Bottom. These are displayed for context and are not included in the composite score.

Score Interpretation

Score Assessment Interpretation
80-100 🟢 Extreme Accumulation Zone Broad capitulation/value conditions across active metrics
65-79 🟢 Strong Accumulation Zone Historically attractive long-term entry territory
50-64 🟡 Moderate Opportunity DCA-friendly, but not maximum-signal conditions
35-49 🟡 Neutral Mixed signals; not compelling either direction
20-34 🔴 Caution — Overheated Market conditions becoming less favorable
0-19 🔴 Extreme Caution Historically poor accumulation setup

Backtest tables provide actual historical forward-return statistics per score bracket, including 30d/90d/180d/1yr averages, win rate, max gain/loss, and average max drawdown.

ML-Optimized Scoring

The ML mode uses a GradientBoostingClassifier trained on historical feature rows to predict whether a day was a good long-term buy based on 365-day forward return. Training features include:

  • Classic metric scores.
  • Raw metric values.
  • 30-day metric deltas.
  • Interaction features such as MVRV × NUPL and Puell × Reserve Risk.
  • Cycle-position context such as days since ATH.

The resulting feature importances are aggregated back into transparent metric weights stored in config/ml_weights.json. The UI displays normalized weight and contribution for each active metric.

Validation uses purged expanding time-series splits: because each label uses a 365-day forward-return window, training rows whose label windows overlap validation are removed before scoring validation folds.

Tech Stack

Component Technology
Backend Python 3.13 + FastAPI
Frontend Inline HTML/CSS/JS dark trading-terminal UI
Charts Chart.js
Scraping requests + Playwright-style browser scraping where needed
Data APIs alternative.me, CoinGecko, LookIntoBitcoin, CheckOnChain
ML NumPy + pandas + scikit-learn GradientBoostingClassifier
Process Manager pm2 or uvicorn
Default Port 3088

How Data Is Collected

Data is collected from free/public sources and cached locally under data/.

  • Fast live refreshes update BTC price, ATH/drawdown, 200D SMA/Mayer where possible, and Fear & Greed.
  • On-chain metrics are cached and reused because they update slowly.
  • Full refresh re-scrapes on-chain metrics from LookIntoBitcoin/CheckOnChain.
  • Historical backtest data lives in data/history.json and supports charting, backtests, and metric-context lookups.
  • Score history appends to data/score_history.jsonl.

Project Structure

├── dashboard/
│   └── server.py                  # FastAPI server + inline dashboard/backtest/settings UI
├── scrapers/
│   ├── lookintobitcoin.py         # LookIntoBitcoin metric scraping
│   ├── checkonchain.py            # CheckOnChain metric scraping
│   ├── history_collector.py       # Full historical data collection
│   ├── history_updater.py         # Incremental historical updates
│   ├── fear_greed.py              # Fear & Greed Index API
│   └── price.py                   # BTC price, ATH, drawdown, SMA helpers
├── scoring/
│   └── engine.py                  # Classic + ML-weighted scoring logic
├── backtesting/
│   └── engine.py                  # Historical backtest engine
├── ml/
│   └── optimizer.py               # ML training, purged CV, weight export
├── tests/
│   ├── test_ml_optimizer_validation.py
│   └── test_scoring_engine_ml.py
├── data/
│   ├── cache.json                 # Live metric cache
│   ├── history.json               # Historical metric/time-series data
│   └── score_history.jsonl        # Live score history
├── config/
│   ├── thresholds.json            # Classic scoring thresholds
│   ├── ml_weights.json            # Learned ML metric weights
│   └── llm_settings.json          # Optional AI commentary provider config
├── screenshots/                   # README screenshots
├── scripts/run.sh                 # Locked local launcher with Playwright path
├── .gitea/workflows/ci.yml        # Gitea Actions test/compile gates
├── Dockerfile                     # Non-root Chromium-enabled image
├── docker-compose.yml             # Port, healthcheck, restart, persistent volumes
├── pyproject.toml                 # Runtime, ML, and development dependency groups
├── uv.lock                        # Exact reproducible dependency resolution
├── ARCHITECTURE.md
└── README.md

Reproducible Setup

Install uv and use Python 3.11-3.13. Dependencies are declared in explicit runtime, ml, and dev groups in pyproject.toml; exact cross-platform resolutions are committed in uv.lock.

git clone <repository-url>
cd btc-accumulation-monitor
uv sync --locked --group runtime --group ml --group dev

Install the Chromium binary once for full on-chain refreshes. Keep its path explicit so installation and runtime use the same browser cache:

export PLAYWRIGHT_BROWSERS_PATH="$PWD/.playwright"
uv run --frozen playwright install chromium

requirements_vps.txt is a lock-derived, hash-pinned compatibility export for pip-based hosts. pyproject.toml and uv.lock remain authoritative; regenerate the compatibility file after dependency changes with:

uv export --frozen --no-dev --group runtime --group ml \
  --no-emit-project --no-header --output-file requirements_vps.txt

Running

The executable launcher fixes PYTHONPATH, preserves an explicitly supplied PLAYWRIGHT_BROWSERS_PATH, and starts port 3088 from the locked environment:

./scripts/run.sh

Equivalent exact command:

PLAYWRIGHT_BROWSERS_PATH="$PWD/.playwright" PYTHONPATH=. \
  uv run --frozen --no-dev --group runtime --group ml \
  python -m uvicorn dashboard.server:app --host 0.0.0.0 --port 3088

Then visit http://localhost:3088.

Container Deployment

The image uses a multi-architecture Python base, installs Playwright Chromium and its OS libraries during the build, and runs the application as non-root UID 10001. Compose publishes port 3088, restarts unless stopped, and persists /app/data and /app/config in named volumes.

docker compose build
docker compose up -d

The Docker and Compose healthchecks probe GET /health/live. The deployment must include the reliability revision that supplies that endpoint; without it, Docker correctly reports the container unhealthy even if the older application server is accepting requests.

Named volumes are initialized from the image on first use. Back up both before replacing or deleting them:

docker volume inspect btc-accumulation-monitor_btc-monitor-data
docker volume inspect btc-accumulation-monitor_btc-monitor-config

For bind-mounted deployments, ensure the host directories are writable by UID 10001 and do not replace config/ with an empty directory.

First Run and Data Freshness

  1. Visit http://localhost:3088 for the dashboard.
  2. Use Quick Refresh for price and Fear & Greed updates while retaining cached slow-moving on-chain metrics.
  3. Use Full Refresh when on-chain source data must be re-scraped; this requires the installed Playwright Chromium browser and external source availability.
  4. Visit http://localhost:3088/backtest for historical analysis.
  5. If historical data is missing, populate data/history.json through the existing collection flow.

Freshness is metric-specific. Price and sentiment APIs can update frequently, while public on-chain chart sources commonly update daily and may be reused from cache. A successful refresh is not proof that every upstream metric has a new observation. Check source timestamps/status exposed by the running revision, and treat missing, stale, or scrape-failed metrics as unavailable rather than silently current. data/ is operational state and should be persisted and backed up.

ML and Backtest Caveats

ML weights and backtest output are research artifacts, not investment advice or evidence of future performance. Any reported ML result must retain its provenance: source-data snapshot/range, feature and label definitions, training window, purge/embargo policy, code revision, dependency lock, random seed (when applicable), and generated weight/config artifact.

Model selection and threshold tuning must use training/validation data only. Report final performance on a genuinely untouched out-of-sample (OOS) period; do not describe in-sample fit, cross-validation used for selection, or the best result from repeated experiments as OOS. Forward-return labels require purging overlapping label horizons, but purged cross-validation alone does not create an untouched final test set. Results without reproducible provenance and a reserved OOS evaluation should be labeled exploratory.

Useful API Endpoints

Endpoint Description
GET /api/data?mode=classic Current metrics using equal-weight scoring
GET /api/data?mode=ml Current metrics using ML-optimized weights
GET /api/history Recent live score history
POST /api/refresh Quick refresh
POST /api/refresh?full=true Full on-chain refresh
GET /api/backtest?mode=classic Historical backtest with classic scoring
GET /api/backtest?mode=ml Historical backtest with ML scoring
GET /api/metric-context?metric=mvrv_zscore&mode=ml Similar historical levels and forward returns for one metric
GET /api/settings Safe LLM settings payload

Testing and CI

Run the committed test suite and the same static compilation gate used by Gitea Actions:

uv sync --locked --group runtime --group ml --group dev
uv run --frozen python -m compileall -q \
  dashboard scrapers scoring backtesting ml ml_engine llm_client scripts orchestrator.py
uv run --frozen pytest

.gitea/workflows/ci.yml runs lock validation/install, static compilation, and tests for pull requests and pushes to main.

Architecture

See ARCHITECTURE.md for deeper implementation details on scoring, data collection, and backtesting.

License

Private — not for public distribution.

S
Description
Bitcoin Accumulation Zone Monitor — On-chain metrics dashboard with historical backtesting for long-term BTC holders. Scores buying opportunities 0-100 using Puell Multiple, MVRV Z-Score, Fear & Greed, Reserve Risk, RHODL, NUPL, LTH Realized Price, 200W SMA, and more.
Readme
3.3 MiB
Languages
Python 99.5%
Shell 0.5%