{
  "np2_check": {
    "n": 1000,
    "seed_start": 0,
    "seed_end_inclusive": 999,
    "mean_edge": 526.1155722459862,
    "required_rounded": 526.1156,
    "source_sha256": "51f1c1a9d4999ee460eb8bbc66880da8074b173399a4f5d71f96671482e18ac0",
    "passes": true
  },
  "ch_check": {
    "n": 1000,
    "mean_edge": 525.5258821780834,
    "required": 525.525882178,
    "passes": true
  },
  "design": "Information oracle: current true fair price before arbitrage each step and after every fill; true gbm_sigma is available in engine config but unused by this fixed-g family. With spot=y/x, bid_fee=clamp(max(0,1-fair/spot)+g_bid,0,0.1); ask_fee=clamp(max(0,1-spot/fair)+g_ask,0,0.1). Selected g_bid=25 bps, g_ask=25 bps. Recompute from new reserves after every fill, including multiple retail fills within a step. Keep normalizer unmodified at 30 bps. Separate oracle-vs-normalizer and NP2-vs-normalizer runs; pair submission edge by seed.",
  "paired": {
    "n": 10000,
    "mean_diff": -33.47865590825804,
    "se": 0.0709126707032814,
    "worst": [
      {
        "seed": 2284120,
        "diff": -59.86793160088007
      },
      {
        "seed": 2281703,
        "diff": -57.361899081007834
      },
      {
        "seed": 2281819,
        "diff": -56.21237109088088
      },
      {
        "seed": 2287280,
        "diff": -56.12212895616142
      },
      {
        "seed": 2280000,
        "diff": -56.11207463773917
      }
    ],
    "oracle_mean": 489.65539284151345,
    "np2_mean": 523.1340487497716
  },
  "arb_edge": {
    "oracle": -3.189260081693879,
    "np2": -31.19412383623352,
    "mean_diff": 28.004863754539645,
    "se_diff": 0.03806897603057371
  },
  "retail_edge": {
    "oracle": 492.84465292320743,
    "np2": 554.3281725860049,
    "mean_diff": -61.48351966279766,
    "se_diff": 0.08417038759565912
  },
  "arb_volume": {
    "oracle": 71.33839119264168,
    "np2": 24088.412772373904,
    "mean_diff": -24017.074381181257,
    "se_diff": 15.696697173226571
  },
  "retail_volume": {
    "oracle": 51973.829343962294,
    "np2": 77163.25984993276,
    "mean_diff": -25189.430505970467,
    "se_diff": 23.67096351025768
  },
  "blocked": null,
  "next": "No net gain demonstrated. Inference: use better fair-price estimates to reduce NP2's remaining arbitrage loss while preserving favorable-side retail quotes and inventory recentering. Target the oracle's arbitrage savings without copying its larger retail-edge cost.",
  "tuning": {
    "seeds": "10000..19999 inclusive, all 10000 for every trial",
    "grid_bps": [
      5,
      10,
      15,
      20,
      25,
      30,
      35,
      40,
      45,
      50,
      55,
      60
    ],
    "method": "Symmetric grid first; coordinate sweep bid with ask fixed at symmetric winner; coordinate sweep ask with bid fixed at preceding winner. Duplicate pairs reused only on training seeds. Maximize mean submission edge. No holdout selection.",
    "selected": {
      "bid_g_bps": 25,
      "ask_g_bps": 25,
      "symmetric_g_bps": 25
    },
    "trials": [
      {
        "bid_g_bps": 5,
        "ask_g_bps": 5,
        "n": 10000,
        "mean_edge": 477.5177688982455
      },
      {
        "bid_g_bps": 10,
        "ask_g_bps": 10,
        "n": 10000,
        "mean_edge": 483.69862538778244
      },
      {
        "bid_g_bps": 15,
        "ask_g_bps": 15,
        "n": 10000,
        "mean_edge": 487.8331949549321
      },
      {
        "bid_g_bps": 20,
        "ask_g_bps": 20,
        "n": 10000,
        "mean_edge": 490.2359420814864
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 491.1764047442567
      },
      {
        "bid_g_bps": 30,
        "ask_g_bps": 30,
        "n": 10000,
        "mean_edge": 490.83059536277466
      },
      {
        "bid_g_bps": 35,
        "ask_g_bps": 35,
        "n": 10000,
        "mean_edge": 489.22622698419445
      },
      {
        "bid_g_bps": 40,
        "ask_g_bps": 40,
        "n": 10000,
        "mean_edge": 486.2563125371844
      },
      {
        "bid_g_bps": 45,
        "ask_g_bps": 45,
        "n": 10000,
        "mean_edge": 481.77448995810283
      },
      {
        "bid_g_bps": 50,
        "ask_g_bps": 50,
        "n": 10000,
        "mean_edge": 475.64883105421075
      },
      {
        "bid_g_bps": 55,
        "ask_g_bps": 55,
        "n": 10000,
        "mean_edge": 467.7836661497907
      },
      {
        "bid_g_bps": 60,
        "ask_g_bps": 60,
        "n": 10000,
        "mean_edge": 458.2665809638541
      },
      {
        "bid_g_bps": 5,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 487.379978361642
      },
      {
        "bid_g_bps": 10,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 488.9074797709029
      },
      {
        "bid_g_bps": 15,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 490.05386920359956
      },
      {
        "bid_g_bps": 20,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 490.8190446563
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 491.1764047442567
      },
      {
        "bid_g_bps": 30,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 491.06694227888676
      },
      {
        "bid_g_bps": 35,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 490.3919236159371
      },
      {
        "bid_g_bps": 40,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 489.01392767707205
      },
      {
        "bid_g_bps": 45,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 486.76201021550054
      },
      {
        "bid_g_bps": 50,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 483.4158553586319
      },
      {
        "bid_g_bps": 55,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 478.73475076939724
      },
      {
        "bid_g_bps": 60,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 472.5901436997101
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 5,
        "n": 10000,
        "mean_edge": 487.3631698713001
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 10,
        "n": 10000,
        "mean_edge": 488.88945835378337
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 15,
        "n": 10000,
        "mean_edge": 490.03764706054295
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 20,
        "n": 10000,
        "mean_edge": 490.8115843222033
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 25,
        "n": 10000,
        "mean_edge": 491.1764047442567
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 30,
        "n": 10000,
        "mean_edge": 491.07309456030254
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 35,
        "n": 10000,
        "mean_edge": 490.40701014865095
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 40,
        "n": 10000,
        "mean_edge": 489.04566758074236
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 45,
        "n": 10000,
        "mean_edge": 486.80777052047
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 50,
        "n": 10000,
        "mean_edge": 483.46694191334956
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 55,
        "n": 10000,
        "mean_edge": 478.78803249034416
      },
      {
        "bid_g_bps": 25,
        "ask_g_bps": 60,
        "n": 10000,
        "mean_edge": 472.6346688271907
      }
    ]
  },
  "confirmation": {
    "seeds": "2280000..2289999 inclusive",
    "candidate_evaluations": 1,
    "same_draws": "numpy.default_rng(seed): U(19,21) size, U(0.6,1.0) rate, U(0.000882,0.001008) sigma; cfg.seed=seed; Rust price seed=seed, retail seed=seed+1.",
    "fixed_config": {
      "n_steps": 10000,
      "initial_price": 100,
      "initial_x": 100,
      "initial_y": 10000,
      "gbm_mu": 0,
      "gbm_dt": 1,
      "retail_size_sigma": 1.2,
      "retail_buy_prob": 0.5
    },
    "se": "Sample std of paired differences (ddof=1)/sqrt(n).",
    "units": "All edge and volume fields are per-simulation arithmetic means in Y; volumes use simulator amount_y. worst lists the five lowest total-edge differences."
  },
  "checks": {
    "setup_exit_code": 0,
    "simulator_commit": "6b6921f8d463314aa7c96234e1be25a25245f704",
    "np2_patch_parity": "First 100 seeds: bit-exact edge, arb volume and retail volume versus unpatched simulator, with oracle disabled.",
    "attribution": "Every confirmation result passes abs(total_edge-arb_edge-retail_edge)<1e-7.",
    "data_sha256": {
      "np2-check.json": "d791c0f5ae539d3a615f3fa603c88f10f87dc9b63a88236faebddb8e000a3041",
      "ch-check.json": "8951dcac2ffd464777c3a7fa3e5e249e15bf83e43ef08ced897aec2907b5c402",
      "np2-holdout.json": "78d27dd631440724924668dadd06c58c7068264fd8bc0703a8c0051531be8353",
      "oracle-holdout.json": "cf2853081241fe7e2db3462fec802861f9b2f1c8e5f9d47fc8179705e8150d90"
    }
  },
  "limitations": "This is an empirical information-oracle benchmark for the specified capped fee family, not a proof of a global upper bound. Per-side search is coordinate-wise rather than exhaustive 2D. Sigma is known but this requested fixed-g rule does not condition on it. It is not a legal deployable Solidity strategy.",
  "code": "diff --git a/amm_sim_rs/src/amm/cfmm.rs b/amm_sim_rs/src/amm/cfmm.rs\nindex 02e6645..4cf967b 100644\n--- a/amm_sim_rs/src/amm/cfmm.rs\n+++ b/amm_sim_rs/src/amm/cfmm.rs\n@@ -48,6 +48,7 @@ pub struct CFMM {\n     reserve_y: f64,\n     /// Current fee quote\n     current_fees: FeeQuote,\n+    oracle: Option<(f64, f64, f64)>,\n     /// Whether initialized\n     initialized: bool,\n     /// Accumulated fees in X (collected separately, not in reserves)\n@@ -67,6 +68,7 @@ impl CFMM {\n             reserve_y,\n             current_fees: FeeQuote::symmetric(Wad::from_bps(30)),\n             initialized: false,\n+            oracle: None,\n             accumulated_fees_x: 0.0,\n             accumulated_fees_y: 0.0,\n         }\n@@ -84,6 +86,23 @@ impl CFMM {\n         Ok(())\n     }\n \n+    /// Oracle-only experiment: recompute after every price move and fill.\n+    pub fn set_oracle(&mut self, fair: f64, bid_g: f64, ask_g: f64) {\n+        self.oracle = Some((fair, bid_g, ask_g));\n+        self.refresh_oracle();\n+    }\n+\n+    fn refresh_oracle(&mut self) {\n+        if let Some((fair, bid_g, ask_g)) = self.oracle {\n+            let spot = self.spot_price();\n+            let bid = (1.0 - fair / spot).max(0.0) + bid_g;\n+            let ask = (1.0 - spot / fair).max(0.0) + ask_g;\n+            self.current_fees = FeeQuote::new(\n+                Wad::from_f64(bid).clamp_fee(),\n+                Wad::from_f64(ask).clamp_fee());\n+        }\n+    }\n+\n     /// Get current reserves.\n     pub fn reserves(&self) -> (f64, f64) {\n         (self.reserve_x, self.reserve_y)\n@@ -291,6 +310,7 @@ impl CFMM {\n \n     /// Update fees from strategy after a trade.\n     fn update_fees(&mut self, trade_info: &TradeInfo) {\n+        if self.oracle.is_some() { self.refresh_oracle(); return; }\n         if let Ok((bid_fee, ask_fee)) = self.strategy.after_swap(trade_info) {\n             self.current_fees = FeeQuote::new(bid_fee.clamp_fee(), ask_fee.clamp_fee());\n         }\ndiff --git a/amm_sim_rs/src/simulation/engine.rs b/amm_sim_rs/src/simulation/engine.rs\nindex 6834c43..583ec38 100644\n--- a/amm_sim_rs/src/simulation/engine.rs\n+++ b/amm_sim_rs/src/simulation/engine.rs\n@@ -113,6 +113,13 @@ impl SimulationEngine {\n         edges.insert(submission_name.clone(), 0.0);\n         edges.insert(baseline_name.clone(), 0.0);\n \n+        let mut arb_edges = edges.clone();\n+        let mut retail_edges = edges.clone();\n+        let oracle_g = std::env::var(\"ORACLE_BID_G\").ok().map(|x| {\n+            (x.parse::<f64>().unwrap() / 10000.0,\n+             std::env::var(\"ORACLE_ASK_G\").unwrap().parse::<f64>().unwrap() / 10000.0)\n+        });\n+        // Sigma is available via self.config.gbm_sigma; fixed-g rule does not use it.\n         // Run simulation steps\n         let mut steps = Vec::with_capacity(self.config.n_steps as usize);\n \n@@ -137,10 +144,14 @@ impl SimulationEngine {\n             // 1. Generate new fair price\n             let fair_price = price_process.step();\n \n+            if let Some((bid, ask)) = oracle_g {\n+                amms[0].set_oracle(fair_price, bid, ask);\n+            }\n             // 2. Arbitrageur extracts profit from each AMM\n             for amm in amms.iter_mut() {\n                 if let Some(arb_result) = arbitrageur.execute_arb(amm, fair_price, t as u64) {\n                     *arb_volume_y.get_mut(&arb_result.amm_name).unwrap() += arb_result.amount_y;\n+                    *arb_edges.get_mut(&arb_result.amm_name).unwrap() -= arb_result.profit;\n                     let entry = edges.entry(arb_result.amm_name).or_insert(0.0);\n                     // AMM edge is the negative of arbitrageur profit at true price\n                     *entry += -arb_result.profit;\n@@ -157,27 +168,20 @@ impl SimulationEngine {\n                 } else {\n                     trade.amount_y - trade.amount_x * fair_price\n                 };\n+                *retail_edges.get_mut(&trade.amm_name).unwrap() += trade_edge;\n                 let entry = edges.entry(trade.amm_name).or_insert(0.0);\n                 *entry += trade_edge;\n             }\n \n-            // 4. Capture step result and accumulate fees\n-            let step = capture_step(\n-                t,\n-                fair_price,\n-                &amms,\n-                &names,\n-                &initial_reserves,\n-                initial_fair_price,\n-            );\n-            // Accumulate fees for averaging\n-            for name in &names {\n-                if let Some((bid_fee, ask_fee)) = step.fees.get(name) {\n-                    *cumulative_bid_fees.get_mut(name).unwrap() += bid_fee;\n-                    *cumulative_ask_fees.get_mut(name).unwrap() += ask_fee;\n-                }\n+            for (amm, name) in amms.iter().zip(names.iter()) {\n+                let fees = amm.fees();\n+                *cumulative_bid_fees.get_mut(name).unwrap() += fees.bid_fee.to_f64();\n+                *cumulative_ask_fees.get_mut(name).unwrap() += fees.ask_fee.to_f64();\n+            }\n+            if std::env::var_os(\"KEEP_STEPS\").is_some() {\n+                steps.push(capture_step(t, fair_price, &amms, &names,\n+                    &initial_reserves, initial_fair_price));\n             }\n-            steps.push(step);\n         }\n \n         // Calculate final PnL (reserves + accumulated fees)\n@@ -209,6 +213,8 @@ impl SimulationEngine {\n             strategies: vec![submission_name, baseline_name],\n             pnl,\n             edges,\n+            arb_edges,\n+            retail_edges,\n             initial_fair_price,\n             initial_reserves,\n             steps,\ndiff --git a/amm_sim_rs/src/types/result.rs b/amm_sim_rs/src/types/result.rs\nindex 0257335..3c0be1a 100644\n--- a/amm_sim_rs/src/types/result.rs\n+++ b/amm_sim_rs/src/types/result.rs\n@@ -57,6 +57,10 @@ pub struct LightweightSimResult {\n     /// Edge by strategy name (sum over trades)\n     #[pyo3(get)]\n     pub edges: HashMap<String, f64>,\n+    #[pyo3(get)]\n+    pub arb_edges: HashMap<String, f64>,\n+    #[pyo3(get)]\n+    pub retail_edges: HashMap<String, f64>,\n \n     /// Initial fair price\n     #[pyo3(get)]\n",
  "sha256": "80004a599289dfa8d43d6340b218631b93db6a8c8587f739d72f5681a0d5cdf1",
  "reproduction": {
    "code_format": "git diff against pinned simulator; apply as a patch. ORACLE_BID_G and ORACLE_ASK_G are bps, only submission uses them. With both absent NP2 is unmodified. KEEP_STEPS enables step capture; disabling capture affects output storage only.",
    "measurement_python": "import numpy as np, json, pathlib, time, sys\nimport amm_sim_rs\nfrom amm_competition.evm.compiler import SolidityCompiler\nfrom amm_competition.competition.config import build_config\nP=pathlib.Path('scratch')\ndef compile_file(f):\n    out=P/(pathlib.Path(f).stem+'.bin')\n    if not out.exists():\n        r=SolidityCompiler().compile(pathlib.Path(f).read_text(), 'VanillaStrategy' if 'VanillaStrategy' in f else 'Strategy');assert r.success,r.errors\n        out.write_bytes(r.bytecode)\n    return out.read_bytes()\ndef configs(start,n):\n    out=[]\n    for s in range(start,start+n):\n        r=np.random.default_rng(s); size=r.uniform(19,21);rate=r.uniform(.6,1);sig=r.uniform(.000882,.001008)\n        out.append(build_config(seed=s,gbm_sigma=sig,retail_arrival_rate=rate,retail_mean_size=size))\n    return out\ndef measure(code,start,n,out):\n    normal=compile_file('contracts/src/VanillaStrategy.sol')\n    rows=[];t=time.time()\n    for i in range(start,start+n,20):\n        rr=amm_sim_rs.run_batch(code,normal,configs(i,min(20,start+n-i)),8).results\n        for r in rr:\n            row={'seed':r.seed,'edge':r.edges['submission'],'arb_volume':r.arb_volume_y['submission'],'retail_volume':r.retail_volume_y['submission']}\n            if hasattr(r,'arb_edges'):\n                row['arb_edge']=r.arb_edges['submission'];row['retail_edge']=r.retail_edges['submission']\n            rows.append(row)\n        if len(rows)%100==0:print(out,len(rows),round(time.time()-t,2),flush=True)\n    (P/out).write_text(json.dumps(rows))\n    print('MEAN',np.mean([r['edge'] for r in rows]),flush=True)\nif __name__=='__main__':measure(compile_file('scratch/NP2.sol'),0,1000,'np2-check.json')\n",
    "tuning_python": "from measure import *\nimport os\nout=P/'grid.json'\ntrials=[]\ndef trial(bid,ask):\n    key=f'g{bid}-{ask}.json'\n    os.environ['ORACLE_BID_G']=str(bid);os.environ['ORACLE_ASK_G']=str(ask)\n    if not (P/key).exists():measure(compile_file('contracts/src/StarterStrategy.sol'),10000,10000,key)\n    rows=json.loads((P/key).read_text())\n    d={'bid_g_bps':bid,'ask_g_bps':ask,'n':len(rows),'mean_edge':float(np.mean([r['edge'] for r in rows]))}\n    trials.append(d);out.write_text(json.dumps(trials));print('TRIAL',d,flush=True)\n    return d['mean_edge']\ngrid=list(range(5,61,5))\nscores=[trial(g,g) for g in grid];best=grid[int(np.argmax(scores))]\n# coordinate search using the same fixed training block only\nscores=[trial(g,best) for g in grid];b=grid[int(np.argmax(scores))]\nscores=[trial(b,g) for g in grid];a=grid[int(np.argmax(scores))]\n(P/'selected.json').write_text(json.dumps({'bid_g_bps':b,'ask_g_bps':a,'symmetric_g_bps':best}))\nprint('SELECTED',b,a,flush=True)\n",
    "np2_reconstruction": "Read code from the two artifact URLs in sources.json. Extract NP's substring from // Interior retail routing to the following uint256 tradeRatio = (exclusive). Insert immediately before TWO's uint256 tradeRatio =, preserving whitespace. SHA-256 must equal np2_check.source_sha256.",
    "holdout_command": "With scratch/env.sh sourced, call measure(compile_file('scratch/NP2.sol'),2280000,10000,'np2-holdout.json') without oracle environment variables. In a separate process set ORACLE_BID_G=25 ORACLE_ASK_G=25 and call measure(compile_file('contracts/src/StarterStrategy.sol'),2280000,10000,'oracle-holdout.json')."
  },
  "conclusion": "The selected requested oracle family underperforms NP2; it is not an upper bound on legal strategies.",
  "interpretation": {
    "arbitrage_savings": 28.004863754539645,
    "retail_edge_change": -61.48351966279766,
    "net_change": -33.47865590825804,
    "residual_arbitrage": "Oracle arbitrage loss is nonzero. The 10% fee cap can prevent covering the full spot-to-fair gap, so current true-price knowledge does not guarantee zero arbitrage loss under this capped rule.",
    "implication": "Information access alone does not make a restricted fee family an upper bound. The measured rule reduces arbitrage loss but sacrifices more retail edge."
  }
}
