fix: apply Ganymede review fixes to portfolio code
Some checks are pending
CI / lint-and-test (push) Waiting to run

dashboard_portfolio.py:
- datetime.utcnow() → datetime.now(timezone.utc) (deprecation fix)
- days parameter validation with try/except + min(..., 365) on 2 endpoints

fetch_coins.py:
- isinstance(chain, str) guard prevents AttributeError on string chain values
- Log when adjusted market cap differs from DexScreener value

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
m3taversal 2026-04-20 17:00:02 +01:00
parent ddf3c25e88
commit 5071ecef16
2 changed files with 16 additions and 4 deletions

View file

@ -8,7 +8,7 @@ import json
import sqlite3 import sqlite3
import logging import logging
from html import escape as esc from html import escape as esc
from datetime import datetime from datetime import datetime, timezone
from aiohttp import web from aiohttp import web
from shared_ui import render_page from shared_ui import render_page
@ -304,7 +304,7 @@ async def handle_portfolio_page(request):
d['nav_per_token'] = nav d['nav_per_token'] = nav
d['price_nav_ratio'] = ratio d['price_nav_ratio'] = ratio
coins.append(d) coins.append(d)
now = datetime.utcnow() now = datetime.now(timezone.utc)
html = render_portfolio_page(coins, now) html = render_portfolio_page(coins, now)
return web.Response(text=html, content_type='text/html') return web.Response(text=html, content_type='text/html')
finally: finally:
@ -315,7 +315,10 @@ async def handle_nav_ratios(request):
"""Server-side computed NAV ratios — only returns dates with valid data.""" """Server-side computed NAV ratios — only returns dates with valid data."""
conn = _get_db(request) conn = _get_db(request)
try: try:
days = int(request.query.get('days', '90')) try:
days = min(int(request.query.get('days', '90')), 365)
except (ValueError, TypeError):
days = 90
rows = conn.execute(""" rows = conn.execute("""
SELECT name, snapshot_date, price_usd, treasury_multisig_usd, SELECT name, snapshot_date, price_usd, treasury_multisig_usd,
lp_usdc_total, adjusted_circulating_supply lp_usdc_total, adjusted_circulating_supply
@ -355,7 +358,10 @@ async def handle_nav_ratios(request):
async def handle_portfolio_history(request): async def handle_portfolio_history(request):
conn = _get_db(request) conn = _get_db(request)
try: try:
days = int(request.query.get('days', '90')) try:
days = min(int(request.query.get('days', '90')), 365)
except (ValueError, TypeError):
days = 90
rows = conn.execute(""" rows = conn.execute("""
SELECT * FROM coin_snapshots SELECT * FROM coin_snapshots
WHERE snapshot_date >= date('now', ? || ' days') WHERE snapshot_date >= date('now', ? || ' days')

View file

@ -92,6 +92,8 @@ def load_ownership_coins():
continue continue
chain = fm.get("chain") or {} chain = fm.get("chain") or {}
if isinstance(chain, str):
chain = {}
raise_data = fm.get("raise") or {} raise_data = fm.get("raise") or {}
ops = fm.get("operations") or {} ops = fm.get("operations") or {}
liq = fm.get("liquidation") or {} liq = fm.get("liquidation") or {}
@ -563,8 +565,12 @@ def compute_derived(row, coin):
if adj_circ and adj_circ > 0: if adj_circ and adj_circ > 0:
row["effective_liq_price"] = cash_total / adj_circ row["effective_liq_price"] = cash_total / adj_circ
if price and price > 0: if price and price > 0:
original_mcap = row.get("market_cap_usd")
row["market_cap_usd"] = price * adj_circ row["market_cap_usd"] = price * adj_circ
mcap = row["market_cap_usd"] mcap = row["market_cap_usd"]
if original_mcap and abs(mcap - original_mcap) > 1:
logger.debug("%s: adjusted mcap $%.0f (was $%.0f, protocol_owned=%s)",
row.get("name", "?"), mcap, original_mcap, protocol_tokens)
if price and price > 0 and row.get("effective_liq_price"): if price and price > 0 and row.get("effective_liq_price"):
row["delta_pct"] = ((row["effective_liq_price"] / price) - 1) * 100 row["delta_pct"] = ((row["effective_liq_price"] / price) - 1) * 100