SPB Git

spb/wp25_uqo

Public
6.1 KB · 144 lines python
1"""Three market designs for Model C. Each takes a list of orders and returns executed trades.23Order: dict(agent=i, side='buy'|'sell', qty=float, limit=float|None, type='limit'|'market').4Trade: dict(buyer, seller, qty, price).56* `walrasian_call`: a single clearing price equates buy and sell quantities (limit orders define step demand/supply7  schedules; market orders are price-insensitive); rationing pro rata on the long side.8* `continuous_double_auction`: limit order book with price–time priority; orders arrive in a random sequence9  (seeded), marketable orders execute against resting ones at the resting price; records mid, spread, depth.10* `bilateral_bargaining`: random matching of a buyer and a seller for R rounds; trade if the buyer's limit >= the11  seller's limit at the Nash-bargaining price (weight `power` on the buyer), quantity = min of the two.12Feasibility (cash, holdings) is enforced by the ledger when trades are settled; designs only match.13"""14from __future__ import annotations1516import numpy as np171819def _split(orders):20    buys = [o for o in orders if o['side'] == 'buy' and o['qty'] > 0]21    sells = [o for o in orders if o['side'] == 'sell' and o['qty'] > 0]22    return buys, sells232425def walrasian_call(orders, p_ref: float, p_lo: float = 1e-6, p_hi: float = None):26    buys, sells = _split(orders)27    if not buys or not sells:28        return [], p_ref, dict(excess=0.0)29    p_hi = p_hi or 100 * p_ref3031    def demand(p):32        return sum(o['qty'] for o in buys if o['type'] == 'market' or o['limit'] >= p)3334    def supply(p):35        return sum(o['qty'] for o in sells if o['type'] == 'market' or o['limit'] <= p)3637    # price grid from limits: excess demand is a step function, find the price minimising |excess|38    cands = sorted(set([o['limit'] for o in buys + sells if o['limit'] is not None] + [p_ref]))39    best, best_ex = p_ref, float('inf')40    for p in cands:41        ex = demand(p) - supply(p)42        if abs(ex) < best_ex or (abs(ex) == best_ex and abs(p - p_ref) < abs(best - p_ref)):43            best, best_ex = p, abs(ex)44    p = best45    D, S = demand(p), supply(p)46    if D <= 0 or S <= 0:47        return [], p_ref, dict(excess=D - S)48    q = min(D, S)49    rb, rs = q / D, q / S50    bq = [(o['agent'], o['qty'] * rb) for o in buys if o['type'] == 'market' or o['limit'] >= p]51    sq = [(o['agent'], o['qty'] * rs) for o in sells if o['type'] == 'market' or o['limit'] <= p]52    trades = []53    i = j = 054    bq = [list(x) for x in bq]55    sq = [list(x) for x in sq]56    while i < len(bq) and j < len(sq):57        x = min(bq[i][1], sq[j][1])58        if x > 1e-12:59            trades.append(dict(buyer=bq[i][0], seller=sq[j][0], qty=x, price=p))60        bq[i][1] -= x61        sq[j][1] -= x62        if bq[i][1] <= 1e-12:63            i += 164        if sq[j][1] <= 1e-12:65            j += 166    return trades, p, dict(excess=D - S, demand=D, supply=S)676869def continuous_double_auction(orders, p_ref: float, rng: np.random.Generator):70    book_b, book_s = [], []       # resting orders: [price, time, agent, qty]71    trades = []72    seq = rng.permutation(len(orders))73    spreads, mids = [], []74    t = 075    for k in seq:76        o = dict(orders[k])77        t += 178        qty = o['qty']79        if qty <= 0:80            continue81        if o['side'] == 'buy':82            lim = np.inf if o['type'] == 'market' else o['limit']83            book_s.sort(key=lambda r: (r[0], r[1]))84            while qty > 1e-12 and book_s and book_s[0][0] <= lim:85                r = book_s[0]86                x = min(qty, r[3])87                trades.append(dict(buyer=o['agent'], seller=r[2], qty=x, price=r[0]))88                qty -= x89                r[3] -= x90                if r[3] <= 1e-12:91                    book_s.pop(0)92            if qty > 1e-12 and o['type'] == 'limit':93                book_b.append([o['limit'], t, o['agent'], qty])94        else:95            lim = -np.inf if o['type'] == 'market' else o['limit']96            book_b.sort(key=lambda r: (-r[0], r[1]))97            while qty > 1e-12 and book_b and book_b[0][0] >= lim:98                r = book_b[0]99                x = min(qty, r[3])100                trades.append(dict(buyer=r[2], seller=o['agent'], qty=x, price=r[0]))101                qty -= x102                r[3] -= x103                if r[3] <= 1e-12:104                    book_b.pop(0)105            if qty > 1e-12 and o['type'] == 'limit':106                book_s.append([o['limit'], t, o['agent'], qty])107        if book_b and book_s:108            bb = max(r[0] for r in book_b)109            ba = min(r[0] for r in book_s)110            if ba >= bb:111                spreads.append(ba - bb)112                mids.append(0.5 * (ba + bb))113    last = trades[-1]['price'] if trades else p_ref114    depth_b = sum(r[3] for r in book_b)115    depth_s = sum(r[3] for r in book_s)116    info = dict(spread=float(np.mean(spreads)) if spreads else np.nan, mid=float(np.mean(mids)) if mids else np.nan,117                depth=depth_b + depth_s, n_trades=len(trades))118    return trades, last, info119120121def bilateral_bargaining(orders, p_ref: float, rng: np.random.Generator, rounds: int = 3, power: float = 0.5):122    buys, sells = _split(orders)123    buys = [dict(o) for o in buys]124    sells = [dict(o) for o in sells]125    trades = []126    for _ in range(rounds):127        bl = [o for o in buys if o['qty'] > 1e-12]128        sl = [o for o in sells if o['qty'] > 1e-12]129        if not bl or not sl:130            break131        rng.shuffle(bl)132        rng.shuffle(sl)133        for b, s in zip(bl, sl):134            bl_lim = b['limit'] if b['type'] == 'limit' else p_ref * 1.5135            sl_lim = s['limit'] if s['type'] == 'limit' else p_ref * 0.5136            if bl_lim >= sl_lim and b['agent'] != s['agent']:137                price = power * sl_lim + (1 - power) * bl_lim   # buyer with power `power` pulls price toward seller's ask138                x = min(b['qty'], s['qty'])139                trades.append(dict(buyer=b['agent'], seller=s['agent'], qty=x, price=price))140                b['qty'] -= x141                s['qty'] -= x142    last = float(np.mean([t['price'] for t in trades])) if trades else p_ref143    return trades, last, dict(n_trades=len(trades), dispersion=float(np.std([t['price'] for t in trades])) if trades else np.nan)144
Files · main
Menu

Theme