{
 "key": "f-nofill-bounds",
 "approach": "Overlay on N: censored no-fill likelihood. An own order present across a whole step with unchanged remaining quantity (or placed last step and still at full size) was not touched by the arbitrageur, so an unfilled bid at x gives p>=x and an unfilled ask at y gives p<=y (engine.execute_arbitrage sweeps every mispriced level). Interval [lo,hi] = intersect(own no-fill bounds, competitor best bid/ask). N's new PlaceOrders that are asks priced strictly below lo or bids strictly above hi are dropped; everything else passes through unchanged. Acts only when first-step competitor spread s=4.",
 "tried": [
  "Variant A strict (drop ask<lo, bid>hi), all spreads, tuning 310000..310199: mean_diff -0.347 se 0.288; per s: s1 0.0 (never triggered), s2 -0.611 (0.215), s3 -0.916 (0.221), s4 +0.326 (1.284)",
  "Variant B inclusive (drop ask<=lo, bid>=hi), all spreads: mean_diff -7.781 se 0.832; every spread negative",
  "Chosen: variant A, spread set {4}; tuning on s=4 only mean_diff 0.326439 se 1.284461 (n=43); holdout mean_diff -0.099236 se 0.105246: no gain",
  "Diagnostic: the no-fill/competitor interval was narrower than the competitor spread on 72.9% of steps (holdout mean over runs; tuning 74.3%), yet dropping on it lowers edge: the bound is one step stale (p moves before arb), so N's quotes it removes are mostly profitable retail-catching quotes, not arb losers."
 ],
 "next": "Do not promote. The bound information is real (interval narrower than competitor spread ~73% of steps) but as a hard drop rule it is stale by a step; try using the interval midpoint as a soft probability signal fed to N's pricing (shift quotes) rather than dropping, or condition drops on a jump detector.",
 "spreads_acted": [
  4
 ],
 "off_equals_n": true,
 "code": "class Strategy(BaseStrategy):\n    \"\"\"Overlay f-nofill-bounds: censored no-fill bounds on p.\n\n    An own bid at x that sat a whole step untouched means p >= x (else the\n    arbitrageur would have sold into it); an untouched ask at y means p <= y.\n    Together with the competitor quotes this keeps an interval [lo, hi] (ticks).\n    N's new asks priced below lo and new bids priced above hi are dropped.\"\"\"\n\n    ACT = (4,)\n    STRICT = True\n    OFF = False\n\n    def __init__(self):\n        self._n = _N()\n        self._s = None\n        self._prev = {}\n        self._placed = []\n        self.steps = 0\n        self.narrow = 0\n        self.dropped = 0\n\n    def on_step(self, state):\n        acts = self._n.on_step(state)\n        cb, ca = state.competitor_best_bid_ticks, state.competitor_best_ask_ticks\n        if self._s is None:\n            if cb is not None and ca is not None:\n                self._s = max(1, (ca - cb + 1) // 2)\n            else:\n                self._s = 0\n        lo, hi = None, None\n        cur = {}\n        placed = self._placed\n        for o in state.own_orders:\n            cur[o.order_id] = o.remaining_quantity\n            q = self._prev.get(o.order_id)\n            full = False\n            if q is not None:\n                full = abs(q - o.remaining_quantity) < 1e-9\n            elif o.submitted_step == state.step - 1:\n                for (sd, px, qq) in placed:\n                    if sd == o.side and px == o.price_ticks and abs(qq - o.remaining_quantity) < 1e-9:\n                        full = True\n                        break\n            if not full:\n                continue\n            if o.side.value == \"BUY\":\n                lo = o.price_ticks if lo is None else max(lo, o.price_ticks)\n            else:\n                hi = o.price_ticks if hi is None else min(hi, o.price_ticks)\n        self._prev = cur\n        L = cb if lo is None else (lo if cb is None else max(lo, cb))\n        H = ca if hi is None else (hi if ca is None else min(hi, ca))\n        self.steps += 1\n        if L is not None and H is not None and cb is not None and ca is not None:\n            if H - L < ca - cb:\n                self.narrow += 1\n        out = list(acts)\n        if not self.OFF and self._s in self.ACT and (lo is not None or hi is not None):\n            keep = []\n            for a in out:\n                if isinstance(a, PlaceOrder):\n                    sv = a.side.value\n                    if sv == \"SELL\" and lo is not None and (a.price_ticks < lo if self.STRICT else a.price_ticks <= lo):\n                        self.dropped += 1\n                        continue\n                    if sv == \"BUY\" and hi is not None and (a.price_ticks > hi if self.STRICT else a.price_ticks >= hi):\n                        self.dropped += 1\n                        continue\n                keep.append(a)\n            out = keep\n        self._placed = [(a.side, a.price_ticks, a.quantity) for a in out if isinstance(a, PlaceOrder)]\n        return out\n",
 "code_sha256": "bc5f1d1aeb505ca20ab5f4f6b58037c2a2a2599f9d14fdeb129e3389b439446e",
 "assembled_sha256": "d05a77cc3c3f1d8cba78a6f596fa354d220a760b5f40540a345f984572427f52",
 "tuning": {
  "n": 43,
  "mean_diff": 0.326439,
  "se": 1.284461
 },
 "paired": {
  "first_seed": 410000,
  "last_seed": 410499,
  "n": 500,
  "complete": true,
  "incumbent_mean": 104.413753,
  "mean_diff": -0.099236,
  "se": 0.105246,
  "sum_d": -49.61814,
  "sum_d2": 2768.567977,
  "retail_diff": 0.056202,
  "arb_diff": -0.155439,
  "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": 0.0,
   "se": 0.0,
   "sum_d": 0.0,
   "sum_d2": 0.0,
   "retail_diff": 0.0,
   "arb_diff": 0.0
  },
  "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.378764,
   "se": 0.401829,
   "sum_d": -49.61814,
   "sum_d2": 2768.567977,
   "retail_diff": 0.214512,
   "arb_diff": -0.593277
  }
 },
 "seconds_per_simulation": 0.88
}