Files
arena/environments/grand_exchange_live/tests/test_live.py
T
kartiandClaude Opus 5 f953c03bd1 grand-exchange-live: protocol layer, taskset, config, widened reference gate
The stepped engine becomes a runnable taskset. protocol.py is both the
render/parse boundary and the security boundary: TurnView carries the live
Market, so view.market.items[i].prices is the whole held-out series and a
foresight policy reading it earned +9.4% on the oracle. The renderer emits only
observed ticks, the trace carries plain scalars, and a sentinel scan of every
rendered turn fails if either reopens. House rule 1 had no other enforcement.

Replies are a delta sheet and parsing never raises — NaN, Infinity, 1e309 and
20k-deep nesting all resolve to a hold, because the one-shot form has already
shown what a real model sends when it gives up.

LiveExchangeEnv.run drives the engine host-side with the harness left null. Two
things learned the hard way and worth keeping: a terminated Segment mid-window
holds the remaining looks instead of raising, and rewards are recorded INSIDE
the interaction, before close. Recording after close writes the traces correctly
and leaves every eval.log line reading reward=0.000 — the canonical verifiers
reference does it after the block; do not copy it there.

The reference-family gate is widened from four named rungs to the whole 8x8
(requote_band, idle_share) family: no member may out-earn the shipped reference
by more than 1.02x, and any that does must still score >= 0.90. That is the check
that caught the first draft's fake 0.152 gap, now shipped rather than remembered.

Also guards `clean` on the reference being profitable, here and in the one-shot
form. Against a reference that lost money the bar is negative, so doing nothing
cleared it and inaction collected a quarter of the reward. Unreachable while
viable_market admits only profitable references; one relaxed filter from
reachable, and exactly the floor house rule 3 forbids. Probe values unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 17:44:20 -07:00

557 lines
26 KiB
Python

"""The stepped engine's invariants, as tests rather than as docstrings.
Five claims are load-bearing and every one of them is a way the environment could be quietly
wrong rather than loudly broken:
the fixed point one look on turn 1 and nothing after it must reproduce the one-shot
engine EXACTLY — same realised profit, same capital committed. If it
does not, the live form is a different market wearing the same
generator, and no number measured here is comparable to a `grand-exchange`
number.
the ledger cash, coins locked, cost basis and realised profit must sum to the
starting capital after every single tick. A bug that mints money reads
as a good score, not as a crash.
the buy limit cumulative across the whole window, so cancel-and-re-place cannot buy
the same item twice over. Without it, re-quoting resets the limit and
the limit stops being a limit.
the freeze a look that touches a live book stops the WHOLE book for `market.freeze`
ticks, and the first placement is free.
the queue an amended offer starts again at zero accrued fill room, so moving a
limit costs even after the freeze has expired.
"""
from __future__ import annotations
import random
import statistics
import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import context # noqa: E402 - resolves the package before anything imports it
HERE = context.ROOT
# The one-shot environment is a sibling checkout rather than a dependency, and the fixed-point
# assertion below needs both engines in one process. It is always a shim.
context.shim(HERE.parent / "grand_exchange" / "grand_exchange")
from grand_exchange_live.book import Order, execute, reference_orders # noqa: E402
from grand_exchange_live.live import ( # noqa: E402
HORIZON,
IDLE_SHARE,
REQUOTE_BAND,
TAIL_TICKS,
TICKS_PER_TURN,
TURNS,
Leg,
Ledger,
Reference,
desired_orders,
play,
)
from grand_exchange_live.market import build_market # noqa: E402
SEEDS = range(60_000, 60_024)
def market(seed: int, held_out: int = HORIZON):
return build_market(seed, 5, 56, held_out)
def once(orders):
"""Submit a plan on turn 1 and never look again — the one-shot strategy, played live."""
state = {"done": False}
def policy(view):
if state["done"]:
return []
state["done"] = True
return [Leg(o.item, o.quantity, o.buy, o.sell) for o in orders]
return policy
def restate(view):
"""Re-submit the book exactly as it already stands, on every look.
The true no-op re-quote, and the only honest way to price the freeze and the queue
reset on their own: same names, same prices, same units still sought. Nothing about the
plan changes, so any difference in what it buys or earns IS the cost of looking.
Note that re-submitting the ORIGINAL quantities instead is a different policy and a
bigger one — the units already bought free up their reservation, sale proceeds return
to the purse, and asking for the full size again spends them. That policy is `churn` in
the ladder; it earns about the same gp off a third more peak capital, which is what
`efficiency` is for.
"""
return [Leg(o.item, o.quantity, o.buy, o.sell) for o in view.open_orders.values()]
class FixedPointTests(unittest.TestCase):
def test_single_look_matches_one_shot(self) -> None:
"""The stepped engine, used exactly the way the one-shot engine is used, IS the
one-shot engine. This is the assertion that makes the two forms comparable."""
for seed in SEEDS:
m = market(seed)
plan = reference_orders(m)
stepped = play(m, once(plan))
flat = execute(m, plan)
self.assertAlmostEqual(stepped.realised, flat.realised, places=6, msg=f"seed {seed}")
self.assertAlmostEqual(stepped.peak_employed, flat.committed, places=6,
msg=f"seed {seed}")
self.assertEqual(stepped.bought, flat.bought, f"seed {seed}")
self.assertEqual(stepped.sold, flat.sold, f"seed {seed}")
self.assertEqual(stepped.dumped, flat.dumped, f"seed {seed}")
self.assertEqual(stepped.frozen_ticks, 0, "the first placement is not free")
def test_turn_one_matches_reference_orders(self) -> None:
"""`desired_orders` is `book.reference_orders` generalised to a moment in the
window, and on turn 1 the moment is the one the one-shot form scores."""
for seed in SEEDS:
m = market(seed)
wanted = desired_orders(
m.items,
{i.name: list(i.visible_prices(m.visible)) for i in m.items},
{i.name: list(i.visible_volumes(m.visible)) for i in m.items},
float(m.capital),
m.held_out,
{i.name: i.buy_limit for i in m.items},
)
self.assertEqual(
[(o.item, o.quantity, o.buy, o.sell) for o in wanted],
[(o.item, o.quantity, o.buy, o.sell) for o in reference_orders(m)],
f"seed {seed}",
)
class LedgerTests(unittest.TestCase):
def test_nothing_mints_coins(self) -> None:
"""Cash + locked + cost basis + realised == capital, checked after every tick of
every turn by instrumenting `Ledger.mark`, which the engine calls on each one."""
broken: list[str] = []
original = Ledger.mark
def checked(self) -> None:
total = self.cash + self.locked + sum(self.basis.values()) \
+ sum(self.incoming_basis.values())
if abs((total - self.realised) - self.capital) > 1e-6:
broken.append(f"identity: {total:.6f} against {self.capital:.6f}")
# The purse is the binding constraint on everything: an offer is funded out of
# it or it is not placed. A negative purse is the engine having spent coins that
# were not there, which reads as a good score rather than as a crash.
if self.cash < -1e-9:
broken.append(f"purse: {self.cash:.6f}")
original(self)
Ledger.mark = checked
try:
for seed in SEEDS:
m = market(seed)
for policy in (Reference(), once(reference_orders(m)), restate):
play(m, policy)
finally:
Ledger.mark = original
self.assertEqual(broken, [], "the ledger identity broke")
def test_inaction_is_exactly_zero(self) -> None:
for seed in SEEDS:
result = play(market(seed), lambda view: [])
self.assertEqual(result.realised, 0.0)
self.assertEqual(result.peak_employed, 0.0)
self.assertEqual(result.roc, 0.0)
self.assertEqual(result.looks, 0)
def test_capital_employed_can_exceed_the_starting_purse(self) -> None:
"""A policy that recycles sale proceeds can employ more than the starting capital,
and that is the whole point of a purse the proceeds return to — a one-shot plan
cannot spend what its own sales earned. What it can never do is employ coins that
do not exist, and that is `test_nothing_mints_coins`'s negative-purse check, not a
bound on the final realised profit: gains made mid-window and given back later are
real capital while they are employed."""
peaks = [play(market(s), Reference()).peak_employed for s in SEEDS]
self.assertTrue(all(p > 0.0 for p in peaks))
self.assertTrue(any(p > 250_000 for p in peaks),
"no basket ever employs more than the starting purse — sale "
"proceeds are not being redeployed and the live form is a "
"one-shot form with extra steps")
class BuyLimitTests(unittest.TestCase):
def test_the_window_limit_is_cumulative_across_requotes(self) -> None:
"""Ask for the whole limit on every one of the eight looks. The window limit is the
window's, not the offer's, so the total bought can never exceed it."""
greedy = lambda view: [
Leg(i.name, i.buy_limit, round(i.prices[view.seen - 1] * 1.20),
round(i.prices[view.seen - 1] * 0.80))
for i in view.market.items
]
seen_a_cap = False
for seed in SEEDS:
m = market(seed)
result = play(m, greedy)
for item in m.items:
got = result.bought_by_item.get(item.name, 0)
self.assertLessEqual(got, item.buy_limit,
f"seed {seed}: bought {got} of {item.name} against a "
f"window limit of {item.buy_limit}")
seen_a_cap = seen_a_cap or got == item.buy_limit
self.assertTrue(seen_a_cap, "no basket ever reached a buy limit — the invariant is "
"untested by this policy rather than upheld by the engine")
class FreezeTests(unittest.TestCase):
def test_the_first_placement_is_free_and_the_second_is_not(self) -> None:
for seed in SEEDS:
m = market(seed)
plan = reference_orders(m)
if not plan:
continue
self.assertEqual(play(m, once(plan)).frozen_ticks, 0)
state = {"n": 0}
def twice(view, plan=plan, state=state):
state["n"] += 1
if state["n"] <= 2:
return [Leg(o.item, o.quantity, o.buy, o.sell) for o in plan]
return []
result = play(m, twice)
self.assertEqual(result.frozen_ticks, m.freeze,
f"seed {seed}: one re-quote should freeze {m.freeze} ticks")
self.assertEqual(result.amendments, 1)
self.assertEqual(result.looks, 2)
def test_a_frozen_tick_fills_nothing(self) -> None:
"""The freeze has to bite on the tape, not only on a counter. Re-quoting the
identical plan changes nothing about what is wanted, so any difference in units
bought is the freeze and the queue reset — and it must never be an increase."""
churned = fresh = 0
for seed in SEEDS:
m = market(seed)
plan = reference_orders(m)
fresh += play(m, once(plan)).bought
churned += play(m, restate).bought
self.assertLess(churned, fresh,
f"churning the identical plan bought {churned} units against "
f"{fresh} — re-quoting costs nothing")
def test_churn_earns_less_than_standing_still(self) -> None:
"""The same claim in gp, which is what the reward actually reads."""
churned = fresh = 0.0
for seed in SEEDS:
m = market(seed)
plan = reference_orders(m)
fresh += play(m, once(plan)).realised
churned += play(m, restate).realised
self.assertLess(churned, fresh, f"churn earned {churned:,.0f} gp against {fresh:,.0f}")
class QueueTests(unittest.TestCase):
def test_an_amendment_resets_accrued_fill_room(self) -> None:
"""A thin item accrues a quarter of a tick's volume per eligible tick and carries it
between ticks — that carry is the only reason an item trading two units a tick is
tradeable rather than untradeable. Throwing it away on every look must show up as
fewer units bought even when the freeze is one tick, so the two mechanics are
separable rather than one mechanic counted twice."""
thin = [s for s in SEEDS if build_market(s, 5, 56, HORIZON).freeze == 1]
self.assertTrue(thin, "no one-tick-freeze basket in the block — test proves nothing")
worse = 0
for seed in thin:
m = market(seed)
plan = reference_orders(m)
if play(m, restate).bought < play(m, once(plan)).bought:
worse += 1
self.assertGreater(worse, 0, "with a one-tick freeze, re-quoting cost nothing at all")
class HeldOutTests(unittest.TestCase):
def test_the_tail_after_the_last_look_is_never_observed(self) -> None:
"""House rule 1, in its WEAKER live form, asserted on the size of the slice rather
than on the frontier. An agent that takes every look still has TICKS_PER_TURN +
TAIL_TICKS ticks executed after its final observation."""
seen: list[int] = []
def watcher(view):
seen.append(view.seen - view.market.visible)
return []
for seed in SEEDS:
m = market(seed)
seen.clear()
play(m, watcher)
self.assertEqual(len(seen), TURNS)
self.assertEqual(seen[0], 0, "the first look sees a graded tick")
self.assertEqual(seen[-1], (TURNS - 1) * TICKS_PER_TURN)
self.assertEqual(HORIZON - seen[-1], TICKS_PER_TURN + TAIL_TICKS)
self.assertGreaterEqual(HORIZON - seen[-1], 4)
def test_a_view_never_carries_a_graded_tick_early(self) -> None:
"""The observation handed over on a look must contain only ticks that have already
been executed against — a one-tick lookahead here would hand the agent the answer."""
for seed in (60_000, 60_007, 60_013):
m = market(seed)
def strict(view, m=m):
for item in m.items:
got = view.new_prices[item.name]
start = view.seen - len(got)
self.assertEqual(got, item.prices[start:view.seen])
self.assertLessEqual(view.seen, m.visible + m.held_out)
return []
play(m, strict)
class SheetTests(unittest.TestCase):
def test_a_cancel_returns_the_coins_and_stops_the_buying(self) -> None:
m = market(60_000)
plan = reference_orders(m)
name = plan[0].item
def cancel_after_one(view, plan=plan, name=name):
if view.turn == 1:
return [Leg(o.item, o.quantity, o.buy, o.sell) for o in plan]
if view.turn == 2:
return [Leg(name, 0)]
return []
held = play(m, once(plan)).bought_by_item.get(name, 0)
cut = play(m, cancel_after_one).bought_by_item.get(name, 0)
self.assertLess(cut, held, "cancelling an offer did not stop it buying")
def test_a_leg_naming_nothing_on_the_board_is_dropped_not_fatal(self) -> None:
"""A malformed sheet scores what doing nothing scores. It must never raise: the
reward runs inside the metric, so a raise takes the whole rollout with it rather
than scoring zero."""
junk = lambda view: [
Leg("Nonexistent bauble", 10, 5, 6),
Leg("", 1, 1, 2),
Leg(view.market.items[0].name, 5, 0, 0),
Leg(view.market.items[0].name, -3),
]
result = play(market(60_000), junk)
self.assertEqual(result.realised, 0.0)
self.assertGreaterEqual(result.dropped, 4)
def test_two_legs_on_one_name_keep_the_first(self) -> None:
m = market(60_000)
name = m.items[0].name
def duplicated(view, name=name):
if view.turn > 1:
return []
price = view.market.item(name).prices[view.seen - 1]
return [Leg(name, 10, round(price * 0.95), round(price * 1.05)),
Leg(name, 10_000, round(price * 1.50), round(price * 1.60))]
result = play(m, duplicated)
self.assertEqual(result.dropped, 1)
class ReferencePolicyTests(unittest.TestCase):
def test_the_reference_does_not_requote_every_turn(self) -> None:
"""If it did, the freeze would be a constant cost and the environment would be
measuring the one-shot answer with extra steps."""
amendments = [play(market(s), Reference()).amendments for s in SEEDS]
# Per-basket, the reference will sometimes want every look it is given — a basket
# where the purse keeps freeing up is a basket where redeploying it keeps paying.
# The claim that matters is the average, and it is that a look is declined often
# enough for declining to be a judgement rather than an accident.
self.assertLess(statistics.fmean(amendments), TURNS - 2,
"the reference re-quotes on nearly every look — the budget is a "
"formality")
self.assertGreater(sum(amendments), 0,
"the reference never re-quotes — the stepped form is decorative")
def test_the_reference_makes_money_in_most_baskets(self) -> None:
earned = [play(market(s), Reference()).realised for s in SEEDS]
won = sum(1 for e in earned if e > 0)
self.assertGreaterEqual(won, len(earned) - 2,
f"the reference profits in only {won}/{len(earned)} baskets "
"— the reward's denominator is not reliable")
def test_it_is_deterministic(self) -> None:
for seed in (60_000, 60_011):
m = market(seed)
a, b = play(m, Reference()), play(m, Reference())
self.assertEqual(a, b)
class RandomSheetTests(unittest.TestCase):
def test_random_legs_never_break_the_engine(self) -> None:
"""The hostile battery, cheaply: random garbage on every look, replayed per turn,
must never raise and must never mint a coin."""
rng = random.Random(7)
for seed in SEEDS:
m = market(seed)
def noise(view, rng=rng):
return [
Leg(rng.choice([i.name for i in view.market.items] + ["", "nope"]),
rng.choice([-5, 0, 1, 10 ** 9]),
rng.choice([0, -1, 1, 10 ** 9]),
rng.choice([0, 1, 10 ** 9]))
for _ in range(rng.randint(0, 8))
]
result = play(m, noise)
self.assertLessEqual(result.peak_employed, m.capital + 1e-6)
self.assertGreaterEqual(result.realised, -float(m.capital))
class LadderGateTests(unittest.TestCase):
"""House rules 3 and 4, executable, in this package rather than in `probe.py`.
`probe.py` is B1's file and five workstreams are queued behind it; the gate this
environment lives or dies on should not wait on a merge, and it should fail here first
if the engine drifts. When A's probe row lands it asserts the same three things over the
same helper.
"""
def setUp(self) -> None:
sys.path.insert(0, str(HERE))
import measure_ladder
self.ladder = measure_ladder
self.markets = measure_ladder.baskets(measure_ladder.BLOCK * measure_ladder.BLOCKS)
self.blocks = [self.markets[i:i + measure_ladder.BLOCK]
for i in range(0, len(self.markets), measure_ladder.BLOCK)]
def test_the_floor_is_exactly_zero_and_the_ceiling_is_reachable(self) -> None:
rows = self.ladder.ladder(self.markets)
self.assertEqual(rows["inaction"]["total"], 0.0)
self.assertEqual(rows["staller"]["total"], 0.0)
self.assertEqual(rows["oracle (the live reference)"]["total"], 1.0)
def test_the_budget_binds_in_every_block(self) -> None:
"""Rule 4. Not on the 120-basket mean — `probe.py` documents this generator swinging
0.48 against 0.18 between blocks of twenty-four, and a mean that holds while one
block of the size a run actually grades goes the other way is a mean measured off
the seed."""
gaps = []
for block in self.blocks:
rows = self.ladder.ladder(block)
gap = (rows["oracle (the live reference)"]["total"]
- rows["exhaustive (re-quote every look)"]["total"])
gaps.append(gap)
self.assertGreater(gap, 0.10,
f"baskets {block[0].seed}-{block[-1].seed}: spending every "
f"look scores {gap:.3f} below the reference — the turn "
"budget is a formality")
self.assertGreater(statistics.fmean(gaps), 0.25)
def test_the_reference_is_not_merely_the_denominator(self) -> None:
"""The gap has to be a fact about money, not about normalisation. If a policy EARNED
more than the reference and scored below it, the reward would be an imitation score
for the reference's constants — which is precisely what the first cut of this engine
did, at 63,974 gp against the reference's 51,520.
Two halves, because there are two ways to be the wrong denominator. The rungs are
different strategies; the sweep below is the reference's OWN family, and that is the
half this test used to skip.
"""
rows = self.ladder.ladder(self.markets)
oracle = rows["oracle (the live reference)"]
for name in ("exhaustive (re-quote every look)", "churn (re-place at full size every look)",
"impatient (one-shot reference, one look)",
"restate (re-quote the same book every look)"):
self.assertLess(rows[name]["gp"], oracle["gp"],
f"{name} earns more gp than the reference it is scored against")
def test_no_member_of_the_references_own_family_beats_it_and_scores_below_it(self) -> None:
"""The whole sixty-four-cell family, not four named rungs.
The shipped reference is not the family's argmax and this is the assertion that says
how far from it that is allowed to be. Measured over 120 baskets, 2026-08-21:
shipped (0.025, 0.50) 76,744 gp rule-4 gap 0.369 mean, 0.316 worst block
argmax (1.000, 0.50) 77,599 gp rule-4 gap 0.372 mean, 0.299 worst block
so the best member earns 1.0111x the reference, and the worst-scoring member that
out-earns it — (0.040, 0.50) — still scores 0.972. Moving the denominator to the
argmax would make the invariant exact and was measured before this test was written:
it is not taken, because the argmax's requote band is 1.0, i.e. the anchor trigger
never fires, so the denominator would be a strategy that cannot express "the anchor
moved" at all — and the freeze, the mechanic the whole environment uses to make the
turn budget bind, exists to price exactly that judgement. It also does not buy a
wider gap: 0.372 against 0.369 on the mean, and a WORSE worst block. Paying 1.1% of
gp to keep the denominator a strategy rather than a cash-threshold reflex is the
trade, and the tolerances below are the measurement, not a taste.
"""
bands = (0.0, 0.005, 0.010, 0.015, 0.025, 0.040, 0.060, 1.0)
idles = (0.0, 0.05, 0.10, 0.15, 0.20, 0.30, 0.50, 1.01)
shipped = statistics.fmean(
self.ladder.live_reference(m).realised for m in self.markets
)
for band in bands:
for idle in idles:
if (band, idle) == (REQUOTE_BAND, IDLE_SHARE):
continue
earned = statistics.fmean(
play(m, Reference(band, idle)).realised for m in self.markets
)
self.assertLessEqual(
earned, 1.02 * shipped,
f"reference family member ({band}, {idle}) earns {earned:,.0f} gp "
f"against the shipped reference's {shipped:,.0f} — the denominator is "
"leaving too much money on the table, so the reward's argmax is its "
"constants rather than the profit",
)
if earned <= shipped:
continue
got = statistics.fmean(
self.ladder.score(play(m, Reference(band, idle)),
self.ladder.live_reference(m))["total"]
for m in self.markets
)
self.assertGreaterEqual(
got, 0.90,
f"reference family member ({band}, {idle}) earns {earned:,.0f} gp "
f"against {shipped:,.0f} and scores {got:.3f} — the reward is an "
"imitation score, not a profit metric",
)
def test_looking_costs_and_a_wasted_look_costs_more(self) -> None:
rows = self.ladder.ladder(self.markets)
self.assertLess(rows["restate (re-quote the same book every look)"]["total"],
rows["impatient (one-shot reference, one look)"]["total"],
"re-quoting the identical book every turn is free")
self.assertLess(rows["crude (market orders, one look)"]["total"],
rows["plausible (mean anchor, no filter)"]["total"])
self.assertLess(rows["plausible (mean anchor, no filter)"]["total"],
rows["impatient (one-shot reference, one look)"]["total"])
class LoadCostTests(unittest.TestCase):
def test_a_default_taskset_load_is_not_three_thousand_simulations(self) -> None:
"""`num_tasks` defaults to 48 and viability retries; a live `viable_market` running
a full stepped reference per candidate is thousands of simulations before the first
token, on every eval AND every `--dry-run`. The reference is cached per seed and the
try cap is 16 rather than 64, and this is the assertion that says so."""
import time
sys.path.insert(0, str(HERE))
import measure_ladder
from grand_exchange_live import live
live._REFERENCE_CACHE.clear()
started = time.monotonic()
markets = measure_ladder.baskets(48)
cold = time.monotonic() - started
self.assertEqual(len(markets), 48)
self.assertLess(cold, 2.0, f"a 48-task load took {cold:.2f}s")
hits = len(live._REFERENCE_CACHE)
started = time.monotonic()
for market in markets:
live.live_reference(market)
self.assertEqual(len(live._REFERENCE_CACHE), hits, "scoring re-simulated the "
"reference the model played against")
self.assertLess(time.monotonic() - started, 0.05)
if __name__ == "__main__":
unittest.main()