{"item":"h2","experiment":"offline H=2 recentering table replaces G fee mapping; V2 direction skew retained","status":"completed_negative_result","recommendation":"Do not replace V2 with this table.","code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport {AMMStrategyBase} from \"./AMMStrategyBase.sol\";\nimport {TradeInfo} from \"./IAMMStrategy.sol\";\n\ncontract Strategy is AMMStrategyBase {\n    uint256 constant ELAPSED_CAP = 8;\n    uint256 constant SIGNAL_THRESHOLD = WAD / 650;\n    uint256 constant DIR_DECAY = 750000000000000000;\n    uint256 constant ACT_DECAY = 530000000000000000;\n    uint256 constant SIZE_DECAY = 530000000000000000;\n    uint256 constant TOX_DECAY = 910000000000000000;\n    uint256 constant SIGMA_DECAY = 690000000000000000;\n    uint256 constant LAMBDA_DECAY = 990000000000000000;\n    uint256 constant SIZE_BLEND_DECAY = 828000000000000000;\n    uint256 constant TOX_BLEND_DECAY = 51000000000000000;\n    uint256 constant ACT_BLEND_DECAY = 985000000000000000;\n    uint256 constant PHAT_ALPHA = 270000000000000000;\n    uint256 constant PHAT_ALPHA_RETAIL = 60000000000000000;\n    uint256 constant PHAT_ALPHA_SAME = 120000000000000000;\n    uint256 constant REVERT_U = 0;\n    uint256 constant DIR_IMPACT_MULT = 4;\n    uint256 constant GATE_SIGMA_MULT = 7200000000000000000;\n    uint256 constant MIN_GATE = 30000000000000000;\n    uint256 constant TRADE_TOX_BOOST = 3500 * BPS;\n    uint256 constant STALE_ATTRACT_FRAC = 1050000000000000000;\n    uint256 constant RET_CAP = WAD / 35;\n    uint256 constant TOX_CAP = WAD / 5;\n    uint256 constant TRADE_RATIO_CAP = WAD / 5;\n    uint256 constant LAMBDA_CAP = 1820000000000000000;\n    uint256 constant STEP_COUNT_CAP = 64;\n    uint256 constant BASE_FEE = 4 * BPS;\n    uint256 constant SIGMA_COEF = 200000000000000000;\n    uint256 constant LAMBDA_COEF = 12 * BPS;\n    uint256 constant FLOW_SIZE_COEF = 4842 * BPS;\n    uint256 constant TOX_COEF = 300 * BPS;\n    uint256 constant TOX_QUAD_COEF = 15000 * BPS;\n    uint256 constant ACT_COEF = 91843 * BPS;\n    uint256 constant DIR_COEF = 20 * BPS;\n    uint256 constant DIR_TOX_COEF = 100 * BPS;\n    uint256 constant STALE_DIR_COEF = 6850 * BPS;\n    uint256 constant SIGMA_TOX_COEF = 500 * BPS;\n    uint256 constant TAIL_KNEE = 600 * BPS;\n    uint256 constant TAIL_SLOPE_PROTECT = 930000000000000000;\n    uint256 constant TAIL_SLOPE_ATTRACT = 850000000000000000;\n\n    function afterInitialize(uint256 initialX, uint256 initialY) external override returns (uint256, uint256) {\n        slots[0] = BASE_FEE; slots[1] = BASE_FEE; slots[3] = WAD; \n        slots[5] = initialX > 0 ? wdiv(initialY, initialX) : 100 * WAD;\n        slots[6] = 950000000000000; slots[7] = 800000000000000000; slots[8] = 2000000000000000;\n        slots[13] = 902500000000; slots[14] = slots[5]; \n        return (BASE_FEE, BASE_FEE);\n    }\n\n    function afterSwap(TradeInfo calldata trade) external override returns (uint256, uint256) {\n        uint256 prevBidFee = slots[0]; uint256 prevAskFee = slots[1]; uint256 lastTs = uint64(slots[2]);\n        uint256 dirState = slots[3]; uint256 actEma = slots[4]; uint256 pHat = slots[5];\n        uint256 sigmaHat = slots[6]; uint256 lambdaHat = slots[7]; uint256 sizeHat = slots[8];\n        uint256 toxEma = slots[9]; uint256 stepTradeCount = slots[10];\n\n        uint256 sigmaForFast = sigmaHat;\n        uint256 fastHat = slots[14];\n        uint256 uncertainty = slots[13];\n        uint256 silent = trade.timestamp > lastTs ? trade.timestamp - lastTs : 0;\n        bool isNewStep = trade.timestamp > lastTs;\n        if (isNewStep) {\n            uint256 elapsedRaw = trade.timestamp - lastTs;\n            uint256 elapsed = elapsedRaw > ELAPSED_CAP ? ELAPSED_CAP : elapsedRaw;\n            dirState = _decayCentered(dirState, DIR_DECAY, elapsed);\n            actEma = wmul(actEma, _powWad(ACT_DECAY, elapsed));\n            sizeHat = wmul(sizeHat, _powWad(SIZE_DECAY, elapsed));\n            toxEma = wmul(toxEma, _powWad(TOX_DECAY, elapsed));\n            if (stepTradeCount > 0 && elapsedRaw > 0) {\n                uint256 lambdaInst = (stepTradeCount * WAD) / elapsedRaw;\n                if (lambdaInst > LAMBDA_CAP) lambdaInst = LAMBDA_CAP;\n                lambdaHat = wmul(lambdaHat, LAMBDA_DECAY) + wmul(lambdaInst, WAD - LAMBDA_DECAY);\n            }\n            uncertainty += wmul(sigmaForFast, sigmaForFast) * elapsedRaw;\n            if (uncertainty > 100000000000000) uncertainty = 100000000000000;\n            stepTradeCount = 0;\n        }\n\n        bool firstInStep = stepTradeCount == 0;\n        uint256 spot = trade.reserveX > 0 ? wdiv(trade.reserveY, trade.reserveX) : pHat;\n        if (pHat == 0) pHat = spot;\n\n        uint256 feeUsed = trade.isBuy ? prevBidFee : prevAskFee;\n        uint256 gamma = feeUsed < WAD ? WAD - feeUsed : 0;\n        uint256 pImplied;\n        if (gamma == 0) { pImplied = spot; }\n        else { pImplied = trade.isBuy ? wmul(spot, gamma) : wdiv(spot, gamma); }\n\n        // S: infer a no-trade interval center from the last post-trade spot.\n        uint256 silenceScale = WAD;\n        if (firstInStep && silent > 1) {\n            uint256 g = silent - 1;\n            if (g > 1) g = 1;\n            uint256 w = WAD - _powWad(800000000000000000, g);\n            uint256 s = slots[2] >> 64;\n            if (s == 0) s = pHat;\n            uint256 c = (wmul(s, WAD - prevBidFee) + wdiv(s, WAD - prevAskFee)) / 2;\n            if (c >= pHat) pHat += wmul(w, c - pHat);\n            else pHat -= wmul(w, pHat - c);\n            silenceScale = WAD - wmul(300000000000000000, w);\n        }\n\n        {\n            uint256 ret = pHat > 0 ? wdiv(absDiff(pImplied, pHat), pHat) : 0;\n            uint256 alpha = firstInStep ? PHAT_ALPHA : PHAT_ALPHA_RETAIL;\n            uint256 prePhat = pHat;\n            bool secondPrint = !firstInStep && stepTradeCount == 1;\n            bool sameDir = false; bool oppDir = false;\n            if (secondPrint) {\n                bool up2 = pImplied >= pHat;\n                uint256 d1 = slots[12];\n                if (d1 != 0) { sameDir = (d1 == 1) == up2; oppDir = !sameDir; }\n                if (sameDir) alpha = PHAT_ALPHA_SAME;\n            }\n            if (firstInStep) { slots[12] = pImplied >= pHat ? 1 : 2; }\n            uint256 adaptiveGate = wmul(sigmaHat, GATE_SIGMA_MULT);\n            if (adaptiveGate < MIN_GATE) adaptiveGate = MIN_GATE;\n            alpha = wmul(alpha, silenceScale);\n            adaptiveGate = wmul(adaptiveGate, silenceScale);\n            if (ret <= adaptiveGate) { pHat = wmul(pHat, WAD - alpha) + wmul(pImplied, alpha); }\n            if (oppDir && REVERT_U > 0) {\n                uint256 pre = slots[11];\n                if (pre > pHat) pHat = pHat + wmul(pre - pHat, REVERT_U);\n                else pHat = pHat - wmul(pHat - pre, REVERT_U);\n            }\n            if (firstInStep) {\n                if (ret > RET_CAP) ret = RET_CAP;\n                sigmaHat = wmul(sigmaHat, SIGMA_DECAY) + wmul(ret, WAD - SIGMA_DECAY);\n            }\n        }\n\n        // Interior retail routing equalizes fee-adjusted marginal prices.\n        // Subsequent trades in a step cannot be the initial arbitrage trade.\n        if (!firstInStep && feeUsed > 30 * BPS && gamma > 0) {\n            uint256 normalSpot = trade.isBuy\n                ? wdiv(wmul(spot, gamma), WAD - 30 * BPS)\n                : wdiv(wmul(spot, WAD - 30 * BPS), gamma);\n            pHat = wmul(pHat, 90 * WAD / 100) + wmul(normalSpot, 10 * WAD / 100);\n        }\n\n        {\n            uint256 ret = fastHat > 0 ? wdiv(absDiff(pImplied, fastHat), fastHat) : 0;\n            uint256 alpha;\n            if (firstInStep) {\n                uint256 processSigma = sigmaForFast;\n                uint256 obsVar = uncertainty + wmul(processSigma, processSigma);\n                uint256 probArb;\n                uint256 z2 = wdiv(wmul(ret, ret), obsVar);\n                uint256 density = _powWad(wdiv(WAD, WAD + z2 / 16), 8);\n                uint256 sigmaObs = _isqrt(obsVar * WAD);\n                uint256 odds = wmul(density, wdiv(6700000000000000, sigmaObs));\n                bool arbFlag = ret <= wmul(sigmaForFast, 1220000000000000000);\n                uint256 prior = arbFlag ? 650000000000000000 : 400000000000000000;\n                uint256 tr = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0;\n                if (tr > 3000000000000000) prior = wmul(prior, 6 * WAD / 10);\n                if (silent > 1) prior = wmul(prior, 8 * WAD / 10);\n                odds = wmul(odds, wdiv(prior, WAD - prior));\n                probArb = wdiv(odds, WAD + odds);\n                alpha = wmul(probArb, 600000000000000000) + wmul(WAD - probArb, 80000000000000000);\n                slots[15] = pImplied >= fastHat ? 1 : 2;\n                uncertainty = wmul(uncertainty, WAD - alpha);\n            } else {\n                if ((slots[15] == 1) == (pImplied >= fastHat)) alpha = PHAT_ALPHA_RETAIL;\n            }\n            uint256 gate = wmul(sigmaHat, GATE_SIGMA_MULT);\n            if (gate < MIN_GATE) gate = MIN_GATE;\n            if (ret <= gate) fastHat = wmul(fastHat, WAD - alpha) + wmul(pImplied, alpha);\n            slots[13] = uncertainty;\n        }\n\n        if (!firstInStep && feeUsed > 30 * BPS && gamma > 0) {\n            uint256 normalObs = trade.isBuy\n                ? wdiv(wmul(spot, gamma), WAD - 30 * BPS)\n                : wdiv(wmul(spot, WAD - 30 * BPS), gamma);\n            if ((slots[15] == 1) == (normalObs >= fastHat)) {\n                fastHat = wmul(fastHat, 9 * WAD / 10) + wmul(normalObs, WAD / 10);\n            }\n        }\n        slots[14] = fastHat;\n        uint256 tradeRatio = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0;\n        if (tradeRatio > TRADE_RATIO_CAP) tradeRatio = TRADE_RATIO_CAP;\n\n        if (tradeRatio > SIGNAL_THRESHOLD) {\n            uint256 push = tradeRatio * DIR_IMPACT_MULT;\n            if (push > WAD / 4) push = WAD / 4;\n            if (trade.isBuy) {\n                dirState = dirState + push;\n                if (dirState > 2 * WAD) dirState = 2 * WAD;\n            } else {\n                dirState = dirState > push ? dirState - push : 0;\n            }\n            actEma = wmul(actEma, ACT_BLEND_DECAY) + wmul(tradeRatio, WAD - ACT_BLEND_DECAY);\n            sizeHat = wmul(sizeHat, SIZE_BLEND_DECAY) + wmul(tradeRatio, WAD - SIZE_BLEND_DECAY);\n            if (sizeHat > WAD) sizeHat = WAD;\n        }\n\n        uint256 tox = pHat > 0 ? wdiv(absDiff(spot, pHat), pHat) : 0;\n        if (tox > TOX_CAP) tox = TOX_CAP;\n        toxEma = wmul(toxEma, TOX_BLEND_DECAY) + wmul(tox, WAD - TOX_BLEND_DECAY);\n        uint256 toxSignal = toxEma;\n\n        stepTradeCount = stepTradeCount + 1;\n        if (stepTradeCount > STEP_COUNT_CAP) stepTradeCount = STEP_COUNT_CAP;\n\n        uint256 staleHat = wmul(pHat, 700000000000000000) + wmul(fastHat, 300000000000000000);\n        uint256 gap = wdiv(absDiff(spot, staleHat), staleHat);\n        uint256 gi;\n        if (gap >= 240 * BPS) gi = 6;\n        else if (gap >= 120 * BPS) gi = 5;\n        else if (gap >= 60 * BPS) gi = 4;\n        else if (gap >= 30 * BPS) gi = 3;\n        else if (gap >= 14 * BPS) gi = 2;\n        else if (gap >= 4 * BPS) gi = 1;\n        uint256 flowSize = wmul(lambdaHat, sizeHat);\n        uint256 fi = flowSize < 1000000000000000 ? 0 : (flowSize < 2200000000000000 ? 1 : 2);\n        bytes memory table = hex\"080808080808080808080808080808080808080808080808060608080706060806060608060606060606\";\n        uint256 action = uint8(table[gi * 6 + fi * 2 + (stepTradeCount > 1 ? 1 : 0)]);\n        uint256 protect;\n        if (action / 3 == 0) protect = gap > 10 * BPS ? gap - 10 * BPS : 0;\n        else protect = gap + (action / 3 == 1 ? 0 : 15) * BPS;\n        uint256 attract = (action % 3) * 10 * BPS;\n        uint256 bidFee = spot >= staleHat ? protect : attract;\n        uint256 askFee = spot >= staleHat ? attract : protect;\n        uint256 dirDev = dirState >= WAD ? dirState - WAD : WAD - dirState;\n        uint256 skew = wmul(DIR_COEF, dirDev) + wmul(DIR_TOX_COEF, wmul(dirDev, toxSignal));\n        if (dirState >= WAD) {\n            bidFee += skew;\n            askFee = askFee > skew ? askFee - skew : 0;\n        } else {\n            askFee += skew;\n            bidFee = bidFee > skew ? bidFee - skew : 0;\n        }\n        bidFee = clampFee(bidFee); askFee = clampFee(askFee);\n\n        slots[0] = bidFee; slots[1] = askFee; slots[2] = (spot << 64) | trade.timestamp;\n        slots[3] = dirState; slots[4] = actEma; slots[5] = pHat;\n        slots[6] = sigmaHat; slots[7] = lambdaHat; slots[8] = sizeHat;\n        slots[9] = toxEma; slots[10] = stepTradeCount;\n\n        return (bidFee, askFee);\n    }\n\n    function _compressTailWithSlope(uint256 fee, uint256 slope) internal pure returns (uint256) {\n        if (fee <= TAIL_KNEE) return fee;\n        return TAIL_KNEE + wmul(fee - TAIL_KNEE, slope);\n    }\n\n    function _powWad(uint256 factor, uint256 exp) internal pure returns (uint256 result) {\n        result = WAD;\n        while (exp > 0) {\n            if (exp & 1 == 1) result = wmul(result, factor);\n            factor = wmul(factor, factor);\n            exp >>= 1;\n        }\n    }\n\n    function _decayCentered(uint256 centered, uint256 decayFactor, uint256 elapsed) internal pure returns (uint256) {\n        uint256 mul = _powWad(decayFactor, elapsed);\n        if (centered >= WAD) { return WAD + wmul(centered - WAD, mul); }\n        uint256 below = wmul(WAD - centered, mul);\n        return below < WAD ? WAD - below : 0;\n    }\n\n    // Floor square root: bit-length seed and seven Newton steps (all uint256).\n    function _isqrt(uint256 x) internal pure returns (uint256 r) {\n        if (x == 0) return 0;\n        unchecked {\n            uint256 y = x;\n            r = 1;\n            if (y >> 128 != 0) { y >>= 128; r <<= 64; }\n            if (y >> 64 != 0) { y >>= 64; r <<= 32; }\n            if (y >> 32 != 0) { y >>= 32; r <<= 16; }\n            if (y >> 16 != 0) { y >>= 16; r <<= 8; }\n            if (y >> 8 != 0) { y >>= 8; r <<= 4; }\n            if (y >> 4 != 0) { y >>= 4; r <<= 2; }\n            if (y >> 2 != 0) { r <<= 1; }\n            r = (r + x / r) >> 1;\n            r = (r + x / r) >> 1;\n            r = (r + x / r) >> 1;\n            r = (r + x / r) >> 1;\n            r = (r + x / r) >> 1;\n            r = (r + x / r) >> 1;\n            r = (r + x / r) >> 1;\n            uint256 t = x / r;\n            return r < t ? r : t;\n        }\n    }\n\n    function getName() external pure override returns (string memory) { return \"H2_4\"; }\n}\n","sha256":"d65ce519370c9885550f8a28202cae4bd6ad6aeba9fe02ddfde5f20e2e91984f","blocked":null,"paired":{"n":2000,"mean_diff":-21.660301482976028,"se":0.1449951201562326,"worst":[{"seed":4261618,"diff":-74.00064255815232,"edge_a":540.5958211178361,"edge_b":614.5964636759884},{"seed":4261317,"diff":-70.23647228974528,"edge_a":569.9915828786833,"edge_b":640.2280551684286},{"seed":4261867,"diff":-60.72075278237321,"edge_a":589.9294598550822,"edge_b":650.6502126374554},{"seed":4261887,"diff":-55.57716358444668,"edge_a":587.220290697161,"edge_b":642.7974542816077},{"seed":4261362,"diff":-54.88037674477198,"edge_a":705.9217345939779,"edge_b":760.8021113387499}],"valid":true,"invalid_reasons":[]},"gas":{"by_callback":{"afterInitialize":{"calls":2000,"ok":2000,"max_ok_gas":220944,"sum_ok_gas":441888000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"afterSwap":{"calls":17672729,"ok":17672729,"max_ok_gas":213197,"sum_ok_gas":1732522189756,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"getName":{"calls":2000,"ok":2000,"max_ok_gas":21557,"sum_ok_gas":43114000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0}},"max_ok":220944,"oog":0,"other_errors":0,"oog_seeds":[]},"v2_gas":{"by_callback":{"afterInitialize":{"calls":2000,"ok":2000,"max_ok_gas":236377,"sum_ok_gas":472754000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"afterSwap":{"calls":20702626,"ok":20702601,"max_ok_gas":243913,"sum_ok_gas":2306165712717,"oog":25,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"getName":{"calls":2000,"ok":2000,"max_ok_gas":21545,"sum_ok_gas":43090000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0}},"max_ok":243913,"oog":25,"other_errors":0,"oog_seeds":[[4260078,1],[4260104,1],[4260239,2],[4260391,1],[4260421,2],[4260500,1],[4260505,1],[4260533,3],[4261296,1],[4261383,2],[4261391,1],[4261396,1],[4261558,1],[4261652,2],[4261795,2],[4261877,1],[4261882,1],[4261890,1]]},"actual_edge_deltas":{"overall":{"total":{"n":2000,"mean":-21.660301482976028,"se":0.1449951201562326,"sd":6.48437890150172},"retail":{"n":2000,"mean":-27.90393419929887,"se":0.16216678292514686,"sd":7.252319006261612},"arb":{"n":2000,"mean":6.243632716322828,"se":0.04584374792147086,"sd":2.050194733915471},"v2_retail_volume_range":[55319.00441455151,98057.18563217501]},"by_v2_retail_volume_tercile":[{"total":{"n":667,"mean":-18.58771209621364,"se":0.2066343447534512,"sd":5.336609492171595},"retail":{"n":667,"mean":-24.352826916057513,"se":0.23396293773546414,"sd":6.042407112066381},"arb":{"n":667,"mean":5.76511481984377,"se":0.0680730097435321,"sd":1.7580769082287588},"v2_retail_volume_range":[55319.00441455151,71079.91752291362]},{"total":{"n":667,"mean":-21.49907633181919,"se":0.2128526991123847,"sd":5.49720684561333},"retail":{"n":667,"mean":-27.81570398196336,"se":0.2336559002808119,"sd":6.034477457405611},"arb":{"n":667,"mean":6.316627650144171,"se":0.07285056569458223,"sd":1.8814637075925136},"v2_retail_volume_range":[71119.44476746948,81952.62879479675]},{"total":{"n":666,"mean":-24.898971598279516,"se":0.2673623924324968,"sd":6.899814791637101},"retail":{"n":666,"mean":-31.548736170597323,"se":0.2979127618629368,"sd":7.6882274362439835},"arb":{"n":666,"mean":6.649764572317867,"se":0.09165039791520813,"sd":2.365219601161517},"v2_retail_volume_range":[81997.59140599043,98057.18563217501]}]},"actual_mean_edges":{"candidate":{"total":506.31913481162593,"retail":527.804379291074,"arb":-21.485244479448028},"V2":{"total":527.979436294602,"retail":555.7083134903728,"arb":-27.72887719577086}},"frozen":{"name":"H2_4","sha256":"d65ce519370c9885550f8a28202cae4bd6ad6aeba9fe02ddfde5f20e2e91984f","unix_time":1794114768.6933858,"before_confirmation":true},"seed_ranges_inclusive":{"smoke":[10000,10007],"training":[10000,10063],"top2_selection":[12000,12255],"confirmation":[4260000,4261999],"offline_mc_rng":[10000,10041]},"training":{"H2_0":{"paired":{"n":64,"mean_diff":-34.505917711980246,"se":1.1019459771453335,"worst":[{"seed":10014,"diff":-75.97667213568843,"edge_a":506.58411839851215,"edge_b":582.5607905342006},{"seed":10058,"diff":-56.691787682719735,"edge_a":375.14284700930426,"edge_b":431.834634692024},{"seed":10052,"diff":-53.04124478350508,"edge_a":472.509088912427,"edge_b":525.5503336959321},{"seed":10003,"diff":-44.42018469306629,"edge_a":607.2861314451451,"edge_b":651.7063161382114},{"seed":10022,"diff":-44.14339802128916,"edge_a":594.407403604936,"edge_b":638.5508016262252}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"664931854fc2c9e7253e3810db0bfa38b333a19db57279df658d1a0f3bf379d7","seconds":16.771,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":7.617018127348274e-12}},"H2_1":{"paired":{"n":64,"mean_diff":-35.040030504507826,"se":1.0928860485420193,"worst":[{"seed":10014,"diff":-73.72125010562542,"edge_a":508.83954042857516,"edge_b":582.5607905342006},{"seed":10058,"diff":-57.77806165507661,"edge_a":374.0565730369474,"edge_b":431.834634692024},{"seed":10052,"diff":-54.900051689874886,"edge_a":470.6502820060572,"edge_b":525.5503336959321},{"seed":10022,"diff":-46.01734077681908,"edge_a":592.5334608494061,"edge_b":638.5508016262252},{"seed":10034,"diff":-44.86913461293972,"edge_a":464.79544552217874,"edge_b":509.66458013511846}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"4ed33a8d81cd46aab50427ae9c7dd8708e7b375c6660f695593cbec1f319abde","seconds":16.834,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":6.764366844436154e-12}},"H2_2":{"paired":{"n":64,"mean_diff":-34.82807550931481,"se":1.091036305037854,"worst":[{"seed":10014,"diff":-73.98646810247226,"edge_a":508.5743224317283,"edge_b":582.5607905342006},{"seed":10058,"diff":-57.67116823403285,"edge_a":374.16346645799115,"edge_b":431.834634692024},{"seed":10052,"diff":-54.24474767115805,"edge_a":471.305586024774,"edge_b":525.5503336959321},{"seed":10022,"diff":-45.209093897157686,"edge_a":593.3417077290675,"edge_b":638.5508016262252},{"seed":10040,"diff":-44.626076609266306,"edge_a":554.3663651834664,"edge_b":598.9924417927327}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"42068411b3c9928ac8c22288e3e366aaba8b6bbe3c6fe445e50425fe20a66188","seconds":16.914,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":6.195932655828074e-12}},"H2_3":{"paired":{"n":64,"mean_diff":-36.004676709463475,"se":1.0752623474526035,"worst":[{"seed":10014,"diff":-74.63087063402389,"edge_a":507.9299199001767,"edge_b":582.5607905342006},{"seed":10058,"diff":-55.27328550655403,"edge_a":376.56134918546996,"edge_b":431.834634692024},{"seed":10052,"diff":-53.658425357365445,"edge_a":471.8919083385666,"edge_b":525.5503336959321},{"seed":10022,"diff":-46.41312226367836,"edge_a":592.1376793625468,"edge_b":638.5508016262252},{"seed":10034,"diff":-45.75883700743509,"edge_a":463.90574312768337,"edge_b":509.66458013511846}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"9cb6b43916e2a04670879fe1125bef1a6c904446cea28ac605cf7d0728bcb2e2","seconds":16.71,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":7.901235221652314e-12}},"H2_4":{"paired":{"n":64,"mean_diff":-21.36192350285016,"se":0.8172927395232398,"worst":[{"seed":10014,"diff":-49.0802493938902,"edge_a":533.4805411403104,"edge_b":582.5607905342006},{"seed":10058,"diff":-35.89221537778582,"edge_a":395.9424193142382,"edge_b":431.834634692024},{"seed":10003,"diff":-34.186443703991586,"edge_a":617.5198724342198,"edge_b":651.7063161382114},{"seed":10022,"diff":-32.20847609472116,"edge_a":606.342325531504,"edge_b":638.5508016262252},{"seed":10053,"diff":-30.448911900267376,"edge_a":645.3709238606909,"edge_b":675.8198357609583}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"d65ce519370c9885550f8a28202cae4bd6ad6aeba9fe02ddfde5f20e2e91984f","seconds":21.873,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":4.888534022029489e-12}},"H2_5":{"paired":{"n":64,"mean_diff":-25.79417897816225,"se":0.8885190414991363,"worst":[{"seed":10014,"diff":-55.69911820243874,"edge_a":526.8616723317618,"edge_b":582.5607905342006},{"seed":10058,"diff":-43.158360477170675,"edge_a":388.6762742148533,"edge_b":431.834634692024},{"seed":10022,"diff":-37.64061157227695,"edge_a":600.9101900539482,"edge_b":638.5508016262252},{"seed":10003,"diff":-36.26947387866983,"edge_a":615.4368422595415,"edge_b":651.7063161382114},{"seed":10005,"diff":-34.71944936331238,"edge_a":422.6131653210394,"edge_b":457.33261468435177}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"73a6430c9774455e45ae8be7694c2381d7605fb4e65023f4dcbf126f38e624df","seconds":25.284,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":6.0822458181064576e-12}},"H1":{"paired":{"n":64,"mean_diff":-34.505917711980246,"se":1.1019459771453335,"worst":[{"seed":10014,"diff":-75.97667213568843,"edge_a":506.58411839851215,"edge_b":582.5607905342006},{"seed":10058,"diff":-56.691787682719735,"edge_a":375.14284700930426,"edge_b":431.834634692024},{"seed":10052,"diff":-53.04124478350508,"edge_a":472.509088912427,"edge_b":525.5503336959321},{"seed":10003,"diff":-44.42018469306629,"edge_a":607.2861314451451,"edge_b":651.7063161382114},{"seed":10022,"diff":-44.14339802128916,"edge_a":594.407403604936,"edge_b":638.5508016262252}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"e580e6563deb784cc5b202e716a4a792cd2994de03006cc71752ed75d79bbbe1","seconds":16.81,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":7.617018127348274e-12}}},"top2_selection":{"H2_4":{"paired":{"n":256,"mean_diff":-21.715410399860996,"se":0.42131586364308554,"worst":[{"seed":12211,"diff":-60.33793058661354,"edge_a":394.21513818399336,"edge_b":454.5530687706069},{"seed":12000,"diff":-54.207596062094694,"edge_a":592.2942469679971,"edge_b":646.5018430300918},{"seed":12123,"diff":-52.53032210547508,"edge_a":598.2714346412964,"edge_b":650.8017567467715},{"seed":12241,"diff":-42.47703279673976,"edge_a":684.0290569972927,"edge_b":726.5060897940325},{"seed":12039,"diff":-42.170445038704486,"edge_a":442.8619855444328,"edge_b":485.0324305831373}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"d65ce519370c9885550f8a28202cae4bd6ad6aeba9fe02ddfde5f20e2e91984f","seconds":87.364,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":8.128608897095546e-12}},"H2_5":{"paired":{"n":256,"mean_diff":-25.815286063584203,"se":0.42747709284183366,"worst":[{"seed":12211,"diff":-65.66400015996811,"edge_a":388.8890686106388,"edge_b":454.5530687706069},{"seed":12000,"diff":-56.99448959415304,"edge_a":589.5073534359387,"edge_b":646.5018430300918},{"seed":12096,"diff":-53.10665869439481,"edge_a":547.9153620433641,"edge_b":601.0220207377589},{"seed":12203,"diff":-49.09809766035323,"edge_a":486.56529244983585,"edge_b":535.6633901101891},{"seed":12241,"diff":-45.515469476799694,"edge_a":680.9906203172328,"edge_b":726.5060897940325}],"valid":true,"invalid_reasons":[]},"run":{"sha256":"73a6430c9774455e45ae8be7694c2381d7605fb4e65023f4dcbf126f38e624df","seconds":79.641,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":7.560174708487466e-12}}},"ablation":{"matched_to":"H2_4","h1_vs_v2":{"n":64,"mean_diff":-21.36192350285016,"se":0.8172927395232398,"worst":[{"seed":10014,"diff":-49.0802493938902,"edge_a":533.4805411403104,"edge_b":582.5607905342006},{"seed":10058,"diff":-35.89221537778582,"edge_a":395.9424193142382,"edge_b":431.834634692024},{"seed":10003,"diff":-34.186443703991586,"edge_a":617.5198724342198,"edge_b":651.7063161382114},{"seed":10022,"diff":-32.20847609472116,"edge_a":606.342325531504,"edge_b":638.5508016262252},{"seed":10053,"diff":-30.448911900267376,"edge_a":645.3709238606909,"edge_b":675.8198357609583}],"valid":true,"invalid_reasons":[]},"h2_minus_h1":{"n":64,"mean_diff":0.0,"se":0.0,"worst":[{"seed":10000,"diff":0.0,"edge_a":464.60173152584224,"edge_b":464.60173152584224},{"seed":10001,"diff":0.0,"edge_a":534.3668699477436,"edge_b":534.3668699477436},{"seed":10002,"diff":0.0,"edge_a":427.8452052860663,"edge_b":427.8452052860663},{"seed":10003,"diff":0.0,"edge_a":617.5198724342198,"edge_b":617.5198724342198},{"seed":10004,"diff":0.0,"edge_a":459.77949556302445,"edge_b":459.77949556302445}],"valid":true,"invalid_reasons":[],"label":"H2 minus H1, identical training seeds"},"h1_run":{"sha256":"a21fe25c9369b22b844946654e05deb1568c31bcb3ff38b14c4599d1712727f1","seconds":22.084,"gas":{"max_ok":220944,"oog":0,"other_errors":0},"counters_per_seed_match_global":true,"max_abs_reconciliation_error":4.888534022029489e-12},"note":"H1 matched to the frozen winner has identical actions/priors; only horizon changes. H1 in training is the original H2_0 ablation. No H1 result selected the winner."},"table":{"state_order":"gap*6 + flow*2 + (post-update count >1)","gap_midpoints_bps":[0,8,20,40,80,160,320],"gap_cutpoints_bps":[4,14,30,60,120,240],"flow_cutpoints_wad":[1000000000000000,2200000000000000],"gap_estimator":"abs(spot/(0.7*pHat+0.3*fastHat)-1)","flow_estimator":"lambdaHat*sizeHat (WAD multiplication)","joint_action_semantics":"protect=max(0,absolute observable gap + offset_bps*1e-4); attract=attract_bps*1e-4. For spot>=estimate protect is bid, otherwise ask. Then apply unchanged V2 DIR_COEF/DIR_TOX_COEF skew, clamp to 10%. Nine gap-relative joint action templates per variant, not nine absolute fee pairs.","selected":"H2_4","draws_per_state_action":8192,"variants":[{"actions":[[-10,0],[-10,15],[-10,30],[10,0],[10,15],[10,30],[30,0],[30,15],[30,30]],"h1":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,8,8,8,8,7,6,6,6,7,6,6,6,8,6,6,6,8,6,6,6,6,6,6],"h2":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,8,8,8,8,7,6,6,6,7,6,6,6,8,6,6,6,8,6,6,6,6,6,6],"uncertainty":0.0015,"remaining_multiplier":0.6,"name":"H2_0"},{"actions":[[-10,0],[-10,15],[-10,30],[10,0],[10,15],[10,30],[30,0],[30,15],[30,30]],"h1":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,7,8,7,6,6,6,6,6,6,6,6,6,6,6,6,6],"h2":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,7,8,7,6,6,6,6,6,6,6,6,6,6,6,6,6],"uncertainty":0.003,"remaining_multiplier":0.6,"name":"H2_1"},{"actions":[[-10,0],[-10,15],[-10,30],[10,0],[10,15],[10,30],[30,0],[30,15],[30,30]],"h1":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6],"h2":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6],"uncertainty":0.0015,"remaining_multiplier":1,"name":"H2_2"},{"actions":[[-10,0],[-10,15],[-10,30],[10,0],[10,15],[10,30],[30,0],[30,15],[30,30]],"h1":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,6,6,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6],"h2":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,6,6,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6],"uncertainty":0.003,"remaining_multiplier":1,"name":"H2_3"},{"actions":[[-10,0],[-10,10],[-10,20],[0,0],[0,10],[0,20],[15,0],[15,10],[15,20]],"h1":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,8,8,7,6,6,8,6,6,6,8,6,6,6,6,6,6],"h2":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,8,8,7,6,6,8,6,6,6,8,6,6,6,6,6,6],"uncertainty":0.0015,"remaining_multiplier":0.6,"name":"H2_4"},{"actions":[[-10,0],[-10,15],[-10,30],[5,0],[5,15],[5,30],[20,0],[20,15],[20,30]],"h1":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,7,8,6,6,6,8,6,6,6,8,6,6,6,6,6,6],"h2":[8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,7,8,6,6,6,8,6,6,6,8,6,6,6,6,6,6],"uncertainty":0.0015,"remaining_multiplier":0.6,"name":"H2_5"}]},"method":{"repository":"https://github.com/benedictbrady/amm-challenge","commit":"6b6921f8d463314aa7c96234e1be25a25245f704","inputs":{"H":"https://api.imd.fun/artifacts/db62deadd0a3ec885ad69bd3917fee793a924ead522776d73cc09110923e1e86","Gsrc":"https://api.imd.fun/artifacts/f09f39f5092e5b1e56f156df10f397e5df0e194afa630ab24fc699adfd980d31","rules":"https://api.imd.fun/artifacts/ddbbaac58282566e0505d895c83daf348487cc322e8c482e61d4d87e6e4628de"},"input_hashes_verified":{"V2":"28417e116f57f1029fdd1803832e852574902c1ae7253c05657f317db76da4ab","G":"610c1905e7dd5559b84e40a3b2d6f8e0b53a7b0ac7574ae33b6c1770a28367cb"},"setup":"Prescribed setup run once successfully with requested cargo fallback. Applied H instrumentation.patch and rebuilt release.","scoring":"Each strategy separately against VanillaStrategy through amm_sim_rs.run_batch(X,Vanilla,configs,8); chunks of 8; 10000 steps; 250000 init/swap gas. H config builder. All confirmation seeds returned exactly once per strategy.","selection":"Six H2 variants on 64 training seeds; top two on 256 seeds. Pick highest valid paired mean on selection block; freeze source/hash, then confirm once. No changes after confirmation began.","offline":"Monte Carlo finite-state Bellman backup V1(s)=max_a E[edge], Q2(s,a)=E[edge]+sum P(s_next|s,a)V1(s_next). Transition executes Poisson remaining prints, then GBM price advance and exactly one arb attempt per pool. Terminal V0=0; H2 therefore values later recentering through V1. Common random numbers across actions. Lognormal mean20 sigma1.2, equally probable sides, exact fee-on-input k preservation, two-pool split and dust thresholds. Latent fair and normalizer spot integrated offline.","runtime":"Only G/V2 callback observables, persistent estimators, gap, flow and count. No latent price/reserves, seed, future, final-print flag or next-mix. Original midpoint/stale/tradeBoost/tail fee mapping is replaced.","statistic":"d=actual submission edge(candidate)-actual submission edge(V2) by seed; mean=sum(d)/n; se=sample_std(d)/sqrt(n); worst=lowest five d. Retail and arb deltas are measured instrumented edge components, not proxies. Terciles sort V2 submission retail_volume_y then seed; group sizes 667,667,666."},"limitations":["H2 and H1 selected identical actions in all 42 bins for all six priors/action sets. The continuation backup changed values but not policies; this experiment does not demonstrate a benefit from H2.","Finite-horizon optimization is exact for its sampled, aggregated transition model only, not an exact belief-state solver for the simulator.","Within one offline macro-transition fees are held fixed through remaining prints. Actual contract requotes each callback. This approximation is part of the tested hypothesis.","Offline fair prior is centered on estimate100 with lognormal uncertainty .0015 or .003; normalizer spot uses independent uniform log residual +/-0.003 and k=1e6. These priors are assumed, not empirically fitted posteriors.","Offline count uses a Poisson remainder with heuristic multiplier 1 for first print, .6 for later prints; it never observes the final print. Flow bins shift rate priors; the estimator-transition approximation averages post-arb spots with a .27 update then rebins and resets count to first-print.","Nine actions are gap-relative fee templates. Large-gap states above 240bp share a table row while protective fees still depend on continuous observable gap.","This is a negative/positive result for the delivered model as measured locally; no leaderboard submission or rank improvement is claimed."],"checks":{"harness_tests":{"matchrunner_equal_10000_10031":true,"far_block_len":4,"far_block_seconds":0.0003,"allocation_scales_with_n":true,"far_seed_equals_single":true,"validity_cases":{"cand_oog_equal_v2_valid":true,"cand_oog_less_valid":true,"cand_oog_more_invalid":true,"v2_oog_alone_not_invalid":true,"revert_invalid":true,"ref_revert_invalid":true,"vanilla_oog_invalid":true,"vanilla_other_invalid":true,"incomplete_invalid":true,"control_oog_match_valid":true,"control_oog_mismatch_invalid":true},"all_pass":true},"all_six_sources_reproduced_from_delivered_template":true,"h1_h2_changed_bin_counts":{"H2_0":0,"H2_1":0,"H2_2":0,"H2_3":0,"H2_4":0,"H2_5":0},"cfmm_unit_cases":10000,"cfmm_max_k_error":1.1641532182693481e-10,"known_equal_reserve_10bp_share":0.7507526959629062,"confirmation":{"candidate":{"complete":true,"n_requested":2000,"n_returned":2000,"counters_per_seed_match_global":true,"max_abs_reconciliation_error":9.379164112033322e-12,"seconds":685.115},"V2":{"complete":true,"n_requested":2000,"n_returned":2000,"counters_per_seed_match_global":true,"max_abs_reconciliation_error":9.43600753089413e-12,"seconds":906.346}},"normalizer_gas":{"candidate_market":{"by_callback":{"afterInitialize":{"calls":2000,"ok":2000,"max_ok_gas":21796,"sum_ok_gas":43592000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"afterSwap":{"calls":21707247,"ok":21707247,"max_ok_gas":22592,"sum_ok_gas":489788940624,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"getName":{"calls":2000,"ok":2000,"max_ok_gas":21526,"sum_ok_gas":43052000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0}},"max_ok":22592,"oog":0,"other_errors":0,"oog_seeds":[]},"v2_market":{"by_callback":{"afterInitialize":{"calls":2000,"ok":2000,"max_ok_gas":21796,"sum_ok_gas":43592000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"afterSwap":{"calls":21529306,"ok":21529306,"max_ok_gas":22592,"sum_ok_gas":485764962224,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0},"getName":{"calls":2000,"ok":2000,"max_ok_gas":21526,"sum_ok_gas":43052000,"oog":0,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0}},"max_ok":22592,"oog":0,"other_errors":0,"oog_seeds":[]}}},"timing":{"smoke_seconds":{"V2":3.633,"H2_0":1.994},"confirmation_seconds":1591.461},"generator":{"language":"Python","dependency":"numpy, already required by the prescribed simulator environment","filename":"generate.py","content":"\"\"\"Offline finite horizon Monte Carlo table. No runtime latent inputs.\nNormalized liquidity k=1e6; exact fee-on-input CFMM, two-pool router,\nlognormal arithmetic mean 20 sigma 1.2, Poisson remainder, then one arb.\nLatent fair-price and normalizer spot are Monte Carlo integrated priors.\nH2 = one-step reward + H1 value after recentering, with state aggregation.\n\"\"\"\nimport json, hashlib, re\nfrom pathlib import Path\nimport numpy as np\nGAPS=np.array([0,8,20,40,80,160,320])*1e-4\nCUTS=(GAPS[:-1]+GAPS[1:])/2\nFLOWS=np.array([.0006,.0015,.003])\nFCUTS=np.array([.001,.0022])\nACTIONS=[(a,b) for a in [-10,10,30] for b in [0,15,30]]\nN=8192\n\ndef fees(gap, action):\n    a,b=ACTIONS[action]\n    protect=np.clip(np.abs(gap)+a*1e-4,0,.1)\n    attract=np.full_like(gap,b*1e-4)\n    return np.where(gap>=0,protect,attract),np.where(gap>=0,attract,protect)\n\ndef arb(x,y,p,bid,ask):\n    k=x*y\n    q=np.maximum(0,np.minimum(x-np.sqrt(k/((1-ask)*p)),.99*x))\n    paid=(k/(x-q)-y)/(1-ask)\n    buy=(y/x<p)&(q>0)&(p*q>paid)\n    nx=np.where(buy,x-q,x);ny=k/nx\n    edge=np.where(buy,paid-p*q,0)\n    q=np.maximum(0,(np.sqrt(k*(1-bid)/p)-x)/(1-bid))\n    out=y-k/(x+(1-bid)*q)\n    sell=(y/x>p)&(q>0)&(out>p*q)\n    nx=np.where(sell,x+(1-bid)*q,nx);ny=k/nx\n    edge+=np.where(sell,p*q-out,0)\n    return nx,ny,edge\n\ndef retail(x,y,x2,y2,p,bid,ask,size,buy,active):\n    gamma=np.where(buy,1-ask,1-bid)\n    r=np.sqrt(x*y*gamma/(x2*y2*.997))\n    total=np.where(buy,size,size/p)\n    reserve=np.where(buy,y,x);reserve2=np.where(buy,y2,x2)\n    amount=np.clip((r*(reserve2+.997*total)-reserve)/(gamma+r*.997),0,total)\n    amount2=total-amount\n    amount=np.where(active&(amount>.0001),amount,0)\n    amount2=np.where(active&(amount2>.0001),amount2,0)\n    k=x*y;k2=x2*y2\n    new_in=reserve+gamma*amount\n    out=np.where(buy,x,y)-k/new_in\n    edge=np.where(buy,amount-p*out,p*amount-out)\n    x=np.where(buy,k/new_in,new_in);y=k/x\n    new_in2=reserve2+.997*amount2\n    x2=np.where(buy,k2/new_in2,new_in2);y2=k2/x2\n    return x,y,x2,y2,edge\n\ndef transition(g,f,c,a,uncertainty,remain,seed):\n    rng=np.random.default_rng(seed)\n    # Symmetric gap orientation; latent price is not exposed to table lookup.\n    sign=rng.choice([-1,1],N);gap=GAPS[g]*sign\n    spot=100*(1+gap);x=np.sqrt(1e6/spot);y=1e6/x\n    p=100*np.exp(rng.normal(-.5*uncertainty**2,uncertainty,N))\n    # Opponent residual in its 30bp no-arbitrage band; independent prior.\n    ns=p*np.exp(rng.uniform(-.003,.003,N));x2=np.sqrt(1e6/ns);y2=1e6/x2\n    rate=np.clip(rng.uniform(.6,1,N)+[-.1,0,.1][f],.6,1)\n    lam=rate*remain*[1,.6][c]\n    counts=rng.poisson(lam)\n    bid,ask=fees(gap,a);reward=np.zeros(N)\n    for j in range(int(counts.max())):\n        size=rng.lognormal(np.log(20)-1.2**2/2,1.2,N)\n        buy=rng.random(N)<.5\n        x,y,x2,y2,e=retail(x,y,x2,y2,p,bid,ask,size,buy,j<counts)\n        reward+=e\n    # Exactly one attempt on each pool at the following step's advanced price.\n    sigma=rng.uniform(.000882,.001008,N)\n    pn=p*np.exp(rng.normal(size=N)*sigma-.5*sigma*sigma)\n    x,y,e=arb(x,y,pn,bid,ask);reward+=e\n    x2,y2,_=arb(x2,y2,pn,np.full(N,.003),np.full(N,.003))\n    # Moment approximation to the estimator update; never passes pn to policy.\n    est=100*.73+(.5*(y/x)+.5*(y2/x2))*.27\n    nextgap=np.abs(y/x/est-1)\n    ng=np.searchsorted(CUTS,nextgap)\n    # Preserve flow regime, marginalize first-print count next step.\n    state=ng*6+f*2\n    return reward,state\n\ndef build(uncertainty,remain):\n    rewards=np.zeros((42,9)); trans=np.zeros((42,9,42))\n    for g in range(7):\n      for f in range(3):\n       for c in range(2):\n        s=g*6+f*2+c\n        for a in range(9):\n            r,ns=transition(g,f,c,a,uncertainty,remain,10000+s)\n            rewards[s,a]=r.mean()\n            trans[s,a]=np.bincount(ns,minlength=42)/N\n    v1=rewards.max(axis=1)\n    q2=rewards+trans@v1\n    return {'actions':list(ACTIONS),'h1':rewards.argmax(axis=1).tolist(),'h2':q2.argmax(axis=1).tolist(),\n            'uncertainty':uncertainty,'remaining_multiplier':remain,\n            'h1_value':v1.tolist(),'h2_value':q2.max(axis=1).tolist()}\n\ndef source(table,name,protect_offsets=(-10,10,30),attract_step=15):\n    if Path('scratch/G.sol').exists():\n        s=Path('scratch/G.sol').read_text();a=s.index('        uint256 flowSize =')\n    else:\n        s=json.loads(Path('result.json').read_text())['code'];a=s.index('        uint256 staleHat =')\n    b=s.index('        slots[0] = bidFee;',a)\n    mapping='''        uint256 staleHat = wmul(pHat, 700000000000000000) + wmul(fastHat, 300000000000000000);\n        uint256 gap = wdiv(absDiff(spot, staleHat), staleHat);\n        uint256 gi;\n        if (gap >= 240 * BPS) gi = 6;\n        else if (gap >= 120 * BPS) gi = 5;\n        else if (gap >= 60 * BPS) gi = 4;\n        else if (gap >= 30 * BPS) gi = 3;\n        else if (gap >= 14 * BPS) gi = 2;\n        else if (gap >= 4 * BPS) gi = 1;\n        uint256 flowSize = wmul(lambdaHat, sizeHat);\n        uint256 fi = flowSize < 1000000000000000 ? 0 : (flowSize < 2200000000000000 ? 1 : 2);\n        bytes memory table = hex\"TABLE\";\n        uint256 action = uint8(table[gi * 6 + fi * 2 + (stepTradeCount > 1 ? 1 : 0)]);\n        uint256 protect;\n        if (action / 3 == 0) protect = gap > 10 * BPS ? gap - 10 * BPS : 0;\n        else protect = gap + (action / 3 == 1 ? 10 : 30) * BPS;\n        uint256 attract = (action % 3) * 15 * BPS;\n        uint256 bidFee = spot >= staleHat ? protect : attract;\n        uint256 askFee = spot >= staleHat ? attract : protect;\n        uint256 dirDev = dirState >= WAD ? dirState - WAD : WAD - dirState;\n        uint256 skew = wmul(DIR_COEF, dirDev) + wmul(DIR_TOX_COEF, wmul(dirDev, toxSignal));\n        if (dirState >= WAD) {\n            bidFee += skew;\n            askFee = askFee > skew ? askFee - skew : 0;\n        } else {\n            askFee += skew;\n            bidFee = bidFee > skew ? bidFee - skew : 0;\n        }\n        bidFee = clampFee(bidFee); askFee = clampFee(askFee);\n\n'''.replace('TABLE',bytes(table).hex())\n    mapping=mapping.replace('10 : 30',f'{protect_offsets[1]} : {protect_offsets[2]}').replace('(action % 3) * 15','(action % 3) * '+str(attract_step))\n    s=s[:a]+mapping+s[b:]\n    return re.sub(r'return \"[^\"]+\"', 'return \"'+name+'\"', s)\n\nif __name__=='__main__':\n    Path('scratch').mkdir(exist_ok=True)\n    results=[]\n    for i,(u,r) in enumerate([(.0015,.6),(.003,.6),(.0015,1),(.003,1)]):\n        d=build(u,r);d['name']='H2_'+str(i);results.append(d)\n        Path('scratch/'+d['name']+'.sol').write_text(source(d['h2'],d['name']))\n        if i==0: Path('scratch/H1.sol').write_text(source(d['h1'],'H1_ablation'))\n        print(i,d['h2'],flush=True)\n    for i,offsets,astep in [(4,[-10,0,15],10),(5,[-10,5,20],15)]:\n        ACTIONS[:]=[(a,b) for a in offsets for b in [0,astep,2*astep]]\n        d=build(.0015,.6);d['name']='H2_'+str(i);results.append(d)\n        Path('scratch/'+d['name']+'.sol').write_text(source(d['h2'],d['name'],offsets,astep))\n        print(i,d['h2'],flush=True)\n    Path('scratch/tables.json').write_text(json.dumps({'actions':ACTIONS,'gap_representatives':GAPS.tolist(),'flow_representatives':FLOWS.tolist(),'draws_per_state_action':N,'variants':results}))\n","reproduction":"Place result.json and generate.py in the working directory; run python generate.py in the prescribed environment. It uses result.code as estimator template if scratch/G.sol is absent. Selected table and Solidity source are already included; generator is not a runtime contract dependency."},"source_files":[{"path":"scratch/harness.py","content":"\"\"\"Corrected paired-edge / gas harness for amm-challenge @ 6b6921f8 (e04-harness-repair/control).\n\nUsage (from checkout root, after scratch/setup.sh, git apply scratch/instrumentation.patch and\na release rebuild):\n  . scratch/env.sh && python scratch/harness.py CANDIDATE.sol S0 S1 OUT.json --ref V2.sol\n  . scratch/env.sh && python scratch/harness.py V2.sol S0 S1 OUT.json --control\n\nSeeds are the half-open range S0..S1-1. Each strategy is measured on its own:\namm_sim_rs.run_batch(X, VanillaStrategy, configs, 8), never two strategies in one market.\n--ref: run A = CANDIDATE, run B = REF (V2); paired diff = edge_A - edge_B per seed.\n--control: run A and run B are two independent runs of the same strategy (expect diff 0).\n\nValidity (replaces the old \"any OOG is invalid\" rule):\n  * every requested seed returns exactly once, with finite edges, in both runs;\n  * no revert / tx_error / other_halt / bad_return in any role or callback, in either run;\n  * Vanilla (Baseline role) has no failure of any kind, OOG included;\n  * candidate OutOfGas count <= reference OutOfGas count on the same seeds (V2 itself has\n    rare OOG halts, so the reference's OOG alone never invalidates a result).\nGas limits (250000 init/swap, 50000 name) and the engine's keep-previous-fees fallback on a\nfailed afterSwap are unchanged; failures are only counted.\nExit 0 = valid, 2 = invalid, 1 = runtime error (exception before a result could be written).\n\"\"\"\nimport hashlib, json, math, sys, time\nfrom pathlib import Path\n\nimport numpy as np\nimport amm_sim_rs\nfrom amm_competition.competition.config import BASELINE_VARIANCE as V, build_config\nfrom amm_competition.evm.baseline import get_vanilla_bytecode_and_abi\nfrom amm_competition.evm.compiler import SolidityCompiler\n\nN_WORKERS = 8\nCHUNK = 64  # seeds per run_batch call (bounds memory; each call is still run_batch(X, Vanilla, cfgs, 8))\nCALLBACKS = (\"afterInitialize\", \"afterSwap\", \"getName\")\nFIELDS = (\"calls\", \"ok\", \"max_ok_gas\", \"sum_ok_gas\", \"oog\", \"revert\", \"other_halt\", \"tx_error\", \"bad_return\")\nFAIL_KEYS = (\"revert\", \"other_halt\", \"tx_error\", \"bad_return\")\nROLES = {\"submission\": \"Submission\", \"normalizer\": \"Baseline\"}  # result name -> EVMStrategy role\n\n\ndef config_for_seed(seed):\n    \"\"\"MatchRunner._build_configs for one seed, drawn directly: default_rng(seed) then size, rate,\n    sigma in that order. The engine uses seed for the price process and seed+1 for retail.\"\"\"\n    rng = np.random.default_rng(seed=seed)\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    return build_config(seed=seed, gbm_sigma=sigma, retail_arrival_rate=rate, retail_mean_size=size)\n\n\ndef build_configs(seed_start, seed_end):\n    \"\"\"Configs for seeds seed_start..seed_end-1 only; work and memory scale with the count.\"\"\"\n    return [config_for_seed(s) for s in range(seed_start, seed_end)]\n\n\ndef compile_src(path):\n    src = Path(path).read_text()\n    res = SolidityCompiler().compile(src)\n    if not res.success:\n        raise RuntimeError(\"compile failed: \" + \"; \".join(res.errors or []))\n    return src, list(res.bytecode)\n\n\ndef _zero():\n    return {k: 0 for k in FIELDS}\n\n\ndef _add(acc, st):\n    for k in FIELDS:\n        acc[k] = max(acc[k], st[k]) if k == \"max_ok_gas\" else acc[k] + st[k]\n\n\ndef run(label, bytecode, vanilla, cfgs, chunk=CHUNK):\n    \"\"\"One strategy vs Vanilla over cfgs. Returns (summary, rows by seed).\"\"\"\n    t = time.time()\n    rows, err = {}, None\n    glob = {(r, c): _zero() for r in ROLES.values() for c in CALLBACKS}\n    for i in range(0, len(cfgs), chunk):\n        part = cfgs[i:i + chunk]\n        amm_sim_rs.gas_stats_reset()\n        try:\n            br = amm_sim_rs.run_batch(bytecode, vanilla, part, N_WORKERS)\n        except Exception as e:  # e.g. afterInitialize OOG/revert propagates as SimulationError\n            err = f\"{type(e).__name__}: {e} (chunk seeds {part[0].seed}..{part[-1].seed})\"\n            br = None\n        for s in amm_sim_rs.gas_stats():  # process-global, includes strategies of failed sims\n            _add(glob[(s[\"role\"], s[\"callback\"])], s)\n        if br is None:\n            break\n        for r in br.results:\n            row = {\"seed\": r.seed, \"dup\": r.seed in rows}\n            for name in ROLES:\n                row[name] = {\"total\": r.edges[name], \"retail\": r.retail_edges[name], \"arb\": r.arb_edges[name],\n                             \"retail_volume_y\": r.retail_volume_y[name], \"stats\": {cb: dict(zip(FIELDS, r.call_stats[name][cb])) for cb in CALLBACKS}}\n            rows[r.seed] = row\n        del br\n        if i % 64 == 0 or i + chunk >= len(cfgs):\n            print(label, \"progress\", len(rows), \"/\", len(cfgs), \"elapsed_s\", round(time.time()-t,2), flush=True)\n    dt = time.time() - t\n    # per-seed counters (exact attribution) summed, cross-checked against the global counters\n    per_seed = {(ROLES[n], cb): _zero() for n in ROLES for cb in CALLBACKS}\n    for row in rows.values():\n        for n in ROLES:\n            for cb in CALLBACKS:\n                _add(per_seed[(ROLES[n], cb)], row[n][\"stats\"][cb])\n    counters_agree = err is None and all(per_seed[k] == glob[k] for k in glob)\n    gas = {}\n    for n, role in ROLES.items():\n        g = {cb: dict(glob[(role, cb)]) for cb in CALLBACKS}\n        gas[n] = {\"by_callback\": g,\n                  \"max_ok\": max(g[cb][\"max_ok_gas\"] for cb in CALLBACKS),\n                  \"oog\": sum(g[cb][\"oog\"] for cb in CALLBACKS),\n                  \"other_errors\": sum(g[cb][k] for cb in CALLBACKS for k in FAIL_KEYS),\n                  \"oog_seeds\": sorted([[s, sum(rows[s][n][\"stats\"][cb][\"oog\"] for cb in CALLBACKS)] for s in rows\n                                       if any(rows[s][n][\"stats\"][cb][\"oog\"] for cb in CALLBACKS)])}\n    want = [c.seed for c in cfgs]\n    complete = (err is None and len(rows) == len(want) and set(rows) == set(want)\n                and not any(r[\"dup\"] for r in rows.values())\n                and all(math.isfinite(r[n][k]) for r in rows.values() for n in ROLES for k in (\"total\", \"retail\", \"arb\")))\n    recon = max((abs(r[n][\"total\"] - r[n][\"retail\"] - r[n][\"arb\"]) for r in rows.values() for n in ROLES), default=None)\n    return {\"label\": label, \"seconds\": round(dt, 3), \"n_requested\": len(want), \"n_returned\": len(rows),\n            \"error\": err, \"complete\": complete, \"counters_per_seed_match_global\": counters_agree,\n            \"gas\": gas, \"max_abs_reconciliation_error\": recon}, rows\n\n\ndef validity(ra, rb, mode):\n    \"\"\"Paired validity. A = candidate (or control run 1), B = reference V2 (or control run 2).\"\"\"\n    reasons = []\n    for r in (ra, rb):\n        if not r[\"complete\"]:\n            reasons.append(f\"{r['label']}: incomplete seeds or error ({r['error']})\")\n        if r[\"gas\"][\"submission\"][\"other_errors\"]:\n            reasons.append(f\"{r['label']}: strategy revert/tx_error/other_halt/bad_return\")\n        if r[\"gas\"][\"normalizer\"][\"oog\"] or r[\"gas\"][\"normalizer\"][\"other_errors\"]:\n            reasons.append(f\"{r['label']}: Vanilla failure\")\n    oa, ob = ra[\"gas\"][\"submission\"][\"oog\"], rb[\"gas\"][\"submission\"][\"oog\"]\n    if mode == \"ref\" and oa > ob:\n        reasons.append(f\"candidate OOG {oa} > V2 OOG {ob}\")\n    if mode == \"control\" and oa != ob:\n        reasons.append(f\"control OOG mismatch {oa} != {ob}\")\n    return not reasons, reasons\n\n\ndef summary(xs):\n    n = len(xs)\n    m = sum(xs) / n\n    sd = math.sqrt(sum((x - m) ** 2 for x in xs) / (n - 1)) if n > 1 else 0.0\n    return {\"n\": n, \"mean\": m, \"se\": sd / math.sqrt(n), \"sd\": sd}\n\n\ndef paired(ea, eb, seeds, label):\n    d = [(s, ea[s][\"submission\"][\"total\"] - eb[s][\"submission\"][\"total\"]) for s in seeds]\n    st = summary([x for _, x in d])\n    worst = sorted(d, key=lambda r: r[1])[:5]\n    return {\"label\": label, \"n\": st[\"n\"], \"mean_diff\": st[\"mean\"], \"se\": st[\"se\"], \"sd\": st[\"sd\"],\n            \"n_nonzero\": sum(1 for _, x in d if x != 0.0), \"max_abs_diff\": max(abs(x) for _, x in d),\n            \"worst\": [{\"seed\": s, \"diff\": x, \"edge_a\": ea[s][\"submission\"][\"total\"],\n                       \"edge_b\": eb[s][\"submission\"][\"total\"]} for s, x in worst]}\n\n\ndef decomposition(rows, seeds):\n    return {n: {k: summary([rows[s][n][k] for s in seeds]) for k in (\"total\", \"retail\", \"arb\")} for n in ROLES}\n\n\ndef main():\n    a = sys.argv[1:]\n    mode, ref = None, None\n    if \"--control\" in a:\n        a.remove(\"--control\"); mode = \"control\"\n    if \"--ref\" in a:\n        i = a.index(\"--ref\"); ref = a[i + 1]; del a[i:i + 2]; mode = \"ref\"\n    if mode is None:\n        sys.exit(\"need --ref V2.sol or --control\")\n    path, s0, s1, out = a[0], int(a[1]), int(a[2]), a[3]\n    src, bc = compile_src(path)\n    vanilla = list(get_vanilla_bytecode_and_abi()[0])\n    cfgs = build_configs(s0, s1)\n    seeds = list(range(s0, s1))\n    ra, ea = run(\"A\", bc, vanilla, cfgs)\n    if mode == \"ref\":\n        src_b, bc_b = compile_src(ref)\n        rb, eb = run(\"B_ref\", bc_b, vanilla, cfgs)\n    else:\n        src_b = src\n        rb, eb = run(\"B_repeat\", bc, vanilla, cfgs)\n    ok, reasons = validity(ra, rb, mode)\n    both = ra[\"complete\"] and rb[\"complete\"]\n    res = {\"mode\": mode, \"sha256_a\": hashlib.sha256(src.encode()).hexdigest(),\n           \"sha256_b\": hashlib.sha256(src_b.encode()).hexdigest(), \"seeds\": [s0, s1], \"valid\": ok,\n           \"invalid_reasons\": reasons, \"runs\": [ra, rb],\n           # computed whenever both runs completed every seed; labelled with valid/invalid_reasons\n           \"paired\": paired(ea, eb, seeds, \"control\" if mode == \"control\" else \"candidate_minus_V2\") if both else {\"n\": 0},\n           \"bit_identical_rows\": both and all(ea[s][\"submission\"][\"total\"] == eb[s][\"submission\"][\"total\"]\n                                              and ea[s][\"normalizer\"][\"total\"] == eb[s][\"normalizer\"][\"total\"] for s in seeds),\n           \"decomposition_a\": decomposition(ea, seeds) if both else None,\n           \"decomposition_b\": decomposition(eb, seeds) if both else None,\n           \"per_seed\": [{\"seed\": s, \"a\": {n: [ea[s][n][k] for k in (\"total\", \"retail\", \"arb\")] for n in ROLES},\n                         \"b\": {n: [eb[s][n][k] for k in (\"total\", \"retail\", \"arb\")] for n in ROLES}}\n                        for s in seeds] if both else []}\n    Path(out).write_text(json.dumps(res, indent=1))\n    print(json.dumps({k: v for k, v in res.items() if k not in (\"runs\", \"per_seed\")}, indent=1)[:4000])\n    for r in (ra, rb):\n        print(r[\"label\"], r[\"seconds\"], r[\"complete\"], r[\"counters_per_seed_match_global\"],\n              {n: (g[\"max_ok\"], g[\"oog\"], g[\"other_errors\"]) for n, g in r[\"gas\"].items()})\n    sys.exit(0 if ok else 2)\n\n\nif __name__ == \"__main__\":\n    main()\n"},{"path":"scratch/experiment.py","content":"import json,sys,time,hashlib\nfrom pathlib import Path\nimport harness as h\nimport numpy as np\n\ndef checked_run(name,start,end):\n    p=Path(f'scratch/run_{name}_{start}_{end}.json')\n    if p.exists(): return json.loads(p.read_text())\n    src,bc=h.compile_src('scratch/'+name+'.sol')\n    van=list(h.get_vanilla_bytecode_and_abi()[0])\n    cfg=h.build_configs(start,end)\n    result,rows=h.run(name,bc,van,cfg,chunk=8)\n    assert result['complete'], result\n    assert result['counters_per_seed_match_global'], result\n    assert result['max_abs_reconciliation_error']<1e-8,result\n    assert result['gas']['submission']['other_errors']==0,result\n    assert result['gas']['normalizer']['other_errors']==0 and result['gas']['normalizer']['oog']==0,result\n    obj={'sha256':hashlib.sha256(src.encode()).hexdigest(),'run':result,'rows':rows}\n    p.write_text(json.dumps(obj))\n    print(name,start,end,'seconds',result['seconds'],'mean',np.mean([r['submission']['total'] for r in rows.values()]),'gas',result['gas']['submission'],flush=True)\n    return obj\n\ndef compare(a,b):\n    ea={int(k):v for k,v in a['rows'].items()};eb={int(k):v for k,v in b['rows'].items()};ss=sorted(ea)\n    assert ss==sorted(eb)\n    ok,why=h.validity(a['run'],b['run'],'ref')\n    d=h.paired(ea,eb,ss,'candidate_minus_V2');d['valid']=ok;d['invalid_reasons']=why\n    def decomp(seeds):\n        out={k:h.summary([ea[s]['submission'][k]-eb[s]['submission'][k] for s in seeds]) for k in ['total','retail','arb']}\n        out['v2_retail_volume_range']=[min(eb[s]['submission']['retail_volume_y'] for s in seeds),max(eb[s]['submission']['retail_volume_y'] for s in seeds)]\n        return out\n    rank=sorted(ss,key=lambda s:(eb[s]['submission']['retail_volume_y'],s))\n    d['actual_edge_deltas']={'overall':decomp(ss),'by_v2_retail_volume_tercile':[decomp(list(chunk)) for chunk in np.array_split(rank,3)]}\n    return d\n\nif __name__=='__main__':\n    mode=sys.argv[1]\n    if mode=='smoke':\n        b=checked_run('V2',10000,10008);a=checked_run('H2_0',10000,10008)\n        Path('scratch/smoke.json').write_text(json.dumps(compare(a,b)))\n    elif mode=='train':\n        b=checked_run('V2',10000,10064);scores={}\n        for n in ['H2_0','H2_1','H2_2','H2_3','H2_4','H2_5','H1']:\n            a=checked_run(n,10000,10064);scores[n]=compare(a,b)\n        Path('scratch/training.json').write_text(json.dumps(scores))\n        top=sorted([n for n in scores if n.startswith('H2') and scores[n]['valid']],key=lambda n:scores[n]['mean_diff'],reverse=True)[:2]\n        assert len(top)==2\n        b=checked_run('V2',12000,12256);validation={}\n        for n in top: validation[n]=compare(checked_run(n,12000,12256),b)\n        Path('scratch/validation.json').write_text(json.dumps(validation))\n        winner=max([n for n in top if validation[n]['valid']],key=lambda n:validation[n]['mean_diff'])\n        src=Path('scratch/'+winner+'.sol').read_text()\n        Path('scratch/frozen.json').write_text(json.dumps({'name':winner,'sha256':hashlib.sha256(src.encode()).hexdigest(),'code':src,'frozen_at_unix':time.time()}))\n        print('FROZEN',winner,flush=True)\n    elif mode=='confirm':\n        f=json.loads(Path('scratch/frozen.json').read_text());name=f['name']\n        assert hashlib.sha256(Path('scratch/'+name+'.sol').read_bytes()).hexdigest()==f['sha256']\n        a=checked_run(name,4260000,4262000);b=checked_run('V2',4260000,4262000)\n        assert a['sha256']==f['sha256']\n        Path('scratch/confirmation.json').write_text(json.dumps(compare(a,b)))\n"},{"path":"scratch/ablate_confirm.py","content":"import json,hashlib,sys\nfrom pathlib import Path\nsys.path.insert(0,str(Path(__file__).resolve().parent))\nimport generate as g\nfrom experiment import checked_run,compare\nf=json.loads(Path('scratch/frozen.json').read_text())\nt=json.loads(Path('scratch/tables.json').read_text())\nd=next(d for d in t['variants'] if d['name']==f['name'])\nactions=d['actions'];offsets=[actions[i][0] for i in [0,3,6]];astep=actions[1][1]\nsrc=g.source(d['h1'],'H1_matched',offsets,astep)\nPath('scratch/H1_matched.sol').write_text(src)\nb=checked_run('V2',10000,10064);a=checked_run('H1_matched',10000,10064)\nx=checked_run(f['name'],10000,10064)\nPath('scratch/matched_ablation.json').write_text(json.dumps({'h1_vs_v2':compare(a,b),'h2_minus_h1':compare(x,a),'h1_sha256':hashlib.sha256(src.encode()).hexdigest(),'matched_to':f['name']}))\nassert hashlib.sha256(Path('scratch/'+f['name']+'.sol').read_bytes()).hexdigest()==f['sha256']\na=checked_run(f['name'],4260000,4262000);b=checked_run('V2',4260000,4262000)\nassert a['sha256']==f['sha256']\nPath('scratch/confirmation.json').write_text(json.dumps(compare(a,b)))\n"}],"provenance":"All performance/gas/decomposition numbers above are local measurements. Sources.json identifies external data/code used; their historical measurements are not substituted for these results.","interpretation":"Reject this implementation: measured retail edge falls 27.903934 while arbitrage edge improves 6.243633, net -21.660301. All three V2 retail-volume terciles lose, with larger losses in the high-volume tercile. H2 selects the same policies as H1 in every bin; these results do not establish a benefit from later recentering and do not rule out a better posterior/transition model or action grid."}
