{"approach":"Keep the B strategy unchanged except require the main bid to be at least dmb percentage points below phat (return zero size inside that distance); leave the ask distance gate at zero. Compare dmb values 0.3, 0.5, and 1.0 on the paired tuning set, freeze the highest tuning mean, and evaluate once on the separate holdout.","tried":[{"constants":{"dmb":0.3},"T":{"seeds":[110000,110499],"n":500,"mean_diff":0.0278306440060001,"se":0.14156096544366042}},{"constants":{"dmb":0.5},"T":{"seeds":[110000,110499],"n":500,"mean_diff":-2.2496688284422,"se":0.627286990857069}},{"constants":{"dmb":1.0},"T":{"seeds":[110000,110499],"n":500,"mean_diff":-5.3261390953804,"se":0.9656732337144791}}],"next":"The 0.3 threshold was effectively neutral on tuning and lost on holdout, while larger thresholds lost on tuning. Keep B as incumbent and investigate a different single mechanism for reducing arbitrage fills.","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\"\"\"\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)\nP.update({'W': 50, 'ammo_pmax': 0.02, 'fy': 0.7, 'fu': 0.25, 'Hu': 500})\nC = {'W': 50, 'ammo_pmax': 0.02, 'fy': 0.7, 'reserve': 200, 'scar': 0.0025, 'fu': 0.25, 'Hu': 500}\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 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):\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, (1 - lj) * b + lj * jb\n\n        if H == 1:\n            def AB(x):\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)\n            if phat * 100 - t < 0.3:\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)\n            if t - phat * 100 < 0:\n                return 0.0\n            if phat < C.get('scar_p', .1):\n                a = max(0.0, a - C.get('scar', 0.0) * (1 - min(1.0, state.yes_inventory / C.get('stock', 3000.0))))\n            return min(C.get('askcap', P['qmax']), 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 = min(C.get('bidcap', 3000.0), 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 not (C.get('recover', 0) and o.side is Side.SELL and t < comp and self.res.get(o.order_id, 0.0) > 1 and state.yes_inventory > C.get('recover_yes', 1) and state.free_cash < C.get('recover_cash', 200)) and 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                ub = min(ub, max(0.0, free - C.get('reserve', 0.0) * min(1.0, H / C.get('release', 50.0))))\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":"134d94be692fce4f5ba0cec0d8e41f5537885fff1f23cddee49d2c7828de6251","incumbent_code_sha256":"baef955ec10728839f7c3357016ec9eb0550e7ec689bbd5171b1cdd5c9e37273","tuning_score":{"seeds":[110000,110499],"n":500,"mean_diff":0.0278306440060001,"se":0.14156096544366042},"paired":{"seeds":[210000,210999],"n":1000,"complete":true,"mean_diff":-0.34563982503279944,"se":0.16317209871084296,"sum_d":-345.6398250327994,"sum_d2":26717.975552552078,"candidate_mean":106.1910145025344,"incumbent_mean":106.5366543275672,"retail_diff":-0.5101146876348004,"retail_se":0.09801974776824805,"arb_diff":0.16447486260689984,"arb_se":0.1250726608344411,"failures":[]},"seconds_per_simulation":0.16274954521656038}