Files
poly-maker/tests/test_marketdata.py
Daniel Sapkota c7b686116e Getting there
2026-07-05 17:50:04 -04:00

111 lines
3.3 KiB
Python

"""Tests for market-WS parsing and the book service routing."""
from __future__ import annotations
from polymaker.domain import Side
from polymaker.marketdata.parse import (
parse_book,
parse_last_trade,
parse_price_changes,
parse_tick_size_change,
)
from polymaker.marketdata.service import MarketDataService
# frames modeled on live captures (2026-07-05)
BOOK = {
"event_type": "book",
"market": "0xcond",
"asset_id": "yes-tok",
"timestamp": "1783270000000",
"hash": "abc123",
"tick_size": "0.01",
"bids": [{"price": "0.48", "size": "100"}, {"price": "0.49", "size": "200"}],
"asks": [{"price": "0.52", "size": "150"}, {"price": "0.51", "size": "80"}],
}
PRICE_CHANGE = {
"event_type": "price_change",
"market": "0xcond",
"timestamp": "1783270001000",
"price_changes": [
{"asset_id": "yes-tok", "price": "0.49", "size": "0", "side": "BUY", "hash": "h"},
{"asset_id": "yes-tok", "price": "0.50", "size": "300", "side": "BUY", "hash": "h"},
],
}
LAST_TRADE = {
"event_type": "last_trade_price",
"market": "0xcond",
"asset_id": "yes-tok",
"price": "0.50",
"size": "42",
"side": "BUY",
"timestamp": "1783270002000",
}
def test_parse_book_converts_ms_and_levels():
upd = parse_book(BOOK)
assert upd is not None
assert upd.asset_id == "yes-tok"
assert upd.condition_id == "0xcond"
assert (0.49, 200) in upd.bids
assert upd.ts == 1783270000.0 # ms -> s
assert upd.tick_size == 0.01
def test_parse_price_changes():
changes = parse_price_changes(PRICE_CHANGE)
assert len(changes) == 2
assert changes[0].side is Side.BUY
assert changes[1].price == 0.50 and changes[1].size == 300
def test_parse_last_trade_aggressor():
tp = parse_last_trade(LAST_TRADE)
assert tp is not None
assert tp.aggressor is Side.BUY
assert tp.size == 42
def test_parse_tick_size_change():
tc = parse_tick_size_change(
{"event_type": "tick_size_change", "asset_id": "yes-tok", "new_tick_size": "0.001"}
)
assert tc is not None and tc.tick_size == 0.001
def test_service_routes_book_and_wakes_quoter():
woken: list[tuple[str, str]] = []
svc = MarketDataService(on_dirty=lambda c, t: woken.append((c, t)))
svc.set_markets([("0xcond", ["yes-tok", "no-tok"])])
svc._dispatch(BOOK)
book = svc.book("yes-tok")
assert book is not None
assert book.best_bid().price == 0.49
assert book.best_ask().price == 0.51
assert woken == [("0xcond", "yes-tok")]
def test_service_applies_price_change_delta():
svc = MarketDataService()
svc.set_markets([("0xcond", ["yes-tok"])])
svc._dispatch(BOOK)
svc._dispatch(PRICE_CHANGE) # removes 0.49 bid, adds 0.50 bid
book = svc.book("yes-tok")
assert book.best_bid().price == 0.50
assert 0.49 not in book.bids
def test_service_ignores_unsubscribed_asset():
svc = MarketDataService()
svc.set_markets([("0xcond", ["yes-tok"])])
svc._dispatch({**BOOK, "asset_id": "stranger"})
assert svc.book("stranger") is None
def test_service_forwards_trades_for_flow():
trades = []
svc = MarketDataService(on_trade=trades.append)
svc.set_markets([("0xcond", ["yes-tok"])])
svc._dispatch(LAST_TRADE)
assert len(trades) == 1 and trades[0].aggressor is Side.BUY