{
 "approach": "Keeps the base whole (tick probes, sub-tick belief, quotes one tick inside the competitor, marginal sizing where one more share is quoted while retail reach times edge exceeds expected arbitrage loss, cheap YES stock bought on the tick-1 crossing) and adds a per-side, per-price weight on the jump part of that expected loss. A diagnostic copy of the simulator split arb loss on jump steps by side and price: in the middle prices asks lose about twice what bids do (3.63 vs 1.57 a run on seeds 1000-1599), concentrated at 10-40c, and on outcome-1 runs with spreads 3 and 4 the mid-price ask jump loss is 4.32 and 6.91 a run. An up-jump from a low or middling p costs a resting ask far more than the matching down-jump costs a bid, and the symmetric single-size jump model undersizes that. So the jump loss term now enters the sizing rule with weight jm(side, price), linear between knots at 5c, 50c and 95c: asks 1.0 / 2.5 / 2.0, bids 0.7 / 1.0 / 1.0. Diffusion loss, the low-price stock logic and all other constants are unchanged. On seeds 1000-1599 arb loss fell from 28.56 to 27.11 a run (outcome-1 spread-4 runs: mid ask jump loss 6.91 to 5.02) and the score rose 0.76\u00b10.25 paired; on holdout 30000-30599 it rose only 0.15\u00b10.12 (96.19 vs 96.04), because most of the arb saved is given back in retail.",
 "blocked": null,
 "python_version": "3.15.0",
 "base_mean_edge": 102.33197908899267,
 "score": {
  "mean_edge": 108.00451371283867,
  "retail_edge": 135.11283915310833,
  "arb_edge": -27.108325440305002,
  "simulations": 600,
  "seed_start": 1000,
  "failures": 0
 },
 "holdout": {
  "mean_edge": 96.19078601230667,
  "simulations": 600,
  "seed_start": 30000,
  "failures": 0
 },
 "code": "\"\"\"Stale-quote defence with tick probes, a sub-tick belief and cheap YES stock.\n\nEvery step the arbitrageur removes any order on the wrong side of p, so tiny\ntwo-sided probes around the estimate locate p to a tick; a grid filter narrows\nthat further.  Quotes sit one tick inside the competitor and are sized by the\nmarginal rule: one more share is worth quoting while the chance a retail order\nreaches it, times its edge, exceeds the expected arbitrage loss.  Near p=0 a\nretail buy pays almost its whole notional as edge, so YES is held to cover low\nasks.  That YES is bought when it is cheap: a large bid rests at tick 1 while p\nis above it, so the arbitrageur fills it on the crossing for about 0.1 tick a\nshare instead of nearly a full tick once p is near zero, and smaller bids sit on\nwhichever tick p is within 0.15 of.  Below tick 1 the estimate of p decays with\nthe time spent there, since no probe can see it.\n\nJump weights per side and price: the expected loss to a jump through a resting\norder enters the sizing rule with a weight that depends on the side and on the\norder's price (linear between knots at 5c, 50c and 95c).  Asks in the middle and\nupper prices carry 2.5x and 2x the jump loss, since an up-jump from a low or\nmiddling p costs an ask far more than the matching down-jump costs a bid; bids\nnear zero carry 0.7x.\n\"\"\"\nimport math\nfrom orderbook_pm_challenge.strategy import BaseStrategy\nfrom orderbook_pm_challenge.types import CancelOrder, PlaceOrder, Side\n\nP = dict(lam_r=0.25, m=5.0, sig_n=1.2, lam_j=0.0019, sig_j=0.35, sig=0.02,\n         qmax=3000.0, rmin=2, kA=0.7, kL=0.15, pL=0.05,\n         ammo=1.0, ydem=0.4, W=200.0, fy=0.6, ammo_q=60.0, ammo_pmax=0.05, ammo_maxpay=0.02,\n         fb=0.9, fu=0.7, xu=0.1, sticky=1.0, min_ahead=2.0, keep_hi=1.5, keep_lo=0.7, Hu=300.0, bins=8.0, filt=1.0, uni=0.01, jumpinfo=1.0, dmin=0.0, kLb=0.08, kLf=0.08, pinf=1.0, pre=0.0, H1=100.0,\n         ydf=0.6, Wf=2000.0, g_hi=0.15, g_lo=0.15, qf=400.0, pmaxf=0.05, pre_gap=1.0,\n         ydf1=0.6, g1=5.0, f1=0.6, a1=100.0, nb1=0.0, gs=1.0, p1t=1.0, m1=1.0, zd=1.0, pfl=0.0005,\n         jb_l=0.7, jb_m=1.0, jb_h=1.0, ja_l=1.0, ja_m=2.5, ja_h=2.0)\nSQ2 = math.sqrt(2.0)\nSQ2PI = math.sqrt(2.0 * math.pi)\ndef cdf(x): return 0.5 * (1.0 + math.erf(x / SQ2))\ndef pdf(x): return math.exp(-0.5 * x * x) / SQ2PI\ndef inv(p):\n    p = min(max(p, 1e-9), 1 - 1e-9)\n    t = math.sqrt(-2.0 * math.log(min(p, 1 - p)))\n    x = t - (2.515517 + 0.802853 * t + 0.010328 * t * t) / (1 + 1.432788 * t + 0.189269 * t * t + 0.001308 * t ** 3)\n    x = -x if p < 0.5 else x\n    for _ in range(2):\n        d = pdf(x)\n        if d < 1e-12:\n            break\n        x -= (cdf(x) - p) / d\n    return x\ndef lpm(k):\n    return pdf(k) + k * cdf(k)\ndef jm(x, l, m, h):\n    # jump-loss weight for one side, linear in price between knots at 0.05, 0.5 and 0.95\n    if x <= 0.5:\n        return l + (m - l) * max(0.0, x - 0.05) / 0.45\n    return m + (h - m) * min(0.45, x - 0.5) / 0.45\ndef fl(q):\n    return math.floor(q * 100 + 1e-9) / 100\n\nNODES = [(-2.2, .06), (-1.3, .17), (-0.6, .27), (0.6, .27), (1.3, .17), (2.2, .06)]\nPROBE = 0.01\n\n\nclass Strategy(BaseStrategy):\n    def __init__(self):\n        self.n = 0\n        self.ahead = set()     # main orders placed strictly ahead of any competitor order at their tick\n        self.res = {}          # order id -> uncovered quantity at placement\n        self.bc = None         # belief: sub-tick grid centres (ticks)\n        self.bw = None         # belief: weights\n        self.st = 0.5          # last one-step sd in ticks\n        self.pcb = self.pca = None\n        self.pb = set()\n        self.pa = set()\n        self.tb = 0\n        self.s = None          # competitor spread parameter\n\n    def _oid(self, tag):\n        self.n += 1\n        return tag + str(self.n)\n\n    def on_step(self, state):\n        H = max(1, state.steps_remaining)\n        cb = state.competitor_best_bid_ticks or 0\n        ca = state.competitor_best_ask_ticks or 100\n        if self.s is None:\n            self.s = max(1, (ca - cb + 1) // 2)\n        lo, hi = cb, ca\n        for o in state.own_orders:\n            if o.side is Side.BUY:\n                lo = max(lo, o.price_ticks)\n            else:\n                hi = min(hi, o.price_ticks)\n        if P['pinf'] > 0:\n            sb = {o.price_ticks for o in state.own_orders if o.side is Side.BUY and o.remaining_quantity <= PROBE + 1e-9}\n            sa = {o.price_ticks for o in state.own_orders if o.side is Side.SELL and o.remaining_quantity <= PROBE + 1e-9}\n            ea = self.pa - sa\n            eb = self.pb - sb\n            if sa and ea:\n                lo = max(lo, min(max(ea), hi))\n            if eb and (sb or min(eb) <= P['p1t']):\n                hi = min(hi, max(min(eb), lo))\n        if P['jumpinfo'] > 0 and self.pcb is not None:\n            if ca > self.pca + 1:\n                lo = max(lo, ca - 1)\n            if cb < self.pcb - 1:\n                hi = min(hi, cb + 1)\n        self.pcb, self.pca = cb, ca\n        if lo > hi:\n            lo, hi = hi, lo\n        # sub-tick belief over p_(t-1): predict with one-step noise, truncate to [lo, hi]\n        wid = max(hi - lo, 1e-6)\n        nb = max(2, min(48, int(round(wid * P['bins']))))\n        cs = [lo + (i + 0.5) * wid / nb for i in range(nb)]\n        if self.bc is None or P['filt'] <= 0:\n            ws = [1.0 / nb] * nb\n        else:\n            sg = max(self.st, 0.6 * wid / nb)\n            k = -0.5 / (sg * sg)\n            ws = []\n            uni = P['uni'] * sg\n            for c in cs:\n                t = uni\n                for ci, wi in zip(self.bc, self.bw):\n                    d = c - ci\n                    d2 = d * d * k\n                    if d2 > -12.5:\n                        t += wi * math.exp(d2)\n                ws.append(t)\n            tot = sum(ws)\n            ws = [w / tot for w in ws]\n        self.bc, self.bw = cs, ws\n        mean_t = sum(c * w for c, w in zip(cs, ws))\n        var_t = max(sum(c * c * w for c, w in zip(cs, ws)) - mean_t * mean_t, 0.0)\n        phat = min(max(mean_t / 100.0, 0.002), 0.998)\n        var = P['sig'] ** 2 + P['lam_j'] * P['sig_j'] ** 2\n        sd = math.sqrt(H * var)\n        self.tb = self.tb + 1 if hi <= 1 else 0\n        if self.tb > 0 and P['zd'] > 0:\n            # below the last tick there is no probe: the score keeps walking away from it\n            phat = min(phat, max(P['pfl'], cdf(inv(0.01) - P['zd'] * 0.02 * math.sqrt(self.tb) / sd)))\n        z = inv(phat)\n        Z = z * sd\n        sp = pdf(z) / sd * P['sig']\n        self.st = sp * 100.0\n        s = math.sqrt(sp * sp + var_t / 1e4 + 1e-8)\n        sd1 = math.sqrt(max(1.0, H - 1.0) * var)\n        jp = [(cdf((Z + n * P['sig_j']) / sd1), w) for n, w in NODES]\n        lj = P['lam_j']\n\n        def AB(x, mb=1.0, ma=1.0):\n            k = (x - phat) / s\n            a = s * lpm(k)\n            b = a - (x - phat)\n            ja = sum(w * max(0.0, x - p) for p, w in jp)\n            jb = sum(w * max(0.0, p - x) for p, w in jp)\n            return (1 - lj) * a + lj * ja * mb, (1 - lj) * b + lj * jb * ma\n\n        if H == 1:\n            def AB(x, mb=1.0, ma=1.0):\n                return (1 - phat) * x, phat * (1 - x)\n        rho = P['lam_r'] * 0.5\n        mu = math.log(P['m']) - 0.5 * P['sig_n'] ** 2\n        kA = P['kL'] if phat < P['pL'] else P['kA']\n\n        def qsize(loss, gain, px):\n            loss *= kA\n            if gain <= 0:\n                return 0.0\n            tail = loss / (rho * gain)\n            if tail >= 1.0:\n                return 0.0\n            return min(P['qmax'], math.exp(mu + P['sig_n'] * inv(1 - max(tail, 1e-6))) / px)\n\n        ptk = phat * 100\n        def qbid(t):\n            if not 1 <= t <= 99:\n                return 0.0\n            a, b = AB(t / 100.0, mb=jm(t / 100.0, P['jb_l'], P['jb_m'], P['jb_h']))\n            if phat * 100 - t < P['dmin']:\n                return 0.0\n            if P['nb1'] > 0 and self.s == 1 and t == 1 and ptk < 1.0:\n                return 0.0\n            if phat < P['pL']:\n                ytg = P['ydem'] * min(H, P['W']) / max(ca - 1, ptk + 1) * 100.0\n                a *= (P['kLb'] if state.yes_inventory < ytg else P['kLf']) / P['kL']\n            return qsize(a, b, max(phat, 0.05))\n\n        def qask(t):\n            if not 1 <= t <= 99:\n                return 0.0\n            a, b = AB(t / 100.0, ma=jm(t / 100.0, P['ja_l'], P['ja_m'], P['ja_h']))\n            if t - phat * 100 < P['dmin']:\n                return 0.0\n            return qsize(b, a, t / 100.0)\n\n        bids = [o for o in state.own_orders if o.side is Side.BUY and o.remaining_quantity > PROBE + 1e-9]\n        asks = [o for o in state.own_orders if o.side is Side.SELL and o.remaining_quantity > PROBE + 1e-9]\n        sticky = P['sticky'] > 0\n        def top(orders, comp, step):\n            if sticky and sum(o.remaining_quantity for o in orders if o.price_ticks == comp and o.order_id in self.ahead) >= P['min_ahead']:\n                return comp\n            return comp + step\n        bid_t = top(bids, cb, 1)\n        ask_t = top(asks, ca, -1)\n        qb = qbid(bid_t)\n        qa = qask(ask_t)\n\n        Y = state.yes_inventory\n        if ask_t == 1 and H > P['H1'] and P['pre'] > 0:\n            qa = qa if Y >= P['a1'] > 0 else 0.0          # let the competitor's 2-tick ask erode instead of fronting it for nothing\n        # ammunition: hold YES so that low asks are covered\n        ptick = phat * 100\n        amt = 0\n        qam = 0.0\n        if P['ammo'] > 0 and ask_t >= 2 and phat < max(P['ammo_pmax'], P['pmaxf']):\n            wealth = state.cash + Y * phat\n            x = max(ask_t, ptick + 1) / 100.0\n            capw = P['fy'] * wealth / max(phat, 0.005)\n            target = min(P['ydem'] * min(H, P['W']) / x, capw)\n            full = min(P['ydf'] * min(H, P['Wf']) / x, capw) if phat < P['pmaxf'] else 0.0\n            t = max(1, min(99, int(math.ceil(ptick + 1e-9))))\n            g = t - ptick\n            if Y < full and g <= P['g_hi'] and bid_t <= t < ask_t:\n                amt, qam = t, min(P['qf'], full - Y)     # a tick p has just crossed: nearly free\n            elif Y < target and phat < P['ammo_pmax'] and bid_t <= t < ask_t and t / 100.0 - phat < P['ammo_maxpay']:\n                amt, qam = t, min(P['ammo_q'], target - Y)\n            elif Y < full and t >= 2 and 1.0 - g <= P['g_lo']:\n                amt, qam = t - 1, min(P['qf'], full - Y)  # rest just under p: filled only on the crossing, or by retail\n\n        if P['pre'] > 0 and ca == 2 and phat < 0.01 and H > P['H1'] and Y < P['pre'] and not qam and 1.0 - ptick < P['pre_gap']:\n            amt, qam = 1, min(P['ammo_q'], P['pre'] - Y)\n        q1 = 0.0\n        if P['ydf1'] > 0 and P['m1'] < ptick < P['g1'] + 1.0 and amt != 1:\n            q1 = max(0.0, P['ydf1'] * min(H, P['Wf']) / (self.s / 100.0) - Y)\n        # order management\n        actions = []\n        free = state.free_cash - 2.0\n        keep_ask = 0.0\n        new_b = []\n        new_a = []\n        def cancel(o):\n            nonlocal free\n            actions.append(CancelOrder(o.order_id))\n            x = o.price_ticks / 100.0\n            if o.side is Side.BUY:\n                free += x * o.remaining_quantity\n            else:\n                free += (1 - x) * min(self.res.get(o.order_id, 0.0), o.remaining_quantity)\n            self.ahead.discard(o.order_id)\n            self.res.pop(o.order_id, None)\n        for o in state.own_orders:\n            if o.remaining_quantity <= PROBE + 1e-9:\n                cancel(o)\n        kept_ids = set()\n        def manage(orders, t, q, comp):\n            kept = 0.0\n            for o in orders:\n                if o.price_ticks == t and q > 0 and kept + o.remaining_quantity <= max(q * P['keep_hi'], 1.0):\n                    kept += o.remaining_quantity\n                    kept_ids.add(o.order_id)\n                else:\n                    cancel(o)\n            return kept\n        kb = manage(bids, bid_t, qb, cb)\n        ka = manage(asks, ask_t, qa, ca)\n        add_b = (qb - kb) if (kb < qb * P['keep_lo'] and bid_t != cb) else 0.0\n        add_a = (qa - ka) if (ka < qa * P['keep_lo'] and ask_t != ca) else 0.0\n\n        budget = P['fb'] * max(free, 0.0)\n        cost = add_b * bid_t / 100.0 + qam * amt / 100.0\n        if cost > budget:\n            f = budget / cost\n            add_b *= f\n            qam *= f\n            cost = budget\n        out = []\n        q1 = min(q1, P['f1'] * max(budget - cost, 0.0) * 100.0)\n        if q1 >= 0.01:\n            out.append(PlaceOrder(side=Side.BUY, price_ticks=1, quantity=fl(q1)))\n            free -= fl(q1) / 100.0 + 1e-6\n        if amt and qam >= 0.01:\n            q = fl(qam)\n            out.append(PlaceOrder(side=Side.BUY, price_ticks=amt, quantity=q))\n            free -= q * amt / 100.0 + 1e-6\n        if bid_t >= 1 and add_b >= 0.01:\n            q = fl(add_b)\n            oid = self._oid('b')\n            out.append(PlaceOrder(side=Side.BUY, price_ticks=bid_t, quantity=q, client_order_id=oid))\n            free -= q * bid_t / 100.0 + 1e-6\n            if bid_t > cb:\n                self.ahead.add(oid)\n        if ask_t <= 99 and add_a >= 0.01:\n            x = ask_t / 100.0\n            covk = sum(o.remaining_quantity - min(self.res.get(o.order_id, 0.0), o.remaining_quantity)\n                       for o in asks if o.order_id in kept_ids)\n            cov = max(0.0, Y - covk - 0.1)\n            unc = max(0.0, add_a - cov)\n            if unc > 0:\n                ub = max(0.0, free - (1 - P['fu']) * max(state.cash, 0.0)) if x >= P['xu'] else 0.0\n                if H < P['Hu'] and phat < 0.05:\n                    ub = max(0.0, free * (1 - H / P['Hu']))\n                unc = min(unc, ub / (1 - x))\n            q = fl(min(add_a, cov + unc))\n            if q >= 0.01:\n                oid = self._oid('a')\n                out.append(PlaceOrder(side=Side.SELL, price_ticks=ask_t, quantity=q, client_order_id=oid))\n                r = max(0.0, q - cov)\n                self.res[oid] = r\n                free -= r * (1 - x) + 1e-6\n                if ask_t < ca:\n                    self.ahead.add(oid)\n\n        # probes: tiny orders that the arbitrageur removes when p crosses them\n        f = int(math.floor(ptick + 1e-9))\n        r = max(int(P['rmin']), int(math.ceil(2.0 * s * 100)))\n        self.pb = set()\n        self.pa = set()\n        if free > 1.0:\n            for t in range(f, f + r + 1):\n                if cb < t < ca and 1 <= t <= 99:\n                    out.append(PlaceOrder(side=Side.BUY, price_ticks=t, quantity=PROBE))\n                    self.pb.add(t)\n            for t in range(f + 1 - r, f + 2):\n                if cb < t < ca and 1 <= t <= 99:\n                    out.append(PlaceOrder(side=Side.SELL, price_ticks=t, quantity=PROBE))\n                    self.pa.add(t)\n        return actions + out\n",
 "code_sha256": "1c3706e59c15eda508345a0455a397e6c6c28761b8db42c4a9fdd7e26607594b",
 "tried": [
  "Scorer check: base on seeds 0-599 gave 102.33197908899267, the author's figure exactly. Base on tuning seeds 1000-1599: 107.244 (retail 135.80, arb -28.56); base on holdout 30000-30599: 96.041 (arb -28.33), measured for comparison only. --workers is refused on this machine (os.sysconf SC_SEM_NSEMS_MAX is not permitted), so every score is 14 separate seed-range processes merged.",
  "Diagnosis (instrumented copy of the simulator, jump step = score move above 0.1, seeds 1000-1599, base): arb loss on jump steps is J asks mid (6-94c) -3.63 a run, J bids mid -1.57, J asks low -4.48 (almost all from 2 runs: seed 1374 alone -969), J bids low -3.05. On outcome-1 runs the mid-price jump loss is 5.88 (spread 3) and 7.96 (spread 4), mostly asks (4.32, 6.91); by tick it sits at 10-40c. Asks lose about twice what bids do on jumps in the middle prices, which a symmetric jump model does not size for.",
  "Online jump learning (count score moves above thr from the belief while 4c<p<96c, Gamma prior on rate, update jump size): rate estimate correlates 0.34 with the true rate and runs low (mean 0.00157 vs 0.00184); ad on -0.56\u00b10.38, a0 2 -0.97, thr 0.08 -0.20, thr 0.18 -0.59, fixed size -0.32, raise-only -0.13; dropped.",
  "Fat-tailed jump model (average jump nodes over sizes x(1-m,1,1+m)): mix 0.4 -0.62\u00b10.61, mix 0.6 -0.48, with side weights +0.01 and -0.39; arb loss rose; dropped.",
  "Per-run retail flow estimate (filled notional on front main quotes on steps with no crossing, vs the lognormal flow model; rho scaled by the ratio): arb -28.56 to -23.94 but retail fell to 129.69, net -1.49\u00b10.72; the estimator read 0.39 of the model on average and correlated only 0.22 with true flow; dropped.",
  "Time-of-run weight on the jump term (1+jT(0.5-H/2000)): jT +1 -0.42 (vs the kept rule), jT -1 -0.40; dropped.",
  "Single side/price jump weights against base on seeds 1000-1599 (paired): ja_m 0.6 -0.14, 1.5 +0.20, 2.0 +0.37, 3.0 +0.40; ja_l 2 -0.59, 3 -1.35 (noisy, low-ask outliers); ja_h 2 +0.03; jb_m 0.6 -0.01, 2 -0.25; jb_h 2 -0.19; jb_l 2 -0.34, 0.7 +0.36, 0.4 +0.25.",
  "Combined side/price weights (paired, seeds 1000-1599): ja_m 2.5+ja_l 0.7 -0.32; ja_m 2.5+ja_h 2 +0.34; ja_m 4+ja_h 2+jb_l 0.7 +0.60; ja_m 2.5+ja_h 3+jb_l 0.7 +0.75; +jb_m 0.7 +0.77; +quarter knot ja_q 3 +0.51, ja_q 1.5 +0.75, jb_q 1.5 +0.60; spread>=3 only +0.42; ja_h 1.5+jb_m 0.8 +0.77; jb_h 1.3 +0.69. Plateau: arb loss falls about 1.5 a run, retail falls almost as much.",
  "Kept: ja_m 2.5, ja_h 2.0, jb_l 0.7 (others 1): seeds 1000-1599 108.005 (+0.760\u00b10.251 paired, arb -27.11 vs -28.56); outcome-1 runs, mid jump loss on asks 4.32 to 3.49 (spread 3) and 6.91 to 5.02 (spread 4), arb on those runs +1.30 and +2.36. Holdout 30000-30599 96.191 vs base 96.041 (+0.150\u00b10.117 paired). --sandbox on 20 simulations: 20/20, mean 96.858462, same as unsandboxed (Python 3.15.0; examples/starter_strategy.py also passes)."
 ],
 "next": "The size lever is saturated: every jump weight, time weight and fat-tail model I tried trades arb loss for retail almost one for one, so cutting mid jump loss further needs flow information, not risk weights. The most promising thread is the per-run retail flow estimate: shrinking quotes to it cut arb from 28.56 to 23.94 a run, but my estimator read only 0.39 of the model's flow on average (0.17 on spread 2) and correlated 0.22 with the true flow, so it shrank good runs too. Find why the predicted front-of-queue fills overshoot (ammo bids above the main bid? kept orders not at the front? the hi<ask test?), then size each side to measured flow. Low-price ask jump loss (4.5 a run) is a few huge outliers (one run -969) from selling stock at 1-5c before an up-jump; a cap on stock sold per step below 5c might be worth testing on more seeds."
}