"""Unified market view: FIO book totals merged with PRUNplanner VWAP/volume, plus order-book walks.""" from dataclasses import dataclass from . import fio, prunplanner as pp CXS = ["AI1", "NC1", "CI1", "IC1", "NC2", "CI2"] @dataclass class Quote: tk: str cx: str ask: float | None = None bid: float | None = None supply: float = 0 # units on sell side (order-book total) demand: float = 0 # units on buy side vwap7: float | None = None vwap30: float | None = None traded7: float = 0 # avg units/day over 7d traded30: float = 0 # avg units/day over 30d mm_buy: float | None = None mm_sell: float | None = None def snapshot(refresh=False) -> dict[tuple[str, str], Quote]: q: dict[tuple[str, str], Quote] = {} for e in fio.exchange_all(refresh): k = (e["MaterialTicker"], e["ExchangeCode"]) q[k] = Quote(k[0], k[1], e.get("Ask"), e.get("Bid"), e.get("Supply") or 0, e.get("Demand") or 0, mm_buy=e.get("MMBuy"), mm_sell=e.get("MMSell")) for e in pp.exchanges(refresh): k = (e["ticker"], e["exchange_code"]) x = q.setdefault(k, Quote(k[0], k[1], e.get("ask"), e.get("bid"), e.get("supply") or 0, e.get("demand") or 0)) x.vwap7, x.vwap30 = e.get("vwap_7d"), e.get("vwap_30d") x.traded7, x.traded30 = e.get("avg_traded_7d") or 0, e.get("avg_traded_30d") or 0 return q def walk(mat: str, cx: str, qty: float, side: str, refresh=False) -> dict: """Walk the live order book. side='buy' takes sell orders (you pay asks); side='sell' hits buy orders (you get bids). Returns avg price, units filled, worst price reached, total cost/proceeds. Shallow books give filled < qty.""" ob = fio.order_book(mat, cx, refresh) orders = ob["SellingOrders"] if side == "buy" else ob["BuyingOrders"] # MM orders come with ItemCount None = unlimited depth; treat as infinite orders = sorted((o for o in orders if o.get("ItemCount") != 0), key=lambda o: o["ItemCost"], reverse=(side == "sell")) left, total, worst = qty, 0.0, None for o in orders: take = min(left, float("inf") if o.get("ItemCount") is None else o["ItemCount"]) total += take * o["ItemCost"] worst, left = o["ItemCost"], left - take if left <= 0: break filled = qty - left return dict(avg=total / filled if filled else None, filled=filled, worst=worst, total=total, short=left > 0)