Initial PuGa toolkit: data layer, econ, depth-aware scan, state sync, plan push

- puga/: cached FIO + PRUNplanner clients, market view with order-book walk,
  econ formulas ported from PRUNplanner (tested against its suite and live FIO),
  saturation model v1 (reviewed by Opus)
- tools/: scan (depth-aware), price, book, chain, state sync, plan_push
  (dry run default, [PuGa]-prefixed plans only), legacy prun_scan/prun_cxarb
- docs/: mechanics (PRUNplanner is source of truth), roadmap, decisions,
  saturation design, archived handoff
- secrets stay in .env (gitignored); ref/ holds PRUNplanner source (ignored)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 23:00:21 +02:00
co-authored by Claude Sonnet 5
commit 7a538cb300
35 changed files with 1692 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
"""Tiny disk cache for JSON over HTTP, keyed by URL, expiry by TTL seconds."""
import hashlib, json, time, urllib.request
from .config import CACHE_DIR
UA = "PuGa/0.1 (personal advisory toolkit)"
def get_json(url: str, ttl: int, headers: dict[str, str] | None = None, refresh: bool = False):
CACHE_DIR.mkdir(parents=True, exist_ok=True)
f = CACHE_DIR / (hashlib.sha1(url.encode()).hexdigest()[:16] + ".json")
if not refresh and f.exists() and time.time() - f.stat().st_mtime < ttl:
return json.loads(f.read_text())
req = urllib.request.Request(url, headers={"User-Agent": UA, **(headers or {})})
with urllib.request.urlopen(req, timeout=60) as r:
data = json.load(r)
f.write_text(json.dumps(data))
return data