"""Stub tests for the value paper bot (value/PLAN.md V0): honest FAK fill model, event cap, cooldown, refund-aware chain settlement. No network. Run: python3 tests/test_valuebot.py """ import os import sys import time sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "value")) import valuebot as vb # noqa: E402 vb.STATE = "/tmp/vb_test_state.json" vb.FILLS = "/tmp/vb_test_fills.jsonl" vb.FEED = "/tmp/vb_test_feed.json" fails = [] def case(name): def deco(fn): ba0, op0 = vb.book_asks, vb.onchain_payouts for f in (vb.STATE, vb.FILLS, vb.FEED): if os.path.exists(f): os.remove(f) try: fn() print(f"PASS {name}") except AssertionError as e: print(f"FAIL {name}: {e}") fails.append(name) finally: vb.book_asks, vb.onchain_payouts = ba0, op0 return deco def cand(tok, event="ev-2026-07-17", mark=0.01): return {"token": tok, "outcome": "Yes", "mark": mark, "cond": "0xc" + tok, "title": f"mkt {tok}", "end": "2026-01-01T00:00:00Z", # already past "cat": "esports", "event": event, "tok_index": 0, "n_outcomes": 2} @case("fill model: walks the ladder inside the band, honest miss when thin") def t1(): # $1 at 1c needs 100 shares; 60 at 0.010 + 50 at 0.0104 (inside 1.05 band) sh, px, r = vb.model_fill([(0.010, 60), (0.0104, 50)], 1.0, 0.02) assert r is None and abs(sh - (0.6/0.010 + 0.4/0.0104)) < 1e-6 # thin: only $0.30 inside the band -> MISS sh, px, r = vb.model_fill([(0.010, 30), (0.02, 1000)], 1.0, 0.02) assert sh is None and "FAK no-match" in r, r # best ask above the bucket -> not a candidate sh, px, r = vb.model_fill([(0.03, 1000)], 1.0, 0.02) assert sh is None and "above" in r # empty/failed book assert vb.model_fill([], 1.0, 0.02)[2] == "no asks on the book" assert vb.model_fill(None, 1.0, 0.02)[2] == "book fetch failed" @case("band: a 1.9c best ask does NOT walk into 2.5c levels") def t2(): sh, px, r = vb.model_fill([(0.019, 30), (0.025, 1000)], 1.0, 0.02) assert sh is None, "walked past min(MAX_PX, best*1.05)" @case("event cap 1: second ticket on the same event is skipped") def t3(): st = vb.load_state() vb.book_asks = lambda tok: [(0.01, 1000)] vb.open_positions(st, [cand("A", "ev-2026-07-17"), cand("B", "ev-2026-07-17"), cand("C", "other-2026-07-18")], budget=10) assert set(st["my_pos"]) == {"A", "C"}, set(st["my_pos"]) assert st["stats"]["fills"] == 2 and st["stats"]["attempts"] == 2 @case("cooldown: a missed token is not re-checked inside COOLDOWN_S") def t4(): st = vb.load_state() vb.book_asks = lambda tok: [(0.01, 10)] # thin -> miss vb.open_positions(st, [cand("A")], budget=10) assert st["stats"]["misses"] == 1 vb.open_positions(st, [cand("A")], budget=10) # immediately again assert st["stats"]["attempts"] == 1, "re-attempted inside cooldown" @case("settle: win pays full, refund pays 0.5, loss pays 0 — cash exact") def t5(): st = vb.load_state() vb.book_asks = lambda tok: [(0.01, 1000)] for t_ in ("W", "R", "L"): vb.open_positions(st, [cand(t_, event=t_)], budget=10) cash_after_open = st["cash"] vecs = {"0xcW": [1.0, 0.0], "0xcR": [0.5, 0.5], "0xcL": [0.0, 1.0]} vb.onchain_payouts = lambda cond, rpc: vecs[cond] vb.settle(st, rpc="stub", budget=10) s = st["stats"] assert (s["wins"], s["refunds"], s["losses"]) == (1, 1, 1) sh = 1.0 / 0.01 assert abs(st["cash"] - (cash_after_open + sh*1.0 + sh*0.5)) < 1e-6 assert not st["my_pos"] @case("unresolved market stays open; feed math consistent") def t6(): st = vb.load_state() vb.book_asks = lambda tok: [(0.01, 1000)] vb.open_positions(st, [cand("A")], budget=10) vb.onchain_payouts = lambda cond, rpc: None # denominator 0 vb.settle(st, rpc="stub", budget=10) assert "A" in st["my_pos"] and st["stats"]["resolved"] == 0 feed = vb.write_feed(st) assert feed["open_count"] == 1 and feed["deployed"] == 1.0 assert feed["fill_rate"] == 1.0 print() print("FAILURES:", fails or "none") sys.exit(1 if fails else 0)