From fb590105ce6cd6bf4a497a6b2607d828724d12e7 Mon Sep 17 00:00:00 2001 From: BizzleBot Date: Sat, 21 Mar 2026 22:55:37 +0000 Subject: [PATCH] fix: preserve ATH/Mayer/200D SMA when CoinGecko rate-limits - ATH: fall back to cached value when fetch fails - 200D SMA: compute from history.json when CoinGecko blocks us - Mayer Multiple: derived from 200D SMA fallback - Drawdown: preserve cached value on ATH fetch failure - Fixes N/A Drawdown and -- header stats after quick refresh --- dashboard/server.py | 31 +++++++++++++++++++++++++++---- data/score_history.jsonl | 5 +++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/dashboard/server.py b/dashboard/server.py index 787a8a8..e026c1a 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -126,18 +126,41 @@ def run_scrape(force_full=False): log.info("Fetching BTC ATH...") ath_data = price.fetch_ath() - if price_current.get("price") and ath_data.get("ath"): - drawdown = price.calculate_drawdown(price_current["price"], ath_data["ath"]) - metrics["drawdown"] = {"value": drawdown, "ath": ath_data["ath"]} + ath_val = ath_data.get("ath") or existing_cache.get("drawdown", {}).get("ath") + if price_current.get("price") and ath_val: + drawdown = price.calculate_drawdown(price_current["price"], ath_val) + metrics["drawdown"] = {"value": drawdown, "ath": ath_val} + elif existing_cache.get("drawdown", {}).get("value") is not None: + log.info("ATH fetch failed — reusing cached drawdown") + metrics["drawdown"] = existing_cache["drawdown"] else: metrics["drawdown"] = {"value": None} - log.info("Fetching historical prices...") + log.info("Fetching historical prices for 200D SMA / Mayer...") hist = price.fetch_historical() if hist: sma_200d = price.calculate_200d_sma(hist) mayer = price.calculate_mayer_multiple(price_current.get("price"), sma_200d) metrics["price_extras"] = {"sma_200d": sma_200d, "mayer_multiple": mayer} + else: + # CoinGecko rate-limited — compute from history.json instead + try: + hist_path = os.path.join(DATA_DIR, "history.json") + with open(hist_path) as f: + hdata = json.load(f) + btc_vals = hdata.get("btc_price", {}).get("values", []) + if len(btc_vals) >= 200: + sma_200d = sum(btc_vals[-200:]) / 200 + cur_p = price_current.get("price") or btc_vals[-1] + mayer = cur_p / sma_200d if sma_200d else None + metrics["price_extras"] = {"sma_200d": sma_200d, "mayer_multiple": round(mayer, 4) if mayer else None} + log.info("Computed 200D SMA from history.json (CoinGecko rate-limited)") + elif existing_cache.get("price_extras"): + metrics["price_extras"] = existing_cache["price_extras"] + except Exception: + if existing_cache.get("price_extras"): + metrics["price_extras"] = existing_cache["price_extras"] + log.info("Reusing cached price_extras") # 3. On-chain metrics — use cached values (historical data is permanent) onchain_keys = ["puell_multiple", "mvrv_zscore", "reserve_risk", "rhodl_ratio", diff --git a/data/score_history.jsonl b/data/score_history.jsonl index dce7554..97b65a1 100644 --- a/data/score_history.jsonl +++ b/data/score_history.jsonl @@ -105,3 +105,8 @@ {"timestamp": "2026-03-21T22:41:18.262393+00:00", "composite_score": 71.0, "scored_count": 10, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": 8, "value": 44.20685279187817}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}} {"timestamp": "2026-03-21T22:41:46.036660+00:00", "composite_score": 71.0, "scored_count": 10, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": 8, "value": 44.21875}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}} {"timestamp": "2026-03-21T22:42:33.632103+00:00", "composite_score": 70.0, "scored_count": 9, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": null, "value": null}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}} +{"timestamp": "2026-03-21T22:51:08.461576+00:00", "composite_score": 71.0, "scored_count": 10, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": 8, "value": 44.24968274111675}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}} +{"timestamp": "2026-03-21T22:53:45.530567+00:00", "composite_score": 71.0, "scored_count": 10, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": 8, "value": 44.26713197969543}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}} +{"timestamp": "2026-03-21T22:54:22.144542+00:00", "composite_score": 70.0, "scored_count": 9, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": null, "value": null}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}} +{"timestamp": "2026-03-21T22:55:08.385540+00:00", "composite_score": 71.0, "scored_count": 10, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": 8, "value": 44.26554568527919}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}} +{"timestamp": "2026-03-21T22:55:33.933753+00:00", "composite_score": 71.0, "scored_count": 10, "metrics": {"fear_greed": {"score": 10, "value": 12}, "puell_multiple": {"score": 8, "value": 0.6602699608966011}, "mvrv_zscore": {"score": 8, "value": 0.5211180167687892}, "drawdown": {"score": 8, "value": 44.26316624365482}, "price_vs_200w_sma": {"score": 7, "value": 58895.78086828114}, "reserve_risk": {"score": 10, "value": 0.0012985709697654493}, "rhodl_ratio": {"score": 4, "value": 1230.6243545314708}, "nupl": {"score": 8, "value": 0.22243290955405431}, "lth_realized_price": {"score": 5, "value": 43346.58756410873}, "hash_ribbons": {"score": 3, "value": null}}}