# stack-b: first-print mixture and silence uncertainty Frozen candidate: `scratch/Strategy.sol`; SHA-256 `218426004abed3b6cb69f4ef133fea8e4789ddbeae37bd11e94e5ffc5f2a77cb`. Fresh paired confirmation: **+0.477030867 ± 0.017285879 SE** versus NP2, n=2,000, seeds 2850000–2851999. The measured gain supports a modest promotion; it does not reach the requested +5.8/+9.6 targets. ## Method and provenance Simulator: [pinned upstream](https://github.com/benedictbrady/amm-challenge/tree/6b6921f8d463314aa7c96234e1be25a25245f704), Linux x86_64. The supplied item-specific setup script completed once, including release Rust build and StarterStrategy validation. All experimental files and installed tooling were confined to the disposable test/scratch checkout. No new runtime dependency is needed by Strategy.sol beyond the pinned simulator base contracts. NP2 was reconstructed from [TWO](https://api.imdnotdead.fun/artifacts/528a6da78583c6142644a552c86670f9d25c20e39e92f4de44deae05ddc8cecd) and the specified block from [NP](https://api.imdnotdead.fun/artifacts/da313379a2e69e9286668865e06b18dfcae25732c82b934b0fd9c2ef0d8c2ff5). Its full SHA-256 is `51f1c1a9d4999ee460eb8bbc66880da8074b173399a4f5d71f96671482e18ac0`. Local calibration on seeds 0–999 reproduced CH = **525.5258821780834** using [the pinned CH source](https://raw.githubusercontent.com/ChengHua926/amm-challenge/d34a5199c59a4c44d5336a25f1436b4ae1f21104/contracts/src/Strategy.sol), and NP2 = **526.1155722459862**. These are local measurements; no live leaderboard placement is claimed. For each seed s, NumPy default_rng(s) draws size uniform(19,21), rate uniform(.6,1), then sigma uniform(.000882,.001008); build_config receives seed=s and those three values. Solidity compilation uses upstream SolidityCompiler (solc 0.8.24, optimizer 200 runs, viaIR=true, EVM Paris). All remaining settings are the [pinned canonical defaults](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/config.py): 10,000 steps, initial price 100, reserves (100,10000), mu=0, dt=1, retail size sigma=1.2, buy probability=.5. The engine uses price seed s and retail seed s+1. Every score uses run_batch(candidate, compiled VanillaStrategy.sol, configs, 8), and a separate run_batch(NP2, VanillaStrategy.sol, configs, 8). Each call has exactly 20 seeds; candidate and NP2 never face each other. Differences are paired by seed, with sample standard deviation (ddof=1)/sqrt(n). Cached reference results are reused only for identical tuning seeds/configs. The final 2,000-seed block was not used for tuning, calibration, fitting, or any earlier experiment; the frozen candidate was run there once. Tuning screens use 10000–10079 (80 seeds). Expanded final candidates use 10000–10239 (240 unique seeds total, the 80 plus 160). A discarded pilot, hybp1w0.25, additionally received a separate 240-seed diagnostic at 20000–20239: this is a protocol exception to a strict interpretation of the 256-total-seeds-per-code limit (320 unique tuning seeds for that pilot). It is not the delivered code, and its extra block was not used for final confirmation. ## Mechanism and selection Directly replacing the NP2 price estimator with a high-gain arb/retail mixture lost edge in the tested configurations. The delivered adaptation preserves the NP2 slow estimator for its existing volatility, activity, toxicity, and flow fee signals, and maintains a second price estimate for the stale-price directional fee response. The new estimate performs the requested first-print mixture, same-direction-only later updates, and uncertainty diffusion across silence; the stale-price response blends the slow and new estimates. This is an explicit two-estimator adaptation, rather than a successful direct replacement of every NP2 use of pHat. The source below is authoritative for all selected constants. The first-print probability is a heuristic posterior using innovation relative to adaptive sigma, the explicit <=1.22 sigma flag, trade ratio, silence, a 67 bps retail-noise scale, and a rational approximation to a Gaussian density. It is not asserted to be a calibrated probability. The variance state starts at 9.025e11 WAD units, grows by sigmaHat² × elapsed, is capped at 1e14, and contracts after a first-print update. Later fast-estimator observations are never assigned arb probability. The existing NP2 slow estimator continues its original later-print behavior; that retained control path is separate from the new fast estimator. A normalizer-price observation is permitted in the fast estimator only on later prints, when feeUsed>30bps and the inferred move has the first print’s direction. The existing slow-path NP normalizer observation is preserved. The [pinned engine ordering](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/simulation/engine.rs) executes the per-AMM arbitrage opportunity before retail orders, which supports the later-print classification. Diagnostic fitting used NP2 on tuning seeds 30000–30019 only. Offline labels identify an arb print when its fee-adjusted implied price matches the simulator fair price within 1e-9 relative error; that fair price is never supplied to the strategy. Among 143,306 first observations, arb share was 0.518868714499044; the median adaptive sigma was 0.003776483829980932. Retail-first innovation standard deviation relative to the NP2 estimate was 0.006602852186587558; the implied/true-price residual standard deviation was 0.00671409787068762 (67.14 bps). Arb shares after elapsed=1 and elapsed=2 were 0.634 and 0.244 (rounded). These are descriptive local diagnostics, not extra final-candidate validation seeds. Logistic classifiers fitted to these observations were also tested; their results are included below whether or not selected. ## Frozen tuning point ```json { "point": "scratch/combw0.3n1.sol", "start": 10000, "n": 240, "mean_diff": 0.46672470736818095, "se": 0.06313569873211831, "cand_mean": 523.3194602959919, "np2_mean": 522.8527355886238, "frozen_file": "Strategy.sol", "sha256": "218426004abed3b6cb69f4ef133fea8e4789ddbeae37bd11e94e5ffc5f2a77cb", "parameters": { "fast_stale_blend": 0.3, "first_wA": 0.6, "first_wR": 0.08, "arb_flag_sigma_multiple": 1.22, "prior_if_flagged": 0.65, "prior_otherwise": 0.4, "large_trade_ratio_threshold": 0.003, "large_trade_prior_multiplier": 0.6, "silence_elapsed_threshold": 1, "silence_prior_multiplier": 0.8, "later_implied_alpha_same_direction": 0.06, "later_normalizer_alpha_same_direction": 0.1, "STALE_DIR_COEF_bps": 6850, "STALE_ATTRACT_FRAC": 1.05, "variance_initial_WAD": 902500000000, "variance_cap_WAD": 100000000000000, "variance_growth": "wmul(pre-update NP2 sigmaHat, pre-update NP2 sigmaHat) * elapsedRaw" } } ``` ## Paired confirmation and split ```json { "sha256": "218426004abed3b6cb69f4ef133fea8e4789ddbeae37bd11e94e5ffc5f2a77cb", "seed_start": 2850000, "seed_end": 2851999, "paired": { "n": 2000, "mean_diff": 0.4770308666498022, "se": 0.01728587897685193, "worst": [ { "seed": 2850504, "candidate_edge": 628.9585966697831, "np2_edge": 635.9040289125396, "diff": -6.94543224275651 }, { "seed": 2851435, "candidate_edge": 594.7682030465443, "np2_edge": 599.5502974584087, "diff": -4.7820944118644775 }, { "seed": 2851365, "candidate_edge": 663.7090298498929, "np2_edge": 667.4041837989896, "diff": -3.6951539490967207 }, { "seed": 2850472, "candidate_edge": 503.3725344332251, "np2_edge": 506.5377643191041, "diff": -3.1652298858790004 }, { "seed": 2850708, "candidate_edge": 401.4246593085493, "np2_edge": 403.79457811060126, "diff": -2.369918802051984 } ] }, "split": { "candidate": { "edge": 525.418797338803, "arb": -27.937240717456383, "retail": 553.3560380562593 }, "np2": { "edge": 524.9417664721532, "arb": -31.90133527096398, "retail": 556.8431017431172 } }, "blocked": null } ``` Arbitrage edge improved by 3.964094554 per simulation, while retail edge decreased by 3.487063687; the net paired gain is 0.477030867. This variant trades some retail edge for lower arbitrage losses. The split is mean edge per simulation in quote-token units. Arb edge accumulates -arb_result.profit; retail edge uses the exact engine trade-edge formula. Two extra bookkeeping maps were added to the scratch engine and returned as submission_arb/submission_retail; original edge arithmetic and market/strategy execution were unchanged. The first 20 CH edges were checked for exact equality before/after split instrumentation, and split sums were checked to within 1e-8. Confirmation split sums are checked per simulation as well. Volume is not substituted for edge. ## Ablations All ablations use 10000–10079, n=80, and report paired differences versus NP2. These are tuning diagnostics, not independent confirmatory tests. “No first classifier” fixes the fast first-print alpha at NP2 PHAT_ALPHA; “no later fast” disables all later fast-estimator updates; “no variance growth” removes diffusion; “slow only” restores the original NP2 stale estimate and toxicity signal while leaving unused fast bookkeeping in place. | Variant | n | Mean vs NP2 | SE | Full minus ablation | Paired SE | |---|---:|---:|---:|---:|---:| | ablate_no_first_classifier | 80 | +0.119326513 | 0.089829273 | +0.370509721 | 0.086146608 | | ablate_no_later_fast | 80 | +0.207536958 | 0.278574297 | +0.282299276 | 0.156592042 | | ablate_no_variance_growth | 80 | +0.399912026 | 0.143959905 | +0.089924207 | 0.038724370 | | ablate_slow_only_control | 80 | +0.000000000 | 0.000000000 | +0.489836234 | 0.141132102 | Raw-stale-only control (NP2 with only the stale shift using instantaneous toxicity instead of its EMA): n=80, mean_diff=-0.089605156, se=0.116491171. This control changes no price inference. Attribution should use the ablations, not the selected screen alone. ## Tuning ledger All values in this ledger are locally measured paired submission-edge differences versus NP2. Screens can share seeds, so their errors are correlated and selection is biased upward; the fresh block above is the independent check. Point names encode the relevant constants; the exact delivered edit is provided below. No score in this ledger is a live-board score. | Point | Seed start | n | Mean difference | SE | |---|---:|---:|---:|---:| | k0m0a0.5 | 10000 | 80 | -58.656287 | 2.400690 | | k0m0a0.9 | 10000 | 80 | -60.078589 | 2.326219 | | k0m1a0.5 | 10000 | 80 | -32.055540 | 1.558369 | | k0m1a0.9 | 10000 | 80 | -34.116227 | 1.607149 | | k0m2a0.5 | 10000 | 80 | -27.717740 | 1.074561 | | k0m2a0.9 | 10000 | 80 | -28.760364 | 1.135705 | | k1m0a0.5 | 10000 | 80 | -24.490333 | 1.883552 | | k1m0a0.9 | 10000 | 80 | -45.825227 | 1.735217 | | k1m1a0.5 | 10000 | 80 | -14.716025 | 1.496108 | | k1m1a0.9 | 10000 | 80 | -31.308601 | 1.711398 | | k1m2a0.5 | 10000 | 80 | -9.294336 | 0.453804 | | k1m2a0.9 | 10000 | 80 | -22.784880 | 0.758599 | | dynk0m0a0.5 | 10000 | 80 | -10.787711 | 0.368880 | | dynk0m0a0.9 | 10000 | 80 | -30.200498 | 1.686361 | | dynk0m1a0.5 | 10000 | 80 | -8.565358 | 0.339625 | | dynk0m1a0.9 | 10000 | 80 | -25.957574 | 0.656225 | | dynk1m0a0.5 | 10000 | 80 | -2.794848 | 1.460237 | | dynk1m0a0.9 | 10000 | 80 | -5.180857 | 0.364446 | | dynk1m1a0.5 | 10000 | 80 | -0.450829 | 0.156060 | | dynk1m1a0.9 | 10000 | 80 | -4.554055 | 0.239831 | | smoothm0s0.7 | 10000 | 80 | -14.137371 | 1.532868 | | smoothm0s1.22 | 10000 | 80 | -8.326372 | 0.334780 | | smoothm0s2.0 | 10000 | 80 | -7.086341 | 0.317196 | | smoothm1s0.7 | 10000 | 80 | -10.000546 | 0.723923 | | smoothm1s1.22 | 10000 | 80 | -6.903969 | 0.324279 | | smoothm1s2.0 | 10000 | 80 | -6.586288 | 0.296066 | | smoothm2s0.7 | 10000 | 80 | -8.357140 | 0.301104 | | smoothm2s1.22 | 10000 | 80 | -5.684649 | 0.279756 | | smoothm2s2.0 | 10000 | 80 | -5.758270 | 0.271944 | | sizem0s0.0007 | 10000 | 80 | -3.370025 | 0.241618 | | sizem0s0.0015 | 10000 | 80 | -3.784509 | 0.235580 | | sizem0s0.003 | 10000 | 80 | -4.716636 | 0.255103 | | sizem1s0.0007 | 10000 | 80 | -2.545889 | 0.195778 | | sizem1s0.0015 | 10000 | 80 | -3.320441 | 0.234677 | | sizem1s0.003 | 10000 | 80 | -4.343379 | 0.253784 | | hybp0w0.25 | 10000 | 80 | -1.838732 | 0.167760 | | hybp0w0.5 | 10000 | 80 | -7.217999 | 0.243765 | | hybp0w1 | 10000 | 80 | -31.230031 | 0.638282 | | hybp1w0.25 | 10000 | 80 | +0.393205 | 0.128685 | | hybp1w0.5 | 10000 | 80 | -0.217457 | 0.255579 | | hybp1w1 | 10000 | 80 | -3.398148 | 0.244393 | | gridc3000f0.7 | 10000 | 80 | -40.398113 | 0.845615 | | gridc3000f1.05 | 10000 | 80 | -37.102997 | 0.756536 | | gridc3000f1.4 | 10000 | 80 | -35.853952 | 0.726266 | | gridc5000f0.7 | 10000 | 80 | -10.017346 | 0.260476 | | gridc5000f1.05 | 10000 | 80 | -7.327915 | 0.235661 | | gridc5000f1.4 | 10000 | 80 | -7.663094 | 0.235603 | | gridc8500f0.7 | 10000 | 80 | -67.578995 | 4.644819 | | gridc8500f1.05 | 10000 | 80 | -68.198616 | 4.636967 | | gridc8500f1.4 | 10000 | 80 | -69.828630 | 4.638590 | | gridc11000f0.7 | 10000 | 80 | -218.815216 | 6.404126 | | gridc11000f1.05 | 10000 | 80 | -219.514555 | 6.491944 | | gridc11000f1.4 | 10000 | 80 | -219.762355 | 6.293699 | | gridc15000f0.7 | 10000 | 80 | -307.633228 | 6.628084 | | gridc15000f1.05 | 10000 | 80 | -309.155821 | 6.416971 | | gridc15000f1.4 | 10000 | 80 | -307.124918 | 6.622351 | | flagc6850f1.05 | 10000 | 80 | -1.709456 | 0.197804 | | flagc6850f1.3 | 10000 | 80 | -2.470329 | 0.199929 | | flagc8500f1.05 | 10000 | 80 | -72.325445 | 4.553541 | | flagc8500f1.3 | 10000 | 80 | -72.916971 | 4.617260 | | flagc10500f1.05 | 10000 | 80 | -210.384006 | 6.324128 | | flagc10500f1.3 | 10000 | 80 | -208.562414 | 6.273103 | | bayesp0.2c6850 | 10000 | 80 | -4.228133 | 1.438102 | | bayesp0.2c9000 | 10000 | 80 | -97.355095 | 5.406218 | | bayesp0.4c6850 | 10000 | 80 | -1.207711 | 0.193315 | | bayesp0.4c9000 | 10000 | 80 | -136.319596 | 6.815194 | | bayesp0.6c6850 | 10000 | 80 | -1.647507 | 0.202115 | | bayesp0.6c9000 | 10000 | 80 | -169.433970 | 7.396680 | | hybdw0.15c6500 | 10000 | 80 | -0.243940 | 0.061479 | | hybdw0.15c7500 | 10000 | 80 | -5.217208 | 1.315359 | | hybdw0.3c6500 | 10000 | 80 | +0.154691 | 0.066459 | | hybdw0.3c7500 | 10000 | 80 | -5.489648 | 1.325673 | | hybdw0.5c6500 | 10000 | 80 | +0.292255 | 0.090571 | | hybdw0.5c7500 | 10000 | 80 | -6.208635 | 1.378288 | | combw0.15n0 | 10000 | 80 | +0.301128 | 0.130150 | | combw0.15n1 | 10000 | 80 | +0.342603 | 0.126587 | | combw0.3n0 | 10000 | 80 | +0.450737 | 0.142418 | | combw0.3n1 | 10000 | 80 | +0.489836 | 0.141132 | | combw0.5n0 | 10000 | 80 | +0.185428 | 0.161516 | | combw0.5n1 | 10000 | 80 | +0.221261 | 0.160597 | | learnw0.15 | 10000 | 80 | +0.441800 | 0.129365 | | learnw0.3 | 10000 | 80 | +0.214814 | 0.152681 | | learnw0.5 | 10000 | 80 | -0.761679 | 0.192381 | | learnw0.75 | 10000 | 80 | -3.309559 | 0.345739 | | learndirecta0.5 | 10000 | 80 | -10.734892 | 0.379953 | | learndirecta0.75 | 10000 | 80 | -12.902921 | 0.432222 | | localc6500f0.95 | 10000 | 80 | -0.003366 | 0.071965 | | localc6500f1.15 | 10000 | 80 | +0.138564 | 0.069169 | | localc6700f0.95 | 10000 | 80 | +0.340129 | 0.091487 | | localc6700f1.15 | 10000 | 80 | +0.376649 | 0.086954 | | localc6900f0.95 | 10000 | 80 | +0.273029 | 0.238098 | | localc6900f1.15 | 10000 | 80 | +0.288299 | 0.234745 | | localc7100f0.95 | 10000 | 80 | -0.237595 | 0.322410 | | localc7100f1.15 | 10000 | 80 | -0.421271 | 0.315195 | | combw0.3n1 | 10000 | 240 | +0.466725 | 0.063136 | | learnw0.15 | 10000 | 240 | +0.403022 | 0.058282 | | hybp1w0.25 | 20000 | 240 | +0.344032 | 0.061952 | | RawStale | 10000 | 80 | -0.089605 | 0.116491 | Ledger key: k0=hard 1.22 sigma classifier; k1=soft density; m0=no later fast update; m1=same-direction-only; m2=original later alpha rule. Initial k screens use fixed 9.5bps process scale; dyn screens use adaptive sigma. smooth uses a rational fourth-power probability curve. size conditions on trade ratio. hyb uses a separate fast estimator and w is its stale-price blend. hybd adds adaptive diffusion and the explicit flag. comb adds the pre-update sigma snapshot; n1 adds same-direction normalizer observations. grid/flag/bayes are direct-estimator constant/probability screens. learn uses a logistic classifier fitted only on 30000–30019, and variance-dependent observation weights. local retunes the comb stale coefficient c and attraction fraction f. ## Validation, blocked, next blocked: null. Setup, compilation, strategy validation, calibration, paired simulation, and output checks completed. The confirmed gain and split above are the experimental result, not an assurance of leaderboard rank. Large stale-direction coefficients produced severe losses in the direct-replacement screens. The fast variance is a heuristic based on NP2 observation sigma rather than a separately identified GBM variance; rare large downside paths remain and are shown in the worst-five confirmation records. Next: retain the independent confirmation result as the promotion criterion; do not tune against or reuse 2850000–2851999. Further work should separate process volatility from retail observation noise and infer whether a normalizer routing observation is interior before weighting it. Any new candidate needs a different untouched confirmation block. ## Delta: exact unified edit from reconstructed NP2 ```diff --- NP2.sol +++ Strategy.sol @@ -50,5 +50,5 @@ 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] = 902500000000; slots[14] = slots[5]; slots[15] = 0; slots[9] = 0; slots[10] = 0; slots[11] = 0; slots[12] = 0; return (BASE_FEE, BASE_FEE); } @@ -60,4 +60,8 @@ uint256 toxEma = slots[9]; uint256 stepTradeCount = slots[10]; + uint256 sigmaForFast = sigmaHat; + uint256 fastHat = slots[14]; + uint256 uncertainty = slots[13]; + uint256 silent = trade.timestamp > lastTs ? trade.timestamp - lastTs : 0; bool isNewStep = trade.timestamp > lastTs; if (isNewStep) { @@ -73,4 +77,6 @@ lambdaHat = wmul(lambdaHat, LAMBDA_DECAY) + wmul(lambdaInst, WAD - LAMBDA_DECAY); } + uncertainty += wmul(sigmaForFast, sigmaForFast) * elapsedRaw; + if (uncertainty > 100000000000000) uncertainty = 100000000000000; stepTradeCount = 0; } @@ -122,4 +128,43 @@ } + { + uint256 ret = fastHat > 0 ? wdiv(absDiff(pImplied, fastHat), fastHat) : 0; + uint256 alpha; + if (firstInStep) { + uint256 processSigma = sigmaForFast; + uint256 obsVar = uncertainty + wmul(processSigma, processSigma); + uint256 probArb; + uint256 z2 = wdiv(wmul(ret, ret), obsVar); + uint256 density = _powWad(wdiv(WAD, WAD + z2 / 16), 8); + uint256 sigmaObs = sqrt(obsVar * WAD); + uint256 odds = wmul(density, wdiv(6700000000000000, sigmaObs)); + bool arbFlag = ret <= wmul(sigmaForFast, 1220000000000000000); + uint256 prior = arbFlag ? 650000000000000000 : 400000000000000000; + uint256 tr = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; + if (tr > 3000000000000000) prior = wmul(prior, 6 * WAD / 10); + if (silent > 1) prior = wmul(prior, 8 * WAD / 10); + odds = wmul(odds, wdiv(prior, WAD - prior)); + probArb = wdiv(odds, WAD + odds); + alpha = wmul(probArb, 600000000000000000) + wmul(WAD - probArb, 80000000000000000); + slots[15] = pImplied >= fastHat ? 1 : 2; + uncertainty = wmul(uncertainty, WAD - alpha); + } else { + if ((slots[15] == 1) == (pImplied >= fastHat)) alpha = PHAT_ALPHA_RETAIL; + } + uint256 gate = wmul(sigmaHat, GATE_SIGMA_MULT); + if (gate < MIN_GATE) gate = MIN_GATE; + if (ret <= gate) fastHat = wmul(fastHat, WAD - alpha) + wmul(pImplied, alpha); + slots[13] = uncertainty; + } + + if (!firstInStep && feeUsed > 30 * BPS && gamma > 0) { + uint256 normalObs = trade.isBuy + ? wdiv(wmul(spot, gamma), WAD - 30 * BPS) + : wdiv(wmul(spot, WAD - 30 * BPS), gamma); + if ((slots[15] == 1) == (normalObs >= fastHat)) { + fastHat = wmul(fastHat, 9 * WAD / 10) + wmul(normalObs, WAD / 10); + } + } + slots[14] = fastHat; uint256 tradeRatio = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; if (tradeRatio > TRADE_RATIO_CAP) tradeRatio = TRADE_RATIO_CAP; @@ -167,7 +212,10 @@ { - uint256 staleShift = wmul(STALE_DIR_COEF, toxSignal); + uint256 staleHat = wmul(pHat, 700000000000000000) + wmul(fastHat, 300000000000000000); + uint256 staleTox = wdiv(absDiff(spot, staleHat), staleHat); + if (staleTox > TOX_CAP) staleTox = TOX_CAP; + uint256 staleShift = wmul(STALE_DIR_COEF, staleTox); uint256 attractShift = wmul(staleShift, STALE_ATTRACT_FRAC); - if (spot >= pHat) { + if (spot >= staleHat) { bidFee = bidFee + staleShift; askFee = askFee > attractShift ? askFee - attractShift : 0; ``` ## Code: full Strategy.sol The SHA-256 above covers the UTF-8 contents between the following fence lines, including the final LF after the closing Solidity brace. The Markdown fences are excluded. ```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[13] = 902500000000; slots[14] = slots[5]; slots[15] = 0; 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]; uint256 sigmaForFast = sigmaHat; uint256 fastHat = slots[14]; uint256 uncertainty = slots[13]; uint256 silent = trade.timestamp > lastTs ? trade.timestamp - lastTs : 0; 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); } uncertainty += wmul(sigmaForFast, sigmaForFast) * elapsedRaw; if (uncertainty > 100000000000000) uncertainty = 100000000000000; 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 ret = fastHat > 0 ? wdiv(absDiff(pImplied, fastHat), fastHat) : 0; uint256 alpha; if (firstInStep) { uint256 processSigma = sigmaForFast; uint256 obsVar = uncertainty + wmul(processSigma, processSigma); uint256 probArb; uint256 z2 = wdiv(wmul(ret, ret), obsVar); uint256 density = _powWad(wdiv(WAD, WAD + z2 / 16), 8); uint256 sigmaObs = sqrt(obsVar * WAD); uint256 odds = wmul(density, wdiv(6700000000000000, sigmaObs)); bool arbFlag = ret <= wmul(sigmaForFast, 1220000000000000000); uint256 prior = arbFlag ? 650000000000000000 : 400000000000000000; uint256 tr = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; if (tr > 3000000000000000) prior = wmul(prior, 6 * WAD / 10); if (silent > 1) prior = wmul(prior, 8 * WAD / 10); odds = wmul(odds, wdiv(prior, WAD - prior)); probArb = wdiv(odds, WAD + odds); alpha = wmul(probArb, 600000000000000000) + wmul(WAD - probArb, 80000000000000000); slots[15] = pImplied >= fastHat ? 1 : 2; uncertainty = wmul(uncertainty, WAD - alpha); } else { if ((slots[15] == 1) == (pImplied >= fastHat)) alpha = PHAT_ALPHA_RETAIL; } uint256 gate = wmul(sigmaHat, GATE_SIGMA_MULT); if (gate < MIN_GATE) gate = MIN_GATE; if (ret <= gate) fastHat = wmul(fastHat, WAD - alpha) + wmul(pImplied, alpha); slots[13] = uncertainty; } if (!firstInStep && feeUsed > 30 * BPS && gamma > 0) { uint256 normalObs = trade.isBuy ? wdiv(wmul(spot, gamma), WAD - 30 * BPS) : wdiv(wmul(spot, WAD - 30 * BPS), gamma); if ((slots[15] == 1) == (normalObs >= fastHat)) { fastHat = wmul(fastHat, 9 * WAD / 10) + wmul(normalObs, WAD / 10); } } slots[14] = fastHat; 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 staleHat = wmul(pHat, 700000000000000000) + wmul(fastHat, 300000000000000000); uint256 staleTox = wdiv(absDiff(spot, staleHat), staleHat); if (staleTox > TOX_CAP) staleTox = TOX_CAP; uint256 staleShift = wmul(STALE_DIR_COEF, staleTox); uint256 attractShift = wmul(staleShift, STALE_ATTRACT_FRAC); if (spot >= staleHat) { 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[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"; } } ```