SPB Git

spb/wp27_uqo

Public
3.0 KB · 65 lines python
1"""Property-based tests: random order streams never violate book, reservation or conservation invariants,2and the event log replays to the same final state."""3import numpy as np4from hypothesis import given, settings, strategies as st56from market_agents.exchange.accounts import Account7from market_agents.exchange.engine import AssetSpec, Exchange, ExchangeConfig8from market_agents.exchange.orders import Action, CancelIntent, OrderIntent, OrderType, Side91011def build(seed, n_agents=6, allow_short=False):12    ex = Exchange([AssetSpec(asset=0, name="A0", price_band_pct=0.9)], ExchangeConfig(taker_fee_bps=3.0, maker_fee_bps=-1.0,13                  allow_short=allow_short, max_short_lots=50, order_ttl=7), np.random.default_rng(seed), {0: 1000})14    for i in range(n_agents):15        ex.register(Account(agent_id=i, cash=200_000 * (i + 1), positions={0: 50 * (i + 1)}, short_allowed=allow_short),16                    latency=0.05 + 0.3 * (i % 3))17    return ex181920intent = st.tuples(st.integers(0, 5), st.sampled_from([Side.BUY, Side.SELL]), st.sampled_from([OrderType.LIMIT, OrderType.MARKET]),21                   st.integers(1, 80), st.integers(500, 1500), st.booleans())222324@settings(max_examples=60, deadline=None)25@given(st.lists(intent, min_size=1, max_size=120), st.integers(0, 10_000))26def test_random_streams_keep_invariants(stream, seed):27    ex = build(seed)28    t = 029    for k, (agent, side, otype, qty, price, cancel) in enumerate(stream):30        if cancel and ex.orders:31            oid = max(ex.orders)32            ex.submit(ex.orders[oid].agent_id, Action(cancels=[CancelIntent(oid)]), float(t))33        ex.submit(agent, Action(orders=[OrderIntent(0, side, otype, qty, None if otype is OrderType.MARKET else price)]), float(t))34        if k % 3 == 2:35            ex.process_until(float(t + 1))36            ex.end_of_tick(t)37            t += 138            ex.books[0].check_invariants()39            ex.ledger.check_conservation()40    ex.process_until(float(t + 5))41    ex.books[0].check_invariants()42    ex.ledger.check_conservation()43    # reservations equal the sum over resting orders44    for acc in ex.ledger.accounts.values():45        resting = ex.books[0].resting_for(acc.agent_id)46        assert acc.reserved_cash == sum(o.reserved_cash for o in resting)47        assert acc.reserved_qty.get(0, 0) == sum(o.reserved_qty for o in resting)48        assert acc.positions[0] >= 0495051@settings(max_examples=25, deadline=None)52@given(st.lists(intent, min_size=5, max_size=80), st.integers(0, 1000))53def test_determinism_and_replay(stream, seed):54    def run():55        ex = build(seed)56        for k, (agent, side, otype, qty, price, cancel) in enumerate(stream):57            ex.submit(agent, Action(orders=[OrderIntent(0, side, otype, qty, None if otype is OrderType.MARKET else price)]), float(k // 4))58            if k % 4 == 3:59                ex.process_until(float(k // 4 + 1))60        ex.process_until(1e9)61        return ex62    a, b = run(), run()63    assert a.snapshot() == b.snapshot()64    assert [(e.kind, e.payload) for e in a.events] == [(e.kind, e.payload) for e in b.events]65
Files · b6c1d09b402a43e723c06ff0c9a6073a51ea3fa7
Menu

Theme