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
Executable
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Order-book ladder for one material at one exchange: price levels with cumulative units and value.
tools/book.py BHP --cx AI1 --levels 8
"""
import argparse, sys
from collections import defaultdict
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from puga import config, fio
def ladder(orders, reverse):
lv = defaultdict(int)
for o in orders:
if o.get("ItemCount") != 0:
lv[o["ItemCost"]] += 10**9 if o.get("ItemCount") is None else o["ItemCount"] # 10**9 = market maker, unlimited
cum, out = 0, []
for price in sorted(lv, reverse=reverse):
cum += lv[price]
out.append((price, lv[price], cum))
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("ticker")
ap.add_argument("--cx", default=config.DEFAULT_CX)
ap.add_argument("--levels", type=int, default=8)
ap.add_argument("--refresh", action="store_true")
a = ap.parse_args()
t = a.ticker.upper()
ob = fio.order_book(t, a.cx, a.refresh)
asks, bids = ladder(ob["SellingOrders"], False), ladder(ob["BuyingOrders"], True)
print(f"{t}.{a.cx} ask {ob.get('Ask')} bid {ob.get('Bid')} supply {ob.get('Supply')} demand {ob.get('Demand')} "
f"MMBuy {ob.get('MMBuy')} MMSell {ob.get('MMSell')}")
print(f"{'ASKS':>8} {'units':>7} {'cum':>7} | {'BIDS':>8} {'units':>7} {'cum':>7}")
for i in range(a.levels):
l = f"{asks[i][0]:8.0f} {asks[i][1]:7d} {asks[i][2]:7d}" if i < len(asks) else " " * 24
r = f"{bids[i][0]:8.0f} {bids[i][1]:7d} {bids[i][2]:7d}" if i < len(bids) else ""
print(f"{l} | {r}")
if __name__ == "__main__":
main()