# delta Replace NP2 PHAT_ALPHA / PHAT_ALPHA_RETAIL / PHAT_ALPHA_SAME and the associated second-print alpha selection with one log-price Kalman filter. Add ARB_R_SCALE, RETAIL_R_SCALE and V_FLOOR; store lnP+128e18 and V in slots 13/14. Initialize both, predict V with sigmaHat squared times uncapped elapsed steps, select measurement noise by first-print/size classification, update lnP with V/(V+R), contract/floor V, and persist both. Add internal range-reduced ln/exp helpers. Synchronize lnP after the existing interior-routing blend. Keep NP2 sigma estimation, gating, routing weight, flow state, fee equations and fee constants unchanged. Slots 11/12 remain initialized but are no longer used by the removed alpha classifier. # 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; // Kalman variances are log-return squared, WAD scaled. uint256 constant ARB_R_SCALE = 16000000000000000000; uint256 constant RETAIL_R_SCALE = 64000000000000000000; uint256 constant V_FLOOR = 10000000000; int256 constant LN2 = 693147180559945309; int256 constant LOG_OFFSET = 128e18; 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; slots[13] = uint256(_lnWad(slots[5]) + LOG_OFFSET); slots[14] = V_FLOOR; 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]; uint256 variance = slots[14]; int256 lnP = int256(slots[13]) - LOG_OFFSET; bool isNewStep = trade.timestamp > lastTs; if (isNewStep) { uint256 elapsedRaw = trade.timestamp - lastTs; variance += wmul(sigmaHat, sigmaHat) * elapsedRaw; 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 impact = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; uint256 halfBand = (prevBidFee + prevAskFee) / 2; uint256 noise; if (firstInStep && impact <= 5e15) { noise = wmul(ARB_R_SCALE, wmul(sigmaHat, sigmaHat)); } else { uint256 width = halfBand + impact; noise = wmul(RETAIL_R_SCALE, wmul(width, width)); } if (variance < V_FLOOR) variance = V_FLOOR; if (noise == 0) noise = 1; uint256 gain = wdiv(variance, variance + noise); uint256 adaptiveGate = wmul(sigmaHat, GATE_SIGMA_MULT); if (adaptiveGate < MIN_GATE) adaptiveGate = MIN_GATE; if (ret <= adaptiveGate && pImplied > 0) { int256 observation = _lnWad(pImplied); lnP += (observation - lnP) * int256(gain) / int256(WAD); pHat = _expWad(lnP); variance = wmul(variance, WAD - gain); if (variance < V_FLOOR) variance = V_FLOOR; } 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); // Keep the latent mean synchronized with NP2's routing observation. lnP = _lnWad(pHat); } 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)); } slots[13] = uint256(lnP + LOG_OFFSET); slots[14] = variance; 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); } // Range reduction plus the atanh series; x must be positive. function _lnWad(uint256 x) internal pure returns (int256 value) { require(x > 0, "log domain"); while (x >= 2 * WAD) { x /= 2; value += LN2; } while (x < WAD) { x *= 2; value -= LN2; } int256 z = int256((x - WAD) * WAD / (x + WAD)); int256 z2 = z * z / int256(WAD); int256 term = z; int256 sum = z; for (uint256 k = 3; k <= 25; k += 2) { term = term * z2 / int256(WAD); sum += term / int256(k); } value += 2 * sum; } function _expWad(int256 x) internal pure returns (uint256) { int256 power = x / LN2; int256 remainder = x % LN2; if (remainder < 0) { remainder += LN2; power -= 1; } int256 term = int256(WAD); int256 sum = term; for (uint256 k = 1; k <= 16; ++k) { term = term * remainder / int256(WAD) / int256(k); sum += term; } if (power >= 0) return uint256(sum) << uint256(power); return uint256(sum) >> uint256(-power); } 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"; } } ``` # sha256 `5a75d3b274c99beadb7396b5a97e75ffe25ea34456c5855aaa425af9a90ec69a` SHA-256 of the exact UTF-8 Solidity text in the code block, including its trailing newlines. The contract imports the pinned simulator’s standard base/interface and introduces no new Solidity dependency. # tuning All scores below are local measurements against Vanilla, not leaderboard submissions. NP2 was reconstructed from [TWO](https://api.imdnotdead.fun/artifacts/528a6da78583c6142644a552c86670f9d25c20e39e92f4de44deae05ddc8cecd) plus the specified [NP routing block](https://api.imdnotdead.fun/artifacts/da313379a2e69e9286668865e06b18dfcae25732c82b934b0fd9c2ef0d8c2ff5). Its full SHA-256 is `51f1c1a9d4999ee460eb8bbc66880da8074b173399a4f5d71f96671482e18ac0`. Baseline reproduction, seeds 0..999 (1,000 seeds each): | Strategy | Mean submission edge | SE | |---|---:|---:| | CH | 525.5258821780834 | 2.789426459025 | | NP2 | 526.1155722459862 | 2.789600417820 | CH source: [pinned Strategy.sol](https://raw.githubusercontent.com/ChengHua926/amm-challenge/d34a5199c59a4c44d5336a25f1436b4ae1f21104/contracts/src/Strategy.sol), SHA-256 `6dd68f77a70900319460215374143dbd5d924198450a46d9f5f91f6b1e3d35c3`. Both checks match the brief’s expected values to its stated precision. Simulator commit `6b6921f8d463314aa7c96234e1be25a25245f704`; the supplied setup script completed once. Compilation uses the simulator’s [Solidity compiler](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/evm/compiler.py), Solidity 0.8.24, optimizer 200, viaIR, Paris. Each candidate and NP2 runs separately against `get_vanilla_bytecode_and_abi()[0]`, using `amm_sim_rs.run_batch(code, vanilla, cfgs, 8)`. Chunks contain 16 seeds (the 1,000-seed baseline checks end with 8). Tuning reference edges are cached and reused across candidates; candidate and NP2 never share a market. Configs follow [MatchRunner._build_configs](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/match.py) and the pinned [settings](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/config.py): `rng=default_rng(s)`, draws size U(19,21), rate U(0.6,1.0), sigma U(0.000882,0.001008), in that order; seed=s, 10,000 steps, price=100, reserves=(100,10000), mu=0, dt=1, retail size sigma=1.2, buy probability=0.5. All fields were checked equal to MatchRunner for seeds 0..999. The [engine](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/simulation/engine.rs) uses price seed s and retail seed s+1. For every point, d is candidate `edges["submission"]` minus the separate NP2 result on the same seed. Mean is arithmetic; SE is `std(d,ddof=1)/sqrt(n)`; worst is `min(d)`. All tuning uses only seeds in 10000..99999. Noise definitions (unscaled quantities): likely arbitrage means first print in step and `amountY/reserveY <= 0.005`; R_arb=A*sigmaHat². Otherwise R_retail=B*((prevBidFee+prevAskFee)/2 + amountY/reserveY)². A and B are the two tunable scales. V is log-return variance; F is its floor. Gain K=V/(V+R); accepted observations update lnP and V=(1-K)V. The existing NP2 rejection gate remains. V starts at F, grows by sigmaHat² per raw elapsed step, and is floored before gain and after contraction. The threshold 0.005 was fixed throughout. Code constants are A*1e18, B*1e18 and F*1e18. | Seeds | A | B | F | n | mean_diff | SE | |---|---:|---:|---:|---:|---:|---:| | 10000..10255 | 8 | 2 | 1e-08 | 256 | -28.214298270 | 0.328868562 | | 10000..10255 | 4 | 2 | 1e-08 | 256 | -19.623683429 | 0.227843734 | | 10000..10255 | 16 | 2 | 1e-08 | 256 | -39.768615118 | 0.416363774 | | 10000..10255 | 8 | 8 | 1e-08 | 256 | -6.941538975 | 0.113773168 | | 10000..10255 | 8 | 0.5 | 1e-08 | 256 | -66.578783148 | 0.704399079 | | 10000..10255 | 8 | 32 | 1e-08 | 256 | -3.523504384 | 0.089572450 | | 10000..10255 | 8 | 128 | 1e-08 | 256 | -4.046123027 | 0.134719426 | | 10000..10255 | 8 | 512 | 1e-08 | 256 | -4.355775236 | 0.142541141 | | 10000..10255 | 8 | 2048 | 1e-08 | 256 | -4.469358851 | 0.142859960 | | 10000..10255 | 4 | 32 | 1e-08 | 256 | -7.144349466 | 0.147370955 | | 10000..10255 | 2 | 32 | 1e-08 | 256 | -14.295375745 | 0.193394610 | | 10000..10255 | 1 | 32 | 1e-08 | 256 | -24.232141659 | 0.259381212 | | 10000..10255 | 4 | 128 | 1e-08 | 256 | -7.929823171 | 0.153671539 | | 10000..10255 | 4 | 32 | 1e-07 | 256 | -7.143388261 | 0.147304735 | | 10000..10255 | 4 | 32 | 1e-06 | 256 | -7.183550568 | 0.150317236 | | 10000..10255 | 8 | 32 | 1e-06 | 256 | -3.534784731 | 0.089783070 | | 10000..10255 | 8 | 32 | 1e-05 | 256 | -4.285784437 | 0.094438564 | | 10000..10255 | 12 | 32 | 1e-08 | 256 | -3.155884457 | 0.124665098 | | 10000..10255 | 16 | 32 | 1e-08 | 256 | -3.695406218 | 0.129599783 | | 10000..10255 | 16 | 64 | 1e-08 | 256 | -3.128841202 | 0.128451724 | | 10000..10255 | 24 | 64 | 1e-08 | 256 | -4.108117810 | 0.129153621 | | 10000..10255 | 24 | 128 | 1e-08 | 256 | -3.898365786 | 0.133480697 | | 10000..10255 | 32 | 128 | 1e-08 | 256 | -4.890655915 | 0.135814179 | Frozen selection: {"arb": 16, "retail": 64, "floor": 1e-08, "sha256": "5a75d3b274c99beadb7396b5a97e75ffe25ea34456c5855aaa425af9a90ec69a"}. Selected using tuning results only; no confirmation seed was evaluated before the source was frozen. Checks on the frozen source: simulator `amm-match validate` passed; 3000 randomized constant-product stress trades all returned bounded fees without a hook failure. Measured pyrevm `EVM.result.gas_used`: initialization 240886, maximum swap 107909; both below the pinned [250,000 gas limits](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/evm/strategy.rs). These maxima cover this stress sample, not every possible input. Math harness: 201 log-spaced prices from exp(-20) to exp(20); max absolute log error 2.4983393132060883e-10, max relative exp(log(p)) error 6.979382627392283e-10. A separate 500-trade shadow calculation matched posterior variance exactly and log mean/price within 1e-10; exercised branch counts {"arb": 93, "retail": 407, "rejected": 16, "floor": 4, "routing": 145}. Storage uses only the standard [32-slot array](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/contracts/src/AMMStrategyBase.sol). # paired ```json { "n": 2000, "mean_diff": -3.221992582358308, "se": 0.03807168294784598, "worst": -8.853330939894477 } ``` Confirmation block: **2580000..2581999**, exactly 2,000 fresh seeds, evaluated once after freeze. Candidate mean edge 521.397457995053; NP2 mean edge 524.619450577411. Worst paired seed: 2581698. No candidate changes or further selection followed confirmation. Do not promote: this Kalman candidate underperforms NP2 on confirmation. The bounded implementation and negative result are supplied for review. # blocked ```json null ``` # next Model the existing interior-routing blend as an additional noisy observation with its own covariance update, so it no longer changes the mean without changing V.