{
 "key": "f-pit-calibration",
 "approach": "Overlay on N (sha256 bf0d179f...ffae, verified; N edge on seed 310000 = 17.116002, matches). A one-step predictive for the competitor mid move dm (ticks): mass 1-k*pi at 0 plus k*pi*Laplace(0, c*b), with pi and b as EWMAs (alpha 0.05). In each run it computes randomized PIT values: k is recalibrated so the PIT tail rate (u<0.05 or u>0.95) tracks 10%, and c so the mean PIT of nonzero move sizes tracks 0.5. Crossing chance q = chance the next move reaches N's nearest sizable (qty>0.05) bid or ask, measured from the midpoint of N's own quotes. When q is at or above the 90th percentile of this run's earlier q values (after 50 warm-up steps), N's nearest sizable order on each side is withdrawn for that step: a resting one is cancelled, a new one is dropped. It is kept visible to N in state.own_orders (dataclasses.replace, with reserved and free cash adjusted) and re-placed with the same id next step unless N cancels it. Acts only when s=2.",
 "tried": [
  "Withdrawing N's literal nearest orders: these are almost always 0.01-lot orders (about 12k of 12k touch slots on 3 seeds vs 39 sizable). Withdrawing them and letting them vanish from own_orders made arb loss far worse (seed 310000: arb -8.23 -> -14.71). Hiding them from N while held still gave arb -24.60, so they appear to feed N's own estimation. Restricted the overlay to orders with qty > 0.05.",
  "Variant A, centred on N's quote midpoint, tuning 310000..310199 with all spreads active, mean diff per run by spread: s1 -3.27 (se 1.50), s2 -2.14 (0.57), s3 -7.38 (1.88), s4 -3.85 (6.67). Arb improved on every spread (+0.50, +1.68, +3.12, +10.05) but retail fell more.",
  "Variant B, centred on the competitor mid: s1 -3.22, s2 -2.20, s3 -7.70, s4 -4.12. Almost identical to A.",
  "Froze A with spread set {2}, the least negative. No spread had a positive tuning mean."
 ],
 "next": "Do not promote. Withdrawing the sizable touch order on high-volatility steps cuts arb loss (+2.03 per spread-2 run on holdout) but costs about 3x that in retail edge, and re-placing loses queue priority. If this is revisited, use the calibrated crossing chance to resize (for example halve) the order instead of removing it, or apply it only after a detected competitor-ladder jump. Leave N's 0.01-lot orders untouched: they appear to carry N's information.",
 "spreads_acted": [
  2
 ],
 "off_equals_n": true,
 "code": "import bisect as _bi\nimport math as _m\nfrom dataclasses import replace as _rep\nfrom orderbook_pm_challenge.types import OwnOrderView as _OV\nfrom orderbook_pm_challenge.types import CancelAll as _CA\nfrom orderbook_pm_challenge.types import CancelOrder as _CO\nfrom orderbook_pm_challenge.types import PlaceOrder as _PO\nfrom orderbook_pm_challenge.types import Side as _S\n\n\nclass Strategy(BaseStrategy):\n    \"\"\"PIT-calibrated one-step predictive for the competitor-quote mid move.\n\n    Predictive for dm (competitor mid change, ticks): mass (1-k*pi) at 0 plus\n    k*pi * Laplace(0, c*b).  pi, b are EWMAs; k is recalibrated from the run's\n    randomized-PIT tail rate (target 10%), c from the mean PIT of the size of\n    nonzero moves (target 0.5).  Crossing chance q = chance that the next move\n    reaches N's nearest bid or ask from the centre.  When q is in the top\n    decile of this run's past q values, N's nearest order on each side is\n    withdrawn for that step.\n    \"\"\"\n\n    SPREADS = (2,)\n    CENTER_N = True\n    A = 0.05\n    ETA = 0.02\n    WARM = 50\n    QTL = 0.9\n    QMIN = 0.05\n\n    def __init__(self):\n        self._n = _N()\n        self._s = None\n        self._m = None\n        self._pi = 0.02\n        self._b = 1.0\n        self._k = 1.0\n        self._c = 1.0\n        self._hist = []\n        self._held = {}\n        self._wid = 0\n\n    def _pit(self, x):\n        p = min(0.95, max(1e-4, self._k * self._pi))\n        sc = max(0.05, self._c * self._b)\n        lo, hi = 0.05, 0.95\n        if x == 0:\n            a, z = p / 2.0, 1.0 - p / 2.0\n            w = max(0.0, min(z, lo) - a) + max(0.0, z - max(a, hi))\n            tail = w / (z - a)\n        else:\n            if x > 0:\n                u = (1.0 - p) + p * (1.0 - 0.5 * _m.exp(-x / sc))\n            else:\n                u = p * 0.5 * _m.exp(x / sc)\n            tail = 1.0 if (u < lo or u > hi) else 0.0\n            v = 1.0 - _m.exp(-abs(x) / sc)\n            self._c *= _m.exp(self.ETA * 2.0 * (v - 0.5))\n            self._c = min(20.0, max(0.05, self._c))\n        self._k *= _m.exp(self.ETA * (tail - 0.1) * 10.0)\n        self._k = min(20.0, max(0.05, self._k))\n        a = self.A\n        self._pi += a * ((1.0 if x != 0 else 0.0) - self._pi)\n        if x != 0:\n            self._b += a * (abs(x) - self._b)\n\n    def on_step(self, state):\n        bb, ba = state.competitor_best_bid_ticks, state.competitor_best_ask_ticks\n        if self._s is None:\n            if bb is not None and ba is not None:\n                self._s = max(1, (ba - bb + 1) // 2)\n            else:\n                self._s = 0\n            self._b = float(max(1, self._s))\n        if self._s not in self.SPREADS:\n            return self._n.on_step(state)\n        m = None\n        if bb is not None and ba is not None:\n            m = (bb + ba) / 2.0\n        if m is not None and self._m is not None:\n            self._pit(m - self._m)\n        if m is not None:\n            self._m = m\n        held = self._held\n        view = state\n        if held:\n            res_c = 0.0\n            vs = list(state.own_orders)\n            for oid, (sd, px, qt, st) in held.items():\n                vs.append(_OV(oid, sd, px, qt, st))\n                res_c += (px if sd is _S.BUY else 100 - px) * qt / 100.0\n            vs.sort(key=lambda o: (o.side.value, o.price_ticks if o.side is _S.SELL else -o.price_ticks))\n            view = _rep(state, own_orders=tuple(vs), reserved_cash=state.reserved_cash + res_c,\n                        free_cash=state.free_cash - res_c)\n        acts = list(self._n.on_step(view))\n        book = {o.order_id: o for o in state.own_orders}\n        new = []\n        out = []\n        for a in acts:\n            if isinstance(a, _CA):\n                book.clear()\n                held.clear()\n                new = []\n                out.append(a)\n            elif isinstance(a, _CO):\n                if a.order_id in book:\n                    del book[a.order_id]\n                    out.append(a)\n                elif a.order_id in held:\n                    del held[a.order_id]\n                else:\n                    hit = [i for i, (j, p) in enumerate(new) if p.client_order_id == a.order_id]\n                    if hit:\n                        new.pop(hit[0])\n                        out.append(a)\n            else:\n                new.append((len(out), a))\n                out.append(a)\n        Q = self.QMIN\n        allo = [(o.side, o.price_ticks, o.remaining_quantity) for o in book.values()]\n        allo += [(p.side, p.price_ticks, p.quantity) for _, p in new]\n        allo += [(v[0], v[1], v[2]) for v in held.values()]\n        bids = [x[1] for x in allo if x[0] is _S.BUY]\n        asks = [x[1] for x in allo if x[0] is _S.SELL]\n        bq = [x[1] for x in allo if x[0] is _S.BUY and x[2] > Q]\n        aq = [x[1] for x in allo if x[0] is _S.SELL and x[2] > Q]\n        nb = max(bq) if bq else None\n        na = min(aq) if aq else None\n        if self.CENTER_N and bids and asks:\n            ctr = (max(bids) + min(asks)) / 2.0\n        else:\n            ctr = self._m\n        fire = False\n        if ctr is not None and (nb is not None or na is not None):\n            p = min(0.95, max(1e-4, self._k * self._pi))\n            sc = max(0.05, self._c * self._b)\n            q = 0.0\n            if nb is not None:\n                q += 0.5 * p * _m.exp(-max(0.0, ctr - nb) / sc)\n            if na is not None:\n                q += 0.5 * p * _m.exp(-max(0.0, na - ctr) / sc)\n            h = self._hist\n            if len(h) >= self.WARM:\n                fire = q >= h[min(len(h) - 1, int(self.QTL * len(h)))]\n            _bi.insort(h, q)\n\n        def near(sd, px, qt):\n            return qt > Q and ((sd is _S.BUY and px == nb) or (sd is _S.SELL and px == na))\n\n        drop = set()\n        tail = []\n        if fire:\n            for i, p_ in new:\n                if near(p_.side, p_.price_ticks, p_.quantity):\n                    drop.add(i)\n                    oid = p_.client_order_id\n                    if not oid:\n                        self._wid += 1\n                        oid = \"pitw%d\" % self._wid\n                    held[oid] = (p_.side, p_.price_ticks, p_.quantity, state.step)\n            for o in book.values():\n                if near(o.side, o.price_ticks, o.remaining_quantity):\n                    tail.append(_CO(o.order_id))\n                    held[o.order_id] = (o.side, o.price_ticks, o.remaining_quantity, o.submitted_step)\n        head = []\n        for oid in list(held):\n            sd, px, qt, st = held[oid]\n            if fire and near(sd, px, qt):\n                continue\n            if st == state.step:\n                continue\n            del held[oid]\n            head.append(_PO(sd, px, qt, oid))\n        return head + [a for i, a in enumerate(out) if i not in drop] + tail\n",
 "code_sha256": "79c32cab81d9c5d1d3938910f81600327f9fae8da58b86b4856d1c618a13fa7c",
 "assembled_sha256": "bbe0ab7f36618704121f91bf89bfd53d6d991548a59f8a89b692cd4c19001aee",
 "tuning": {
  "n": 200,
  "mean_diff": -0.608981,
  "se": 0.174788
 },
 "paired": {
  "first_seed": 410000,
  "last_seed": 410499,
  "n": 500,
  "complete": true,
  "incumbent_mean": 104.413753,
  "mean_diff": -0.874101,
  "se": 0.149297,
  "sum_d": -437.050266,
  "sum_d2": 5943.284315,
  "retail_diff": -1.345242,
  "arb_diff": 0.471141,
  "failures": 0
 },
 "by_spread": {
  "1": {
   "runs": 115,
   "incumbent_mean": 28.495154,
   "mean_diff": 0.0,
   "se": 0.0,
   "sum_d": 0.0,
   "sum_d2": 0.0,
   "retail_diff": 0.0,
   "arb_diff": 0.0
  },
  "2": {
   "runs": 116,
   "incumbent_mean": 97.766262,
   "mean_diff": -3.767675,
   "se": 0.567526,
   "sum_d": -437.050266,
   "sum_d2": 5943.284315,
   "retail_diff": -5.798457,
   "arb_diff": 2.030782
  },
  "3": {
   "runs": 138,
   "incumbent_mean": 128.405451,
   "mean_diff": 0.0,
   "se": 0.0,
   "sum_d": 0.0,
   "sum_d2": 0.0,
   "retail_diff": 0.0,
   "arb_diff": 0.0
  },
  "4": {
   "runs": 131,
   "incumbent_mean": 151.672482,
   "mean_diff": 0.0,
   "se": 0.0,
   "sum_d": 0.0,
   "sum_d2": 0.0,
   "retail_diff": 0.0,
   "arb_diff": 0.0
  }
 },
 "seconds_per_simulation": 0.37
}