{
  "np2_check": {
    "seed_start": 0,
    "n": 1000,
    "mean_edge": 526.1155722459862,
    "expected_rounded": 526.1156,
    "matches_to_4_decimals": true
  },
  "seconds_per_sim": 0.07539808825825457,
  "sims_per_hour": 47746.56868844247,
  "paired": {
    "n": 4000,
    "mean_diff": -2.6796420889679617,
    "se": 0.022914962820787588,
    "worst": -13.307315164971442,
    "mean_a": 520.9354291994272,
    "mean_b": 523.6150712883951
  },
  "measurements": [
    {
      "start": 2250000,
      "n": 2000,
      "mean_diff": -2.7065068369135625,
      "se": 0.03239948020313795,
      "worst": -11.712710901097523,
      "mean_a": 520.649437103215,
      "mean_b": 523.3559439401284,
      "seconds_per_sim": 0.07584100466099335,
      "sims_per_hour": 47467.725620090016,
      "elapsed_seconds": 303.3640186439734,
      "simulations_run": 4000
    },
    {
      "start": 2252000,
      "n": 2000,
      "mean_diff": -2.652777341022361,
      "se": 0.03241078600341126,
      "worst": -13.307315164971442,
      "mean_a": 521.2214212956394,
      "mean_b": 523.8741986366617,
      "seconds_per_sim": 0.0749551718555158,
      "sims_per_hour": 48028.707170992675,
      "elapsed_seconds": 299.8206874220632,
      "simulations_run": 4000
    }
  ],
  "agreement": {
    "absolute_mean_gap": 0.05372949589120157,
    "combined_se": 0.045827779422446896,
    "gap_in_se": 1.1724219800378182,
    "within_2_se": true
  },
  "np2_sha256": "51f1c1a9d4999ee460eb8bbc66880da8074b173399a4f5d71f96671482e18ac0",
  "plus5_sha256": "69568ae4e203bbf12cfcdea7f61d6a02560f835252f536f32833110b45fea833",
  "code_sha256": "4d78017f83800260fbee9cddc97de1b17e86b68ba734dd7704f91ee9a0b425c0",
  "code": "\"\"\"Pair two AMM strategies against the same vanilla opponent and seed draws.\n\nRun from the pinned amm-challenge checkout after `. scratch/env.sh`:\n    python /path/to/pair.py a.sol b.sol START COUNT\n\"\"\"\n\nimport argparse\nimport json\nimport math\nimport sys\nimport time\nfrom pathlib import Path\n\nimport amm_sim_rs\nimport numpy as np\n\nfrom amm_competition.competition.config import (\n    BASELINE_SETTINGS,\n    BASELINE_VARIANCE,\n)\nfrom amm_competition.evm.baseline import get_vanilla_bytecode_and_abi\nfrom amm_competition.evm.compiler import SolidityCompiler\n\n\ndef configs(start, count):\n    \"\"\"Match MatchRunner._build_configs, replacing its index i with seed s.\"\"\"\n    v = BASELINE_VARIANCE\n    b = BASELINE_SETTINGS\n    result = []\n    for s in range(start, start + count):\n        rng = np.random.default_rng(s)\n        size = rng.uniform(v.retail_mean_size_min, v.retail_mean_size_max)\n        rate = rng.uniform(v.retail_arrival_rate_min, v.retail_arrival_rate_max)\n        sigma = rng.uniform(v.gbm_sigma_min, v.gbm_sigma_max)\n        result.append(amm_sim_rs.SimulationConfig(\n            n_steps=b.n_steps, initial_price=b.initial_price,\n            initial_x=b.initial_x, initial_y=b.initial_y, gbm_mu=b.gbm_mu,\n            gbm_sigma=sigma, gbm_dt=b.gbm_dt, retail_arrival_rate=rate,\n            retail_mean_size=size, retail_size_sigma=b.retail_size_sigma,\n            retail_buy_prob=b.retail_buy_prob, seed=s,\n        ))\n    return result\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=__doc__)\n    parser.add_argument(\"a\", type=Path, help=\"first strategy Solidity source\")\n    parser.add_argument(\"b\", type=Path, help=\"second strategy Solidity source\")\n    parser.add_argument(\"start\", type=int)\n    parser.add_argument(\"count\", type=int)\n    parser.add_argument(\"--batch-size\", type=int, default=16)\n    parser.add_argument(\"--workers\", type=int, default=8)\n    args = parser.parse_args()\n    if args.start < 0 or args.count <= 0 or args.batch_size <= 0 or args.workers <= 0:\n        parser.error(\"start must be nonnegative; count, batch-size and workers positive\")\n\n    compiler = SolidityCompiler()\n    byte_a, _ = compiler.compile_and_get_bytecode(args.a.read_text())\n    byte_b, _ = compiler.compile_and_get_bytecode(args.b.read_text())\n    vanilla, _ = get_vanilla_bytecode_and_abi()\n\n    edges_a = []\n    edges_b = []\n    t0 = time.perf_counter()\n    n_runs = 0\n    for offset in range(0, args.count, args.batch_size):\n        n = min(args.batch_size, args.count - offset)\n        batch = configs(args.start + offset, n)\n        a_result = amm_sim_rs.run_batch(list(byte_a), list(vanilla), batch, args.workers)\n        a_edges = [r.edges[\"submission\"] for r in a_result.results]\n        edges_a.extend(a_edges)\n        n_runs += n\n        del a_result\n        if byte_a == byte_b:\n            edges_b.extend(a_edges)\n        else:\n            b_result = amm_sim_rs.run_batch(list(byte_b), list(vanilla), batch, args.workers)\n            edges_b.extend(r.edges[\"submission\"] for r in b_result.results)\n            n_runs += n\n            del b_result\n        if offset == 0 or (offset + n) % 256 == 0 or offset + n == args.count:\n            print(f\"completed {offset + n}/{args.count} seeds\", file=sys.stderr, flush=True)\n\n    elapsed = time.perf_counter() - t0\n    diff = np.asarray(edges_a) - np.asarray(edges_b)\n    per_sim = elapsed / n_runs\n    print(json.dumps({\n        \"start\": args.start, \"n\": args.count,\n        \"mean_diff\": float(np.mean(diff)),\n        \"se\": float(np.std(diff, ddof=1) / math.sqrt(args.count)) if args.count > 1 else None,\n        \"worst\": float(np.min(diff)),\n        \"mean_a\": float(np.mean(edges_a)), \"mean_b\": float(np.mean(edges_b)),\n        \"seconds_per_sim\": per_sim, \"sims_per_hour\": 3600 / per_sim,\n        \"elapsed_seconds\": elapsed, \"simulations_run\": n_runs,\n    }))\n\n\nif __name__ == \"__main__\":\n    main()\n",
  "blocked": null,
  "notes": "mean_diff is first strategy minus second: NP2 with BASE_FEE changed from 4 to 9 BPS versus NP2, each against compiled VanillaStrategy. The pairing script follows the pinned MatchRunner draw order for each absolute seed; the simulator uses seed s for price and s+1 for retail. Simulation timing excludes Solidity compilation and includes both strategy batches. Runs use 16-seed batches and 8 workers. NP2 was reconstructed by inserting the specified NP interior-retail block before TWO's tradeRatio declaration; its SHA-256 matches 51f1c1a9. The two block means agree within 2 combined SE; the fee increase is a calibration control, not an improvement candidate."
}
