// Package market implements financial primitives as lattice elements. // // An order book entry is structure. A spread is structure. A dislocation // between price and information is structure. The lattice doesn't // distinguish domain — Element is Element. Price is structure. Events // are structure. The organism sees structure. package market import "time" // Side is bid or ask. type Side int const ( Bid Side = iota Ask ) func (s Side) String() string { if s == Bid { return "bid" } return "ask" } // Venue identifies an exchange or trading platform. type Venue string // OrderEntry is a single price level in an order book. // It implements no special interface — it becomes a lattice element // through enzymatic decomposition, same as a Nostr event. type OrderEntry struct { Price float64 Volume float64 Side Side Venue Venue Time time.Time } // OrderBook is a snapshot of bids and asks for one asset on one venue. type OrderBook struct { Asset string Venue Venue Bids []OrderEntry Asks []OrderEntry Time time.Time } // BestBid returns the highest bid, or zero if empty. func (ob *OrderBook) BestBid() float64 { best := 0.0 for _, e := range ob.Bids { if e.Price > best { best = e.Price } } return best } // BestAsk returns the lowest ask, or zero if empty. func (ob *OrderBook) BestAsk() float64 { if len(ob.Asks) == 0 { return 0 } best := ob.Asks[0].Price for _, e := range ob.Asks[1:] { if e.Price < best { best = e.Price } } return best } // Spread returns the difference between best ask and best bid. // Negative or zero spread indicates a crossed book. func (ob *OrderBook) Spread() float64 { ask := ob.BestAsk() bid := ob.BestBid() if ask == 0 || bid == 0 { return 0 } return ask - bid } // MidPrice returns the midpoint between best bid and best ask. func (ob *OrderBook) MidPrice() float64 { ask := ob.BestAsk() bid := ob.BestBid() if ask == 0 || bid == 0 { return 0 } return (ask + bid) / 2 } // DepthImbalance returns the ratio of bid volume to total volume // within the top N levels. Range [0,1]: 0 = all asks, 1 = all bids. // Returns 0.5 if both sides are empty. func (ob *OrderBook) DepthImbalance(levels int) float64 { bidVol := topVolume(ob.Bids, levels) askVol := topVolume(ob.Asks, levels) total := bidVol + askVol if total == 0 { return 0.5 } return bidVol / total } func topVolume(entries []OrderEntry, n int) float64 { vol := 0.0 for i, e := range entries { if i >= n { break } vol += e.Volume } return vol }