From b74ff41344b3151857f4a50b2c5ca8fee636e6c6 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:11:09 +0200 Subject: [PATCH] tsp-arb-latency: lower trigger 50 -> 25 bps + credit sub-poll closures (#1212) Bench 080 published only 7 samples across 3 of 11 assets in 24h at 50 bps trigger. Two contributing causes fixed together: 1) Trigger from 50 -> 25 bps. 50 bps in 90s is a headline move on Nasdaq (earnings, macro prints), not a steady-state event. 25 bps is above normal intra-minute noise on liquid stocks and captures real arb-worthy moves without collapsing the semantic. 2) Sub-poll convergence credited. When the reference moves past the trigger AND the pool is already within band on the same tick, the arb closed inside one poll interval. Previously dropped silently; now observed at pollInterval (60s) so the fast-arb tail on liquid pools stops disappearing from the histogram. Convergence threshold stays at 20 bps: the "how close is the pool to fair" question is unchanged, only trigger sensitivity moves. Spec text (subtitle, methodology, FAQ) updated to 25 bps throughout. Co-authored-by: Florent Tapponnier --- .../cmd/script/arb_latency.go | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/harnesses/tokenized-stock-peg/cmd/script/arb_latency.go b/harnesses/tokenized-stock-peg/cmd/script/arb_latency.go index 48244f60..8ebaa937 100644 --- a/harnesses/tokenized-stock-peg/cmd/script/arb_latency.go +++ b/harnesses/tokenized-stock-peg/cmd/script/arb_latency.go @@ -27,10 +27,18 @@ import ( // - if in flight but not yet converged, keep waiting (no cap: the // "unresolved" case is the story we want to tell too) +// Trigger threshold lowered from 50 bps to 25 bps on 2026-07-16 after +// 24h of live data returned only 7 samples across 3 of 11 assets. +// Nasdaq stocks routinely move 25 bps in a 90s window while a 50 bps +// move is a headline event, so the tighter cohort under-sampled the +// steady-state arb latency the bench is meant to measure. Convergence +// band stays at 20 bps: the "how close is the pool to fair" question +// is unchanged, only the trigger sensitivity moves. const ( - arbTriggerBps = 50.0 - arbConvergedBps = 20.0 - arbMoveWindow = 90 * time.Second // ~1 minute plus one poll jitter + arbTriggerBps = 25.0 + arbConvergedBps = 20.0 + arbMoveWindow = 90 * time.Second // ~1 minute plus one poll jitter + subPollLatencySec = 60.0 // credit sub-poll arb closures at one pollInterval ) var ( @@ -111,9 +119,7 @@ func (t *arbTracker) observe(asset, issuer string, now time.Time, ref, pool floa if !inFlight { // Start a new event only if the reference actually moved and - // the pool is currently out of the convergence band. If the - // pool was already within band during the move, arbs closed - // it faster than one tick, credit as "sub-poll". + // the pool is currently out of the convergence band. if moveBps >= arbTriggerBps && devBps > arbConvergedBps { t.open[asset] = arbEvent{ startedAt: now, @@ -122,6 +128,18 @@ func (t *arbTracker) observe(asset, issuer string, now time.Time, ref, pool floa poolAtTrigger: pool, } tspArbOpenAgeSeconds.WithLabelValues(asset, issuer).Set(0) + return + } + // Sub-poll convergence: the reference moved past the trigger + // AND the pool is already within band on this tick, so the arb + // closed inside a single poll interval. Historically dropped; + // now credited at pollInterval seconds so the histogram picks + // up the fast-arb tail that dominates on liquid assets. Without + // this, calm sessions publish zero events for well-behaved + // pools and the leaderboard reads sparse. + if moveBps >= arbTriggerBps { + tspArbLatencySeconds.WithLabelValues(asset, issuer).Observe(subPollLatencySec) + tspArbEventTotal.WithLabelValues(asset, issuer, "converged").Inc() } return }