build(agent): melter#14fd4b iteration
This commit is contained in:
parent
fcdd3d0e64
commit
582cf99c8b
|
|
@ -0,0 +1,66 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import random
|
||||||
|
from typing import Iterable, List, Tuple
|
||||||
|
|
||||||
|
from .core import LocalArbProblem, SharedSignals
|
||||||
|
from .coordinator import CentralCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
class ScenarioGenerator:
|
||||||
|
"""Deterministic scenario generator for delta-fuzzing.
|
||||||
|
|
||||||
|
Produces a replayable sequence of (LocalArbProblem, SharedSignals) that can
|
||||||
|
be fed into CentralCoordinator.ingest_local. Uses an explicit seed to make
|
||||||
|
runs deterministic.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, seed: int = 0) -> None:
|
||||||
|
self._seed = seed
|
||||||
|
|
||||||
|
def generate(self, steps: int = 10) -> List[Tuple[LocalArbProblem, SharedSignals]]:
|
||||||
|
rnd = random.Random(self._seed)
|
||||||
|
venues = ["VenueA", "VenueB"]
|
||||||
|
assets = ["AAA", "BBB"]
|
||||||
|
out: List[Tuple[LocalArbProblem, SharedSignals]] = []
|
||||||
|
for i in range(steps):
|
||||||
|
venue = venues[rnd.randrange(len(venues))]
|
||||||
|
problem = LocalArbProblem(
|
||||||
|
id=f"prob-{venue}-{i}",
|
||||||
|
venue=venue,
|
||||||
|
assets=list(assets),
|
||||||
|
target_misprice=rnd.uniform(0.0, 0.01),
|
||||||
|
max_exposure=rnd.choice([100.0, 1000.0, 10000.0]),
|
||||||
|
latency_budget=rnd.uniform(0.01, 1.0),
|
||||||
|
)
|
||||||
|
# deterministic but varied price deltas and liquidity
|
||||||
|
price_deltas = {a: rnd.uniform(-0.005, 0.005) for a in assets}
|
||||||
|
# occasionally inject a shock
|
||||||
|
if rnd.random() < 0.15:
|
||||||
|
shock_asset = rnd.choice(assets)
|
||||||
|
price_deltas[shock_asset] = rnd.choice([-1.0, 1.0]) * rnd.uniform(0.001, 0.02)
|
||||||
|
signals = SharedSignals(
|
||||||
|
version=i,
|
||||||
|
price_delta_by_asset=price_deltas,
|
||||||
|
cross_corr={("AAA","BBB"): rnd.uniform(-1.0,1.0)},
|
||||||
|
liquidity_estimates={a: rnd.uniform(10.0, 10000.0) for a in assets},
|
||||||
|
)
|
||||||
|
out.append((problem, signals))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
class ReplayRunner:
|
||||||
|
"""Runs a generated scenario through a fresh CentralCoordinator and
|
||||||
|
returns the replay log (list of PlanDelta signatures) and the final plan
|
||||||
|
signature.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.coordinator = CentralCoordinator()
|
||||||
|
|
||||||
|
def run(self, events: Iterable[Tuple[LocalArbProblem, SharedSignals]]):
|
||||||
|
for problem, sig in events:
|
||||||
|
self.coordinator.ingest_local(problem, sig)
|
||||||
|
# call reconcile to flush pending actions
|
||||||
|
self.coordinator.reconcile()
|
||||||
|
return list(self.coordinator.replay_log), (self.coordinator.last_plan.signature if self.coordinator.last_plan else None)
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
from crossvenue_arbx.fuzz import ScenarioGenerator, ReplayRunner
|
||||||
|
|
||||||
|
|
||||||
|
def test_replay_determinism_same_seed():
|
||||||
|
gen = ScenarioGenerator(seed=42)
|
||||||
|
events = gen.generate(steps=20)
|
||||||
|
|
||||||
|
runner1 = ReplayRunner()
|
||||||
|
log1, final1 = runner1.run(events)
|
||||||
|
|
||||||
|
runner2 = ReplayRunner()
|
||||||
|
# regenerate to ensure fresh objects but same seed
|
||||||
|
gen2 = ScenarioGenerator(seed=42)
|
||||||
|
events2 = gen2.generate(steps=20)
|
||||||
|
log2, final2 = runner2.run(events2)
|
||||||
|
|
||||||
|
assert log1 == log2, "Replay logs must be identical for same seed"
|
||||||
|
assert final1 == final2, "Final plan signature must match for same seed"
|
||||||
|
|
||||||
|
|
||||||
|
def test_replay_variation_different_seed():
|
||||||
|
gen = ScenarioGenerator(seed=1)
|
||||||
|
events = gen.generate(steps=20)
|
||||||
|
runner = ReplayRunner()
|
||||||
|
log1, final1 = runner.run(events)
|
||||||
|
|
||||||
|
genb = ScenarioGenerator(seed=2)
|
||||||
|
eventsb = genb.generate(steps=20)
|
||||||
|
runnerb = ReplayRunner()
|
||||||
|
log2, final2 = runnerb.run(eventsb)
|
||||||
|
|
||||||
|
# usually different seeds will produce different logs or final plan
|
||||||
|
assert (log1 != log2) or (final1 != final2)
|
||||||
Loading…
Reference in New Issue