## delta Insert immediately before the final `slots[0] = bidFee` assignment. All NP2 constants and transient skew terms remain unchanged. ```diff + // Remove the persistent bid-over-ask mean, preserving the midpoint. + uint256 meanLeanHalf = 20000000000000; + if (meanLeanHalf > bidFee) meanLeanHalf = bidFee; + if (meanLeanHalf > MAX_FEE - askFee) meanLeanHalf = MAX_FEE - askFee; + bidFee -= meanLeanHalf; + askFee += meanLeanHalf; + ``` ## code ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {AMMStrategyBase} from "./AMMStrategyBase.sol"; import {TradeInfo} from "./IAMMStrategy.sol"; contract Strategy is AMMStrategyBase { uint256 constant ELAPSED_CAP = 8; uint256 constant SIGNAL_THRESHOLD = WAD / 650; uint256 constant DIR_DECAY = 750000000000000000; uint256 constant ACT_DECAY = 530000000000000000; uint256 constant SIZE_DECAY = 530000000000000000; uint256 constant TOX_DECAY = 910000000000000000; uint256 constant SIGMA_DECAY = 690000000000000000; uint256 constant LAMBDA_DECAY = 990000000000000000; uint256 constant SIZE_BLEND_DECAY = 828000000000000000; uint256 constant TOX_BLEND_DECAY = 51000000000000000; uint256 constant ACT_BLEND_DECAY = 985000000000000000; uint256 constant PHAT_ALPHA = 270000000000000000; uint256 constant PHAT_ALPHA_RETAIL = 60000000000000000; uint256 constant PHAT_ALPHA_SAME = 120000000000000000; uint256 constant REVERT_U = 0; uint256 constant DIR_IMPACT_MULT = 4; uint256 constant GATE_SIGMA_MULT = 7200000000000000000; uint256 constant MIN_GATE = 30000000000000000; uint256 constant TRADE_TOX_BOOST = 3500 * BPS; uint256 constant STALE_ATTRACT_FRAC = 1050000000000000000; uint256 constant RET_CAP = WAD / 35; uint256 constant TOX_CAP = WAD / 5; uint256 constant TRADE_RATIO_CAP = WAD / 5; uint256 constant LAMBDA_CAP = 1820000000000000000; uint256 constant STEP_COUNT_CAP = 64; uint256 constant BASE_FEE = 4 * BPS; uint256 constant SIGMA_COEF = 200000000000000000; uint256 constant LAMBDA_COEF = 12 * BPS; uint256 constant FLOW_SIZE_COEF = 4842 * BPS; uint256 constant TOX_COEF = 300 * BPS; uint256 constant TOX_QUAD_COEF = 15000 * BPS; uint256 constant ACT_COEF = 91843 * BPS; uint256 constant DIR_COEF = 20 * BPS; uint256 constant DIR_TOX_COEF = 100 * BPS; uint256 constant STALE_DIR_COEF = 6850 * BPS; uint256 constant SIGMA_TOX_COEF = 500 * BPS; uint256 constant TAIL_KNEE = 600 * BPS; uint256 constant TAIL_SLOPE_PROTECT = 930000000000000000; uint256 constant TAIL_SLOPE_ATTRACT = 850000000000000000; function afterInitialize(uint256 initialX, uint256 initialY) external override returns (uint256, uint256) { slots[0] = BASE_FEE; slots[1] = BASE_FEE; slots[2] = 0; slots[3] = WAD; slots[4] = 0; slots[5] = initialX > 0 ? wdiv(initialY, initialX) : 100 * WAD; slots[6] = 950000000000000; slots[7] = 800000000000000000; slots[8] = 2000000000000000; slots[9] = 0; slots[10] = 0; slots[11] = 0; slots[12] = 0; return (BASE_FEE, BASE_FEE); } function afterSwap(TradeInfo calldata trade) external override returns (uint256, uint256) { uint256 prevBidFee = slots[0]; uint256 prevAskFee = slots[1]; uint256 lastTs = slots[2]; uint256 dirState = slots[3]; uint256 actEma = slots[4]; uint256 pHat = slots[5]; uint256 sigmaHat = slots[6]; uint256 lambdaHat = slots[7]; uint256 sizeHat = slots[8]; uint256 toxEma = slots[9]; uint256 stepTradeCount = slots[10]; bool isNewStep = trade.timestamp > lastTs; if (isNewStep) { uint256 elapsedRaw = trade.timestamp - lastTs; uint256 elapsed = elapsedRaw > ELAPSED_CAP ? ELAPSED_CAP : elapsedRaw; dirState = _decayCentered(dirState, DIR_DECAY, elapsed); actEma = wmul(actEma, _powWad(ACT_DECAY, elapsed)); sizeHat = wmul(sizeHat, _powWad(SIZE_DECAY, elapsed)); toxEma = wmul(toxEma, _powWad(TOX_DECAY, elapsed)); if (stepTradeCount > 0 && elapsedRaw > 0) { uint256 lambdaInst = (stepTradeCount * WAD) / elapsedRaw; if (lambdaInst > LAMBDA_CAP) lambdaInst = LAMBDA_CAP; lambdaHat = wmul(lambdaHat, LAMBDA_DECAY) + wmul(lambdaInst, WAD - LAMBDA_DECAY); } stepTradeCount = 0; } bool firstInStep = stepTradeCount == 0; uint256 spot = trade.reserveX > 0 ? wdiv(trade.reserveY, trade.reserveX) : pHat; if (pHat == 0) pHat = spot; uint256 feeUsed = trade.isBuy ? prevBidFee : prevAskFee; uint256 gamma = feeUsed < WAD ? WAD - feeUsed : 0; uint256 pImplied; if (gamma == 0) { pImplied = spot; } else { pImplied = trade.isBuy ? wmul(spot, gamma) : wdiv(spot, gamma); } { uint256 ret = pHat > 0 ? wdiv(absDiff(pImplied, pHat), pHat) : 0; uint256 alpha = firstInStep ? PHAT_ALPHA : PHAT_ALPHA_RETAIL; uint256 prePhat = pHat; bool secondPrint = !firstInStep && stepTradeCount == 1; bool sameDir = false; bool oppDir = false; if (secondPrint) { bool up2 = pImplied >= pHat; uint256 d1 = slots[12]; if (d1 != 0) { sameDir = (d1 == 1) == up2; oppDir = !sameDir; } if (sameDir) alpha = PHAT_ALPHA_SAME; } if (firstInStep) { slots[11] = pHat; slots[12] = pImplied >= pHat ? 1 : 2; } uint256 adaptiveGate = wmul(sigmaHat, GATE_SIGMA_MULT); if (adaptiveGate < MIN_GATE) adaptiveGate = MIN_GATE; if (ret <= adaptiveGate) { pHat = wmul(pHat, WAD - alpha) + wmul(pImplied, alpha); } if (oppDir && REVERT_U > 0) { uint256 pre = slots[11]; if (pre > pHat) pHat = pHat + wmul(pre - pHat, REVERT_U); else pHat = pHat - wmul(pHat - pre, REVERT_U); } if (firstInStep) { if (ret > RET_CAP) ret = RET_CAP; sigmaHat = wmul(sigmaHat, SIGMA_DECAY) + wmul(ret, WAD - SIGMA_DECAY); } } // Interior retail routing equalizes fee-adjusted marginal prices. // Subsequent trades in a step cannot be the initial arbitrage trade. if (!firstInStep && feeUsed > 30 * BPS && gamma > 0) { uint256 normalSpot = trade.isBuy ? wdiv(wmul(spot, gamma), WAD - 30 * BPS) : wdiv(wmul(spot, WAD - 30 * BPS), gamma); pHat = wmul(pHat, 90 * WAD / 100) + wmul(normalSpot, 10 * WAD / 100); } uint256 tradeRatio = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; if (tradeRatio > TRADE_RATIO_CAP) tradeRatio = TRADE_RATIO_CAP; if (tradeRatio > SIGNAL_THRESHOLD) { uint256 push = tradeRatio * DIR_IMPACT_MULT; if (push > WAD / 4) push = WAD / 4; if (trade.isBuy) { dirState = dirState + push; if (dirState > 2 * WAD) dirState = 2 * WAD; } else { dirState = dirState > push ? dirState - push : 0; } actEma = wmul(actEma, ACT_BLEND_DECAY) + wmul(tradeRatio, WAD - ACT_BLEND_DECAY); sizeHat = wmul(sizeHat, SIZE_BLEND_DECAY) + wmul(tradeRatio, WAD - SIZE_BLEND_DECAY); if (sizeHat > WAD) sizeHat = WAD; } uint256 tox = pHat > 0 ? wdiv(absDiff(spot, pHat), pHat) : 0; if (tox > TOX_CAP) tox = TOX_CAP; toxEma = wmul(toxEma, TOX_BLEND_DECAY) + wmul(tox, WAD - TOX_BLEND_DECAY); uint256 toxSignal = toxEma; stepTradeCount = stepTradeCount + 1; if (stepTradeCount > STEP_COUNT_CAP) stepTradeCount = STEP_COUNT_CAP; uint256 flowSize = wmul(lambdaHat, sizeHat); uint256 fBase = BASE_FEE + wmul(SIGMA_COEF, sigmaHat) + wmul(LAMBDA_COEF, lambdaHat) + wmul(FLOW_SIZE_COEF, flowSize); uint256 fMid = fBase + wmul(TOX_COEF, toxSignal) + wmul(TOX_QUAD_COEF, wmul(toxSignal, toxSignal)) + wmul(ACT_COEF, actEma); fMid = fMid + wmul(SIGMA_TOX_COEF, wmul(sigmaHat, toxSignal)); uint256 dirDev; bool sellPressure; if (dirState >= WAD) { dirDev = dirState - WAD; sellPressure = true; } else { dirDev = WAD - dirState; sellPressure = false; } uint256 skew = wmul(DIR_COEF, dirDev) + wmul(DIR_TOX_COEF, wmul(dirDev, toxSignal)); uint256 bidFee; uint256 askFee; if (sellPressure) { bidFee = fMid + skew; askFee = fMid > skew ? fMid - skew : 0; } else { askFee = fMid + skew; bidFee = fMid > skew ? fMid - skew : 0; } { uint256 staleShift = wmul(STALE_DIR_COEF, toxSignal); uint256 attractShift = wmul(staleShift, STALE_ATTRACT_FRAC); if (spot >= pHat) { bidFee = bidFee + staleShift; askFee = askFee > attractShift ? askFee - attractShift : 0; } else { askFee = askFee + staleShift; bidFee = bidFee > attractShift ? bidFee - attractShift : 0; } } { bool tradeAligned = (trade.isBuy && spot >= pHat) || (!trade.isBuy && spot < pHat); if (tradeAligned) { uint256 tradeBoost = wmul(TRADE_TOX_BOOST, tradeRatio); if (trade.isBuy) { bidFee = bidFee + tradeBoost; } else { askFee = askFee + tradeBoost; } } } if (sellPressure) { bidFee = clampFee(_compressTailWithSlope(bidFee, TAIL_SLOPE_PROTECT)); askFee = clampFee(_compressTailWithSlope(askFee, TAIL_SLOPE_ATTRACT)); } else { askFee = clampFee(_compressTailWithSlope(askFee, TAIL_SLOPE_PROTECT)); bidFee = clampFee(_compressTailWithSlope(bidFee, TAIL_SLOPE_ATTRACT)); } // Remove the persistent bid-over-ask mean, preserving the midpoint. uint256 meanLeanHalf = 20000000000000; if (meanLeanHalf > bidFee) meanLeanHalf = bidFee; if (meanLeanHalf > MAX_FEE - askFee) meanLeanHalf = MAX_FEE - askFee; bidFee -= meanLeanHalf; askFee += meanLeanHalf; slots[0] = bidFee; slots[1] = askFee; slots[2] = trade.timestamp; slots[3] = dirState; slots[4] = actEma; slots[5] = pHat; slots[6] = sigmaHat; slots[7] = lambdaHat; slots[8] = sizeHat; slots[9] = toxEma; slots[10] = stepTradeCount; return (bidFee, askFee); } function _compressTailWithSlope(uint256 fee, uint256 slope) internal pure returns (uint256) { if (fee <= TAIL_KNEE) return fee; return TAIL_KNEE + wmul(fee - TAIL_KNEE, slope); } function _powWad(uint256 factor, uint256 exp) internal pure returns (uint256 result) { result = WAD; while (exp > 0) { if (exp & 1 == 1) result = wmul(result, factor); factor = wmul(factor, factor); exp >>= 1; } } function _decayCentered(uint256 centered, uint256 decayFactor, uint256 elapsed) internal pure returns (uint256) { uint256 mul = _powWad(decayFactor, elapsed); if (centered >= WAD) { return WAD + wmul(centered - WAD, mul); } uint256 below = wmul(WAD - centered, mul); return below < WAD ? WAD - below : 0; } function getName() external pure override returns (string memory) { return "no_cubic"; } } ``` Validation command: `. scratch/env.sh && amm-match validate scratch/fixed_0p20.sol` (exit 0). ```text Validating strategy... Compiling strategy... Strategy 'no_cubic' validated successfully! ``` ## sha256 `23fac70f933d810d3a9113e200547b146a2f2996b3e403eb9948ea1f366c6fee` Hash covers the UTF-8 Solidity code block, including its trailing newlines. NP2 reconstruction hash: `51f1c1a9d4999ee460eb8bbc66880da8074b173399a4f5d71f96671482e18ac0`. Reconstructed exactly from [TWO](https://api.imd.fun/artifacts/528a6da78583c6142644a552c86670f9d25c20e39e92f4de44deae05ddc8cecd) plus the specified [NP interior-routing block](https://api.imd.fun/artifacts/da313379a2e69e9286668865e06b18dfcae25732c82b934b0fd9c2ef0d8c2ff5). ## tuning Chosen: **fixed half-transfer 0.20 bps** (bid down, ask up, bounded by available fee room). The selection target was a near-zero mean bid-minus-ask in the standard vanilla-opponent market while retaining every NP2 transient term. This point had the smallest absolute standard-market gap among the tested corrections: -0.023130 bps, versus NP2 +0.988124 bps. Its common-vanilla paired training edge was -0.041287 (SE 0.006433); every tested EMA had both a larger residual gap and a larger loss. **Retain NP2 for leaderboard use: this is a validated mean-removal experiment, not a promotion recommendation.** All 39 original Strategy constants remain unchanged; no state is added. The transfer preserves the same hook’s raw quote midpoint exactly, including at fee bounds. Future state can change: feedback amplifies the direct 0.40 bps gap adjustment into roughly a 1 bps average correction. All tuning used seeds within 10000..99999. Diagnostic ablations used only 0..199; CH/NP2 calibration checks used 0..999. These were local measurements using the pinned simulator; no leaderboard submission was made. Calibration against [VanillaStrategy, fixed 30 bps](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/contracts/src/VanillaStrategy.sol): [CH](https://raw.githubusercontent.com/ChengHua926/amm-challenge/d34a5199c59a4c44d5336a25f1436b4ae1f21104/contracts/src/Strategy.sol) = **525.5258821780834** (required 525.525882178), NP2 = **526.1155722459862** (required rounded score 526.1156). Both checks passed. Config parity against `MatchRunner._build_configs` was exact for all fields on 200 seeds. Diagnostic ablations, n=200 each. “Vanilla paired” subtracts NP2’s submission edge on the same seed with the same vanilla opponent; “direct” is the requested candidate–NP2 shared-market edge difference. Fee gaps are mean step-end bid minus ask, in bps. | Ablation | Vanilla paired mean_diff | SE | Vanilla gap | Direct mean_diff | SE | Direct gap | |---|---:|---:|---:|---:|---:|---:| | NP2 control | 0 | 0 | 0.890445 | -0.000024 | 0.000106 | 0.563899 | | `skew = 0` | 0.061225 | 0.012403 | 0.902679 | -2.305771 | 0.103957 | 0.584601 | | `staleShift = 0` (hence attractShift=0) | -122.521314 | 1.648450 | -0.035281 | -92.444986 | 1.262198 | -0.016565 | | `tradeBoost = 0` | -0.809412 | 0.044062 | 0.762273 | 16.404532 | 0.320798 | 0.592481 | The stale-price adjustment is the dominant source in this diagnostic: removing it levels the mean but destroys much of the edge. These are interventions on the whole evolving strategy, not an additive decomposition. The submitted code retains the directional, stale-price and aligned-trade responses. Direct-match gains are not leaderboard deltas: the aligned-boost ablation even reverses sign between the two metrics. Grid tune1: seeds 10000..10499. Fixed values transfer that many bps **from bid to ask** after compression/clamping, bounded by available fee room. EMA decay is `1 - 1/H` per timestamp unit; it estimates the raw step-end gap, then transfers half the estimated gap, with symmetric bounds. EMA starts from zero gap and does not update on additional trades in the same timestamp. | Point | n | Direct mean_diff | Direct SE | Direct gap (bps) | Vanilla paired mean_diff | Vanilla SE | Vanilla gap (bps) | |---|---:|---:|---:|---:|---:|---:|---:| | Raw-gap EMA, H=1024 | 500 | 3.281556 | 0.115314 | 0.109486 | -0.735366 | 0.022215 | 0.157203 | | Raw-gap EMA, H=256 | 500 | 6.179369 | 0.191217 | 0.106746 | -2.152313 | 0.037454 | 0.148715 | | Raw-gap EMA, H=4096 | 500 | 1.547577 | 0.081612 | 0.169760 | -0.251979 | 0.014542 | 0.246558 | | Fixed half-transfer 0.10 bps | 500 | 0.208213 | 0.032608 | 0.235349 | -0.022383 | 0.004707 | 0.486764 | | Fixed half-transfer 0.15 bps | 500 | 0.260342 | 0.036842 | -0.022772 | -0.032097 | 0.005479 | 0.233169 | | Fixed half-transfer 0.20 bps **chosen** | 500 | 0.356468 | 0.038173 | -0.258163 | -0.041287 | 0.006433 | -0.023130 | | Fixed half-transfer 0.25 bps | 500 | 0.447029 | 0.039657 | -0.504733 | -0.050185 | 0.007153 | -0.273900 | | Fixed half-transfer 0.50 bps | 500 | 0.924982 | 0.055070 | -1.695138 | -0.109562 | 0.011293 | -1.510030 | | Fixed half-transfer 0.75 bps | 500 | 1.337182 | 0.067156 | -2.896581 | -0.171133 | 0.013323 | -2.734642 | | Fixed half-transfer 1.00 bps | 500 | 1.753317 | 0.079632 | -4.094933 | -0.245374 | 0.015119 | -3.958762 | | NP2 control | 500 | -0.001334 | 0.000874 | 0.711346 | 0.000000 | 0.000000 | 0.988124 | ## paired One frozen-candidate confirmation on **2510000..2511999**, 2,000 seeds, each evaluated once with `amm_sim_rs.run_batch(candidate_bytecode, np2_bytecode, configs, 8)`. No confirmation seed was used for tuning; no post-confirmation selection or strategy code changes. Chunk size was 20. ```json { "n": 2000, "mean_diff": 0.38525673639056324, "se": 0.017993012289943672, "worst": [ { "seed": 2510881, "diff": -5.333367692296292, "candidate_edge": 361.97991853431984, "np2_edge": 367.3132862266161 }, { "seed": 2510542, "diff": -3.8291713174841107, "candidate_edge": 401.5585796217864, "np2_edge": 405.3877509392705 }, { "seed": 2510520, "diff": -3.698809589071061, "candidate_edge": 597.1025278868889, "np2_edge": 600.8013374759599 }, { "seed": 2511929, "diff": -3.5617249968063334, "candidate_edge": 795.3264082000852, "np2_edge": 798.8881331968915 }, { "seed": 2510775, "diff": -3.304395731294278, "candidate_edge": 496.6677301152882, "np2_edge": 499.97212584658246 } ] } ``` Edges and differences are total Y per simulation. Mean candidate edge: 576.503069990751; mean NP2 edge in the same markets: 576.117813254360. `worst` lists the five smallest paired differences. SE is sample SD of per-seed differences (`ddof=1`) divided by sqrt(n). This is the explicitly requested direct matchup, not a held-out common-vanilla leaderboard comparison. Configurations follow [MatchRunner’s NumPy draw order](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/match.py) and [canonical settings](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/config.py): `default_rng(seed)` draws size U(19,21), rate U(0.6,1.0), sigma U(0.000882,0.001008), in that order; 10,000 steps, initial price 100, reserves 100 X / 10,000 Y, mu=0, dt=1, retail buy probability=0.5, log-size sigma=1.2, cfg.seed=seed. The [engine](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/simulation/engine.rs) uses price seed=seed and retail seed=seed+1. All simulation source files remained unmodified. Compilation used the [pinned compiler settings](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/evm/compiler.py): Solidity 0.8.24, optimizer 200, viaIR, Paris EVM. Reproduction core (after the supplied setup; save the code above as `scratch/Candidate.sol` and the specified NP2 reconstruction as `scratch/NP2.sol`). This recipe is provided for independent reproduction; the confirmation reported here was executed only once. ```python from pathlib import Path import numpy as np import amm_sim_rs from amm_competition.evm.compiler import SolidityCompiler from amm_competition.competition.config import build_config compiler = SolidityCompiler() def compile_strategy(path): result = compiler.compile(Path(path).read_text()) assert result.success, result.errors return result.bytecode candidate = compile_strategy("scratch/Candidate.sol") np2 = compile_strategy("scratch/NP2.sol") rows = [] for start in range(2510000, 2512000, 20): configs = [] for seed in range(start, start + 20): rng = np.random.default_rng(seed) size = rng.uniform(19, 21) rate = rng.uniform(0.6, 1.0) sigma = rng.uniform(0.000882, 0.001008) configs.append(build_config(seed=seed, retail_mean_size=size, retail_arrival_rate=rate, gbm_sigma=sigma)) batch = amm_sim_rs.run_batch(candidate, np2, configs, 8) for r in batch.results: a, b = r.edges["submission"], r.edges["normalizer"] rows.append((r.seed, a - b, a, b)) diffs = np.array([r[1] for r in rows]) print(len(rows), diffs.mean(), diffs.std(ddof=1) / len(rows)**0.5) print(sorted(rows, key=lambda r: r[1])[:5]) ``` Runtime check of the delivered bytecode: {"name": "fixed_0p20", "sha256": "23fac70f933d810d3a9113e200547b146a2f2996b3e403eb9948ea1f366c6fee", "n_calls": 5000, "initialization_gas": 189944, "max_after_swap_gas": 153786, "gas_limit": 250000, "all_success": true, "min_fee": 0.0, "max_fee": 0.1, "sides": {"buy": 2531, "sell": 2469}, "timestamp_final": 551856}. This is a finite stress test, not a universal gas bound. Gas was read from `pyrevm.EVM.result.gas_used`; all hooks succeeded under the [250,000 limit](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/evm/executor.py). This check matters because the [CFMM keeps old fees on hook failure](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/amm/cfmm.rs). ## fee_stats Confirmation markets, 2510000..2511999. Each observation is a run’s arithmetic mean of **all 10,000 step-end quotes**, including steps without a fill. The [engine’s `average_fees`](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/simulation/engine.rs) supplies these values. “Mid slope” is cross-run OLS of `(mean_bid+mean_ask)/2` in bps against that AMM’s realized retail Y volume divided by 10,000, with an intercept; it is an association, not a causal effect. “Bid >40” counts runs whose **mean** step-end bid exceeds 40 bps. | Strategy | n | Mean bid bps | Mean ask bps | Bid−ask bps | Mid slope bps / 10k retail Y | Runs bid >40 | |---|---:|---:|---:|---:|---:|---:| | NP2 | 2000 | 36.265347 | 35.542333 | 0.723014 | 1.710477 | 187 | | Candidate | 2000 | 35.770699 | 35.997596 | -0.226896 | 1.705366 | 134 | Across-run SE of the mean fee gap: NP2 0.073790 bps; candidate 0.073383 bps. The frozen correction overcenters the direct NP2 market: its remaining ask-over-bid mean is 0.226896 bps, about 3.09 standard errors from zero. The near-zero standard-opponent gap below is a training result only. Exact mean neutrality across opponents was not achieved; the candidate was not retuned after confirmation. Retail thirds below sort each strategy’s runs by its own realized retail Y, then split into equal-count groups. | Strategy | Third | n | Mean retail Y | Bid bps | Ask bps | Bid−ask bps | |---|---|---:|---:|---:|---:|---:| | NP2 | 1 | 667 | 66348.171 | 33.933667 | 33.357150 | 0.576517 | | NP2 | 2 | 667 | 79576.951 | 36.240760 | 35.519606 | 0.721154 | | NP2 | 3 | 666 | 93232.677 | 38.625152 | 37.753557 | 0.871595 | | Candidate | 1 | 667 | 66412.550 | 33.443069 | 33.811105 | -0.368036 | | Candidate | 2 | 667 | 79658.518 | 35.757260 | 35.972085 | -0.214826 | | Candidate | 3 | 666 | 93328.050 | 38.115284 | 38.212918 | -0.097634 | Standard-opponent fee statistics, **training only**, seeds 10000..10499: | Strategy vs vanilla | n | Mean bid bps | Mean ask bps | Bid−ask bps | Mid slope bps / 10k retail Y | Runs bid >40 | |---|---:|---:|---:|---:|---:|---:| | NP2 | 500 | 36.347524 | 35.359400 | 0.988124 | 1.748024 | 52 | | Candidate | 500 | 35.810109 | 35.833239 | -0.023130 | 1.747647 | 34 | ## blocked `null` — setup completed once; both calibration scores reproduced; candidate validation and runtime checks passed; all requested confirmation seeds completed. ## next Retain NP2 for leaderboard use. Mean centering lost common-vanilla training edge. Future leaderboard deltas should pair separate candidate-vs-Vanilla and NP2-vs-Vanilla runs on identical seeds; direct NP2 matchups can reverse the sign.