feat: replace ML optimizer with on-chain accumulation zone monitor
Complete rewrite — replaces the ML-based signal optimizer with a transparent on-chain metric monitoring dashboard. Scrapes 10 metrics from LookIntoBitcoin (Playwright) and free APIs, scores each 0-10, composite 0-100. Metrics: Fear & Greed, Puell Multiple, MVRV Z-Score, Drawdown from ATH, Price vs 200W SMA, Reserve Risk, RHODL Ratio, NUPL, LTH Realized Price, Hash Ribbons. Auto-refreshes every 15 minutes. Settings page preserved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
aba30f7718
commit
62e32fc655
@@ -0,0 +1,80 @@
|
||||
"""BTC price data from CoinGecko API."""
|
||||
|
||||
import logging
|
||||
import requests
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
PRICE_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_24hr_change=true"
|
||||
HISTORY_URL = "https://api.coingecko.com/api/v3/coins/bitcoin/market_chart?vs_currency=usd&days=365"
|
||||
ATH_URL = "https://api.coingecko.com/api/v3/coins/bitcoin?localization=false&tickers=false&market_data=true&community_data=false&developer_data=false"
|
||||
|
||||
|
||||
def fetch_current():
|
||||
"""Fetch current BTC price and 24h change."""
|
||||
try:
|
||||
resp = requests.get(PRICE_URL, timeout=15)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
btc = data.get("bitcoin", {})
|
||||
return {
|
||||
"price": btc.get("usd"),
|
||||
"change_24h": btc.get("usd_24h_change"),
|
||||
}
|
||||
except Exception as e:
|
||||
log.error("Price fetch error: %s", e)
|
||||
return {"price": None, "error": str(e)}
|
||||
|
||||
|
||||
def fetch_historical():
|
||||
"""Fetch 365 days of BTC price history. Returns list of [timestamp, price]."""
|
||||
try:
|
||||
resp = requests.get(HISTORY_URL, timeout=30)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
prices = data.get("prices", [])
|
||||
return prices
|
||||
except Exception as e:
|
||||
log.error("Historical price fetch error: %s", e)
|
||||
return []
|
||||
|
||||
|
||||
def fetch_ath():
|
||||
"""Fetch BTC all-time high from CoinGecko."""
|
||||
try:
|
||||
resp = requests.get(ATH_URL, timeout=15)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
market = data.get("market_data", {})
|
||||
ath = market.get("ath", {}).get("usd")
|
||||
ath_change = market.get("ath_change_percentage", {}).get("usd")
|
||||
return {
|
||||
"ath": ath,
|
||||
"ath_change_pct": ath_change,
|
||||
}
|
||||
except Exception as e:
|
||||
log.error("ATH fetch error: %s", e)
|
||||
return {"ath": None, "error": str(e)}
|
||||
|
||||
|
||||
def calculate_200d_sma(prices):
|
||||
"""Calculate 200-day SMA from historical price data."""
|
||||
if not prices or len(prices) < 200:
|
||||
return None
|
||||
# prices is [[timestamp, price], ...]
|
||||
recent_200 = [p[1] for p in prices[-200:]]
|
||||
return sum(recent_200) / len(recent_200)
|
||||
|
||||
|
||||
def calculate_mayer_multiple(current_price, sma_200d):
|
||||
"""Mayer Multiple = current price / 200-day SMA."""
|
||||
if not current_price or not sma_200d or sma_200d == 0:
|
||||
return None
|
||||
return current_price / sma_200d
|
||||
|
||||
|
||||
def calculate_drawdown(current_price, ath):
|
||||
"""Drawdown from ATH as percentage."""
|
||||
if not current_price or not ath or ath == 0:
|
||||
return None
|
||||
return (ath - current_price) / ath * 100
|
||||
Reference in New Issue
Block a user