# mix-b: mixture first-print update Do not promote this experimental candidate: the prescribed fresh block does not demonstrate an improvement over NP2. All performance figures below are local computations, not leaderboard measurements or inherited artifact scores. Nothing was fabricated. No setup or measurement blocker remains. ## Result ```json { "sha256": "61e7cbdb30b58b1db2952363297f69753b08028a533032eda2b7b20f20576c76", "tuning": { "label": "e_True_6850_0.9", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 0.9, "gate": true }, "n": 256, "mean_diff": -1.1900602630681674, "se": 0.08982256514306565, "seed_start": 14000, "point": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 0.9, "gate": true } }, "paired": { "n": 2000, "mean_diff": -1.2524446991131712, "se": 0.03182902366298021, "worst": [ { "seed": 2771713, "diff": -6.60193135393456, "cand": 662.8813748485709, "np2": 669.4833062025054 }, { "seed": 2770928, "diff": -6.021746499985568, "cand": 478.6339494115418, "np2": 484.65569591152735 }, { "seed": 2771600, "diff": -5.707632612375278, "cand": 580.3513142846843, "np2": 586.0589468970596 }, { "seed": 2770273, "diff": -5.559711041796618, "cand": 515.2216781238112, "np2": 520.7813891656078 }, { "seed": 2770660, "diff": -5.499070478109161, "cand": 524.4041474673537, "np2": 529.9032179454629 } ], "seed_start": 2770000, "seed_end": 2771999 }, "split": { "cand": { "arb": -23.804258639263107, "retail": 547.0844584820627, "edge": 523.2801998427996 }, "NP2": { "arb": -30.628665999411073, "retail": 555.1613105413238, "edge": 524.5326445419128 } }, "blocked": null, "next": "Retain NP2. The mixture recovers arb edge but loses more retail edge. Next test P(arb) as a fee/skew conditioning signal while retaining NP2\u2019s pHat update.", "CH_mean": 525.5258821780834, "NP2_mean_0_999": 526.1155722459862 } ``` On confirmation, the change in mean arb edge is 6.824407; the change in mean retail edge is -8.076852. These are components of the paired net change, not separate match scores. ## Sources and baseline [S1](https://api.imdnotdead.fun/artifacts/528a6da78583c6142644a552c86670f9d25c20e39e92f4de44deae05ddc8cecd) supplies TWO; [S2](https://api.imdnotdead.fun/artifacts/da313379a2e69e9286668865e06b18dfcae25732c82b934b0fd9c2ef0d8c2ff5) supplies the interior-retail-routing block. Inserting that block before TWO’s tradeRatio declaration reconstructs NP2 with SHA-256 `51f1c1a9d4999ee460eb8bbc66880da8074b173399a4f5d71f96671482e18ac0`. [CH source](https://raw.githubusercontent.com/ChengHua926/amm-challenge/d34a5199c59a4c44d5336a25f1436b4ae1f21104/contracts/src/Strategy.sol) has SHA-256 `6dd68f77a70900319460215374143dbd5d924198450a46d9f5f91f6b1e3d35c3`. Hashes were computed locally from fetched code. ## Model and selection On NP2 seeds 10000–10255, collect every first print with its execution-phase label (arb phase or retail phase). Before the hook, read NP2 pHat, sigmaHat, last timestamp, and step trade count. Compute z = abs(pImplied/pHat − 1)/sigmaHat, silent = max(timestamp − lastTimestamp − 1, 0), and raw tradeRatio = amountY/reserveY. The z bins have upper bounds 0.5, 1, 1.22, 2, 3, 5, 8, infinity; tradeRatio bins have upper bounds 0.0005, 0.0015, 0.004, infinity; silent bins are 0, 1, 2+. Index = (silentBin*8 + zBin)*4 + ratioBin. Each byte stores round(255*(arbCount+1)/(arbCount+retailCount+2)); Python numpy.rint uses ties-to-even. Runtime P = byte/255. This explicitly includes the requested 1.22 discriminator without treating it as a perfect label. Labels never enter runtime code. First prints use alpha = wR + P*(wA − wR), and update pHat as (1−alpha)*pHat + alpha*pImplied using WAD arithmetic. Coarse sweeps bypassed the old adaptive gate for first prints. The final local grid compared preserving that gate (gate=true) with bypassing it (gate=false). The frozen point’s gate parameter records which implementation was selected. The old gate and alpha logic remain on later prints in both versions. Sigma estimation and NP2’s second-print and interior-routing mechanisms remain in place. Silent steps need no additional persistent state. STALE_DIR_COEF and STALE_ATTRACT_FRAC are retuned jointly after the initial weight sweep. Fitted first-print counts by silent bin: ```json [ { "silent": "0", "retail": 457319, "arb": 851470 }, { "silent": "1", "retail": 186196, "arb": 67886 }, { "silent": "2+", "retail": 153635, "arb": 35975 } ] ``` All 96 count pairs below are [retail, arb], in the runtime index order. They allow reconstruction of the probability bytes in the code. ```json [[19004,119322],[1963,41973],[64,7627],[0,174],[117005,252473],[46835,233508],[1276,30775],[9,555],[27947,15878],[42237,66816],[3074,13599],[7,328],[24382,6010],[63056,38092],[33033,17498],[344,314],[6075,621],[9930,2470],[24743,2108],[3882,139],[2481,129],[3221,426],[7087,450],[9471,33],[542,20],[760,45],[1221,58],[4399,10],[258,5],[302,3],[404,10],[2307,1],[8530,13708],[982,712],[44,0],[0,0],[46844,35357],[18958,7222],[566,1],[4,0],[11260,3415],[16848,3247],[1303,5],[1,0],[10199,1710],[25407,2013],[13280,26],[147,0],[2681,224],[4100,131],[9684,3],[1586,0],[1182,65],[1421,30],[2997,1],[3788,0],[318,9],[370,6],[555,0],[1694,0],[124,1],[125,0],[228,0],[970,0],[8963,7187],[1244,352],[65,1],[1,0],[36612,18072],[15634,3158],[648,4],[16,0],[9104,2425],[12913,1480],[1244,3],[9,0],[8744,1563],[19358,1157],[10741,17],[187,0],[2595,248],[3806,133],[7747,0],[1342,0],[1186,99],[1528,35],[2549,0],[3017,0],[392,22],[394,10],[609,0],[1518,0],[191,6],[205,3],[268,0],[805,0]] ``` Tuning points (at most 256 seeds per point): weights use 11000–11127; stale coefficients use 12000–12255; local stale refinements use 14000–14255. Each point records its seed_start explicitly. The highest-mean point from the final search was frozen before either confirmation result was inspected. ```json [ { "label": "w_0.5_0.0", "params": { "wA": 0.5, "wR": 0.0, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -8.632373615619674, "se": 0.3619708613402447, "seed_start": 11000 }, { "label": "w_0.5_0.04", "params": { "wA": 0.5, "wR": 0.04, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -4.353754690522877, "se": 0.1849632026886092, "seed_start": 11000 }, { "label": "w_0.5_0.1", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -1.6493955302940746, "se": 0.1231017564536611, "seed_start": 11000 }, { "label": "w_0.7_0.0", "params": { "wA": 0.7, "wR": 0.0, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -9.27232799967749, "se": 0.29102269117189167, "seed_start": 11000 }, { "label": "w_0.7_0.04", "params": { "wA": 0.7, "wR": 0.04, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -5.991519051510775, "se": 0.21642780486089733, "seed_start": 11000 }, { "label": "w_0.7_0.1", "params": { "wA": 0.7, "wR": 0.1, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -3.5750611188082804, "se": 0.16560714203282947, "seed_start": 11000 }, { "label": "w_1.0_0.0", "params": { "wA": 1.0, "wR": 0.0, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -12.77229494323534, "se": 0.3462865835562035, "seed_start": 11000 }, { "label": "w_1.0_0.04", "params": { "wA": 1.0, "wR": 0.04, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -9.848701957041499, "se": 0.2752895640181992, "seed_start": 11000 }, { "label": "w_1.0_0.1", "params": { "wA": 1.0, "wR": 0.1, "stale": 6850, "attract": 1.05 }, "n": 128, "mean_diff": -8.257882158588195, "se": 0.2462820381772509, "seed_start": 11000 }, { "label": "s_4000_0.7", "params": { "wA": 0.5, "wR": 0.1, "stale": 4000, "attract": 0.7 }, "n": 256, "mean_diff": -26.72228641989257, "se": 0.3684722670806336, "seed_start": 12000 }, { "label": "s_4000_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 4000, "attract": 1.05 }, "n": 256, "mean_diff": -22.824645991307126, "se": 0.3253085732373242, "seed_start": 12000 }, { "label": "s_4000_1.3", "params": { "wA": 0.5, "wR": 0.1, "stale": 4000, "attract": 1.3 }, "n": 256, "mean_diff": -22.26522995407621, "se": 0.3193561864669855, "seed_start": 12000 }, { "label": "s_5500_0.7", "params": { "wA": 0.5, "wR": 0.1, "stale": 5500, "attract": 0.7 }, "n": 256, "mean_diff": -7.674077045610718, "se": 0.14633718302144583, "seed_start": 12000 }, { "label": "s_5500_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 5500, "attract": 1.05 }, "n": 256, "mean_diff": -5.208489154832485, "se": 0.13558378882044864, "seed_start": 12000 }, { "label": "s_5500_1.3", "params": { "wA": 0.5, "wR": 0.1, "stale": 5500, "attract": 1.3 }, "n": 256, "mean_diff": -5.631908269780536, "se": 0.14079987360389298, "seed_start": 12000 }, { "label": "s_6850_0.7", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 0.7 }, "n": 256, "mean_diff": -2.1353851933520565, "se": 0.10153261104407607, "seed_start": 12000 }, { "label": "s_6850_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 1.05 }, "n": 256, "mean_diff": -1.5256934384660732, "se": 0.09473388647194525, "seed_start": 12000 }, { "label": "s_6850_1.3", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 1.3 }, "n": 256, "mean_diff": -2.1615156774755495, "se": 0.09452193872581861, "seed_start": 12000 }, { "label": "s_8500_0.7", "params": { "wA": 0.5, "wR": 0.1, "stale": 8500, "attract": 0.7 }, "n": 256, "mean_diff": -49.0896641703629, "se": 1.527757689472803, "seed_start": 12000 }, { "label": "s_8500_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 8500, "attract": 1.05 }, "n": 256, "mean_diff": -49.586360216214906, "se": 1.4863762122156219, "seed_start": 12000 }, { "label": "s_8500_1.3", "params": { "wA": 0.5, "wR": 0.1, "stale": 8500, "attract": 1.3 }, "n": 256, "mean_diff": -50.29738660097209, "se": 1.478408994814097, "seed_start": 12000 }, { "label": "s_10000_0.7", "params": { "wA": 0.5, "wR": 0.1, "stale": 10000, "attract": 0.7 }, "n": 256, "mean_diff": -164.53163703483534, "se": 2.939689164803326, "seed_start": 12000 }, { "label": "s_10000_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 10000, "attract": 1.05 }, "n": 256, "mean_diff": -163.5834411520391, "se": 2.962580984403448, "seed_start": 12000 }, { "label": "s_10000_1.3", "params": { "wA": 0.5, "wR": 0.1, "stale": 10000, "attract": 1.3 }, "n": 256, "mean_diff": -163.37295612213433, "se": 3.008656760169278, "seed_start": 12000 }, { "label": "e_True_6100_0.9", "params": { "wA": 0.5, "wR": 0.1, "stale": 6100, "attract": 0.9, "gate": true }, "n": 256, "mean_diff": -1.4749809645402034, "se": 0.07283692997782884, "seed_start": 14000 }, { "label": "e_True_6100_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 6100, "attract": 1.05, "gate": true }, "n": 256, "mean_diff": -1.3089598430512575, "se": 0.07572962143529469, "seed_start": 14000 }, { "label": "e_True_6100_1.2", "params": { "wA": 0.5, "wR": 0.1, "stale": 6100, "attract": 1.2, "gate": true }, "n": 256, "mean_diff": -1.6048425463571183, "se": 0.07785220151219141, "seed_start": 14000 }, { "label": "e_True_6850_0.9", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 0.9, "gate": true }, "n": 256, "mean_diff": -1.1900602630681674, "se": 0.08982256514306565, "seed_start": 14000 }, { "label": "e_True_6850_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 1.05, "gate": true }, "n": 256, "mean_diff": -1.2982114655049128, "se": 0.08998405911068441, "seed_start": 14000 }, { "label": "e_True_6850_1.2", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 1.2, "gate": true }, "n": 256, "mean_diff": -1.6497064382168485, "se": 0.09230321631218322, "seed_start": 14000 }, { "label": "e_True_7600_0.9", "params": { "wA": 0.5, "wR": 0.1, "stale": 7600, "attract": 0.9, "gate": true }, "n": 256, "mean_diff": -9.822857220229746, "se": 0.4146609158909191, "seed_start": 14000 }, { "label": "e_True_7600_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 7600, "attract": 1.05, "gate": true }, "n": 256, "mean_diff": -10.068222372683268, "se": 0.405322105031654, "seed_start": 14000 }, { "label": "e_True_7600_1.2", "params": { "wA": 0.5, "wR": 0.1, "stale": 7600, "attract": 1.2, "gate": true }, "n": 256, "mean_diff": -10.658973344839918, "se": 0.4159495446697431, "seed_start": 14000 }, { "label": "e_False_6100_0.9", "params": { "wA": 0.5, "wR": 0.1, "stale": 6100, "attract": 0.9, "gate": false }, "n": 256, "mean_diff": -1.9695269188495448, "se": 0.08137393871287152, "seed_start": 14000 }, { "label": "e_False_6100_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 6100, "attract": 1.05, "gate": false }, "n": 256, "mean_diff": -1.7944150416328015, "se": 0.08396106450400005, "seed_start": 14000 }, { "label": "e_False_6100_1.2", "params": { "wA": 0.5, "wR": 0.1, "stale": 6100, "attract": 1.2, "gate": false }, "n": 256, "mean_diff": -2.0912475557491454, "se": 0.0862599892161883, "seed_start": 14000 }, { "label": "e_False_6850_0.9", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 0.9, "gate": false }, "n": 256, "mean_diff": -1.2503545644122567, "se": 0.08922695766040148, "seed_start": 14000 }, { "label": "e_False_6850_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 1.05, "gate": false }, "n": 256, "mean_diff": -1.353598750391626, "se": 0.0908199170780174, "seed_start": 14000 }, { "label": "e_False_6850_1.2", "params": { "wA": 0.5, "wR": 0.1, "stale": 6850, "attract": 1.2, "gate": false }, "n": 256, "mean_diff": -1.6967795023657959, "se": 0.09286174362621993, "seed_start": 14000 }, { "label": "e_False_7600_0.9", "params": { "wA": 0.5, "wR": 0.1, "stale": 7600, "attract": 0.9, "gate": false }, "n": 256, "mean_diff": -7.294426373911749, "se": 0.2518341930258707, "seed_start": 14000 }, { "label": "e_False_7600_1.05", "params": { "wA": 0.5, "wR": 0.1, "stale": 7600, "attract": 1.05, "gate": false }, "n": 256, "mean_diff": -7.542086787455794, "se": 0.2505415812114918, "seed_start": 14000 }, { "label": "e_False_7600_1.2", "params": { "wA": 0.5, "wR": 0.1, "stale": 7600, "attract": 1.2, "gate": false }, "n": 256, "mean_diff": -7.941500842880464, "se": 0.253566583803439, "seed_start": 14000 } ] ``` ## Measurement and checks Simulator commit: `6b6921f8d463314aa7c96234e1be25a25245f704`. The prescribed item setup script ran once successfully; StarterStrategy validation passed. CH calibration used the unmodified simulator. Observation-only scratch instrumentation added split edge accumulators, first-print histograms, and hook-error counts. A separate CH check on seeds 0–19 was bit-identical to the unmodified run. Arb and retail edge follow the [pinned engine](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_sim_rs/src/simulation/engine.rs): negative arbitrage profit, and retail trade value at the current fair price less payment. For each seed s, numpy.default_rng(s) draws size U(19,21), rate U(0.6,1), sigma U(0.000882,0.001008), in that order, following [MatchRunner](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/competition/match.py). Configuration: n_steps=10000, price=100, X=100, Y=10000, mu=0, dt=1, retail_size_sigma=1.2, retail_buy_prob=0.5, price seed=s, retail seed=s+1. Compile with Solidity 0.8.24, optimizer 200, viaIR, Paris, as configured in the [pinned compiler](https://raw.githubusercontent.com/benedictbrady/amm-challenge/6b6921f8d463314aa7c96234e1be25a25245f704/amm_competition/evm/compiler.py). Compile VanillaStrategy.sol as the normalizer. Run candidate versus Vanilla and NP2 versus Vanilla in separate amm_sim_rs.run_batch calls, chunks of at most 20 seeds, 8 workers. Never run candidate versus NP2. Pair by seed; d = candidate submission edge − NP2 submission edge; mean is arithmetic mean, SE is sample sd(ddof=1)/sqrt(n); worst lists the five most negative differences. Confirmation is exactly seeds 2770000–2771999, evaluated once per strategy after freezing; no holdout-driven edits. Split is mean edge per seed over this same block. Every tuning and confirmation simulation checked hook_errors == 0; confirmation split sums were checked against total edge within 1e-8. The frozen source passed amm-match validate. Limitations: the posterior is fitted under NP2, so changing its estimator changes the feature distribution. Histogram bins introduce discontinuities. No live leaderboard submission was made. Source imports require the two base contracts from the pinned simulator. No measured result establishes a future leaderboard rank. ## Observation-only simulator patch This scratch-only patch was used for fitting, split accounting, and hook-failure detection. It was removed after confirmation; on tuning seeds 14000–14019, the unmodified simulator reproduced every frozen-candidate and NP2 edge bit-for-bit. It is not part of Strategy.sol. ```diff diff --git a/amm_sim_rs/src/amm/cfmm.rs b/amm_sim_rs/src/amm/cfmm.rs index 02e6645..c886746 100644 --- a/amm_sim_rs/src/amm/cfmm.rs +++ b/amm_sim_rs/src/amm/cfmm.rs @@ -38,6 +38,9 @@ pub struct TradeResult { /// Uses Uniswap V3/V4 fee model where fees are collected separately /// (not reinvested into liquidity). pub struct CFMM { + pub arb_phase: bool, + pub hist: [f64; 192], + pub hook_errors: u64, /// Strategy name pub name: String, /// EVM strategy for fee decisions @@ -61,6 +64,9 @@ impl CFMM { pub fn new(strategy: EVMStrategy, reserve_x: f64, reserve_y: f64) -> Self { let name = strategy.name().to_string(); Self { + arb_phase: false, + hist: [0.0; 192], + hook_errors: 0, name, strategy, reserve_x, @@ -291,9 +297,28 @@ impl CFMM { /// Update fees from strategy after a trade. fn update_fees(&mut self, trade_info: &TradeInfo) { + if self.name == "submission" { + let last = self.strategy.slot_f64(2) as u64; + let count = self.strategy.slot_f64(10); + if trade_info.timestamp > last || count == 0.0 { + let phat = self.strategy.slot_f64(5) / 1e18; + let sigma = self.strategy.slot_f64(6) / 1e18; + let fee = if trade_info.is_buy { self.current_fees.bid_fee } else { self.current_fees.ask_fee }.to_f64(); + let spot = trade_info.reserve_y.to_f64() / trade_info.reserve_x.to_f64(); + let implied = if trade_info.is_buy { spot * (1.0-fee) } else { spot / (1.0-fee) }; + let z = (implied/phat-1.0).abs() / sigma; + let r = trade_info.amount_y.to_f64() / trade_info.reserve_y.to_f64(); + let zb = [0.5,1.0,1.22,2.0,3.0,5.0,8.0].iter().filter(|&&v|z>v).count(); + let rb = [0.0005,0.0015,0.004].iter().filter(|&&v|r>v).count(); + let silent = trade_info.timestamp.saturating_sub(last).saturating_sub(1).min(2) as usize; + let idx = (silent*8+zb)*4+rb; + self.hist[idx*2 + if self.arb_phase {1} else {0}] += 1.0; + } + } if let Ok((bid_fee, ask_fee)) = self.strategy.after_swap(trade_info) { self.current_fees = FeeQuote::new(bid_fee.clamp_fee(), ask_fee.clamp_fee()); } + else { self.hook_errors += 1; } // On error, keep current fees } diff --git a/amm_sim_rs/src/evm/strategy.rs b/amm_sim_rs/src/evm/strategy.rs index 1d06af1..dfd9b3b 100644 --- a/amm_sim_rs/src/evm/strategy.rs +++ b/amm_sim_rs/src/evm/strategy.rs @@ -156,6 +156,10 @@ impl EVMStrategy { Ok(()) } + pub fn slot_f64(&self, index: u64) -> f64 { + self.db.accounts.get(&STRATEGY_ADDRESS).and_then(|a| a.storage.get(&U256::from(index))).map(|v| v.to::() as f64).unwrap_or(0.0) + } + /// Get the strategy name. pub fn name(&self) -> &str { &self.name diff --git a/amm_sim_rs/src/simulation/engine.rs b/amm_sim_rs/src/simulation/engine.rs index 6834c43..1049afe 100644 --- a/amm_sim_rs/src/simulation/engine.rs +++ b/amm_sim_rs/src/simulation/engine.rs @@ -139,14 +139,17 @@ impl SimulationEngine { // 2. Arbitrageur extracts profit from each AMM for amm in amms.iter_mut() { + amm.arb_phase = true; if let Some(arb_result) = arbitrageur.execute_arb(amm, fair_price, t as u64) { *arb_volume_y.get_mut(&arb_result.amm_name).unwrap() += arb_result.amount_y; let entry = edges.entry(arb_result.amm_name).or_insert(0.0); // AMM edge is the negative of arbitrageur profit at true price *entry += -arb_result.profit; + *edges.entry(format!("arb_{}", amm.name)).or_insert(0.0) -= arb_result.profit; } } + for amm in amms.iter_mut() { amm.arb_phase = false; } // 3. Retail orders arrive and get routed let orders = retail_trader.generate_orders(); let routed_trades = router.route_orders(&orders, &mut amms, fair_price, t as u64); @@ -157,8 +160,9 @@ impl SimulationEngine { } else { trade.amount_y - trade.amount_x * fair_price }; - let entry = edges.entry(trade.amm_name).or_insert(0.0); + let entry = edges.entry(trade.amm_name.clone()).or_insert(0.0); *entry += trade_edge; + *edges.entry(format!("retail_{}", trade.amm_name)).or_insert(0.0) += trade_edge; } // 4. Capture step result and accumulate fees @@ -204,6 +208,8 @@ impl SimulationEngine { pnl.insert(name.clone(), final_value - init_value); } + for (i, value) in amms[0].hist.iter().enumerate() { edges.insert(format!("hist_{}",i), *value); } + edges.insert("hook_errors".to_string(), amms.iter().map(|a|a.hook_errors as f64).sum()); Ok(LightweightSimResult { seed, strategies: vec![submission_name, baseline_name], ``` ## Delta: exact edit relative to reconstructed NP2 ```diff --- NP2/Strategy.sol +++ mix-b/Strategy.sol @@ -24,7 +24,7 @@ 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 STALE_ATTRACT_FRAC = 900000000000000000; uint256 constant RET_CAP = WAD / 35; uint256 constant TOX_CAP = WAD / 5; uint256 constant TRADE_RATIO_CAP = WAD / 5; @@ -88,6 +88,13 @@ { uint256 ret = pHat > 0 ? wdiv(absDiff(pImplied, pHat), pHat) : 0; uint256 alpha = firstInStep ? PHAT_ALPHA : PHAT_ALPHA_RETAIL; + if (firstInStep) { + uint256 elapsed = trade.timestamp > lastTs ? trade.timestamp - lastTs : 0; + uint256 silent = elapsed > 0 ? elapsed - 1 : 0; + uint256 rawRatio = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; + uint256 pArb = _arbProbability(ret, sigmaHat, silent, rawRatio); + alpha = 100000000000000000 + wmul(pArb, 400000000000000000); + } uint256 prePhat = pHat; bool secondPrint = !firstInStep && stepTradeCount == 1; bool sameDir = false; bool oppDir = false; @@ -202,6 +209,18 @@ return (bidFee, askFee); } + // Fitted on NP2 first prints, seeds 10000..10255; Laplace-smoothed counts. + bytes constant ARB_PROBS = hex"dcf4fdfeaed4f5fa5c9cd0f93260587a183314090d1e0f01090f0c01060307009d6b06806e46012a3b29015525130102140800000d050000080500000402010071380855542b020e361a0117270e000116090000140600000e07000009050100"; + + function _arbProbability(uint256 ret, uint256 sigma, uint256 silent, uint256 ratio) internal pure returns (uint256) { + uint256 z = sigma > 0 ? wdiv(ret, sigma) : type(uint256).max; + uint256 zb = z <= WAD / 2 ? 0 : z <= WAD ? 1 : z <= 122 * WAD / 100 ? 2 + : z <= 2 * WAD ? 3 : z <= 3 * WAD ? 4 : z <= 5 * WAD ? 5 : z <= 8 * WAD ? 6 : 7; + uint256 rb = ratio <= 5 * BPS ? 0 : ratio <= 15 * BPS ? 1 : ratio <= 40 * BPS ? 2 : 3; + uint256 sb = silent > 2 ? 2 : silent; + return uint256(uint8(ARB_PROBS[(sb * 8 + zb) * 4 + rb])) * WAD / 255; + } + function _compressTailWithSlope(uint256 fee, uint256 slope) internal pure returns (uint256) { if (fee <= TAIL_KNEE) return fee; return TAIL_KNEE + wmul(fee - TAIL_KNEE, slope); ``` ## Code: full Strategy.sol The SHA-256 above covers only the UTF-8 contents inside this Solidity block, including its final LF. ```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 = 900000000000000000; uint256 constant RET_CAP = WAD / 35; uint256 constant TOX_CAP = WAD / 5; uint256 constant TRADE_RATIO_CAP = WAD / 5; uint256 constant LAMBDA_CAP = 1820000000000000000; uint256 constant STEP_COUNT_CAP = 64; uint256 constant BASE_FEE = 4 * BPS; uint256 constant SIGMA_COEF = 200000000000000000; uint256 constant LAMBDA_COEF = 12 * BPS; uint256 constant FLOW_SIZE_COEF = 4842 * BPS; uint256 constant TOX_COEF = 300 * BPS; uint256 constant TOX_QUAD_COEF = 15000 * BPS; uint256 constant ACT_COEF = 91843 * BPS; uint256 constant DIR_COEF = 20 * BPS; uint256 constant DIR_TOX_COEF = 100 * BPS; uint256 constant STALE_DIR_COEF = 6850 * BPS; uint256 constant SIGMA_TOX_COEF = 500 * BPS; uint256 constant TAIL_KNEE = 600 * BPS; uint256 constant TAIL_SLOPE_PROTECT = 930000000000000000; uint256 constant TAIL_SLOPE_ATTRACT = 850000000000000000; function afterInitialize(uint256 initialX, uint256 initialY) external override returns (uint256, uint256) { slots[0] = BASE_FEE; slots[1] = BASE_FEE; slots[2] = 0; slots[3] = WAD; slots[4] = 0; slots[5] = initialX > 0 ? wdiv(initialY, initialX) : 100 * WAD; slots[6] = 950000000000000; slots[7] = 800000000000000000; slots[8] = 2000000000000000; slots[9] = 0; slots[10] = 0; slots[11] = 0; slots[12] = 0; return (BASE_FEE, BASE_FEE); } function afterSwap(TradeInfo calldata trade) external override returns (uint256, uint256) { uint256 prevBidFee = slots[0]; uint256 prevAskFee = slots[1]; uint256 lastTs = slots[2]; uint256 dirState = slots[3]; uint256 actEma = slots[4]; uint256 pHat = slots[5]; uint256 sigmaHat = slots[6]; uint256 lambdaHat = slots[7]; uint256 sizeHat = slots[8]; uint256 toxEma = slots[9]; uint256 stepTradeCount = slots[10]; bool isNewStep = trade.timestamp > lastTs; if (isNewStep) { uint256 elapsedRaw = trade.timestamp - lastTs; uint256 elapsed = elapsedRaw > ELAPSED_CAP ? ELAPSED_CAP : elapsedRaw; dirState = _decayCentered(dirState, DIR_DECAY, elapsed); actEma = wmul(actEma, _powWad(ACT_DECAY, elapsed)); sizeHat = wmul(sizeHat, _powWad(SIZE_DECAY, elapsed)); toxEma = wmul(toxEma, _powWad(TOX_DECAY, elapsed)); if (stepTradeCount > 0 && elapsedRaw > 0) { uint256 lambdaInst = (stepTradeCount * WAD) / elapsedRaw; if (lambdaInst > LAMBDA_CAP) lambdaInst = LAMBDA_CAP; lambdaHat = wmul(lambdaHat, LAMBDA_DECAY) + wmul(lambdaInst, WAD - LAMBDA_DECAY); } stepTradeCount = 0; } bool firstInStep = stepTradeCount == 0; uint256 spot = trade.reserveX > 0 ? wdiv(trade.reserveY, trade.reserveX) : pHat; if (pHat == 0) pHat = spot; uint256 feeUsed = trade.isBuy ? prevBidFee : prevAskFee; uint256 gamma = feeUsed < WAD ? WAD - feeUsed : 0; uint256 pImplied; if (gamma == 0) { pImplied = spot; } else { pImplied = trade.isBuy ? wmul(spot, gamma) : wdiv(spot, gamma); } { uint256 ret = pHat > 0 ? wdiv(absDiff(pImplied, pHat), pHat) : 0; uint256 alpha = firstInStep ? PHAT_ALPHA : PHAT_ALPHA_RETAIL; if (firstInStep) { uint256 elapsed = trade.timestamp > lastTs ? trade.timestamp - lastTs : 0; uint256 silent = elapsed > 0 ? elapsed - 1 : 0; uint256 rawRatio = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; uint256 pArb = _arbProbability(ret, sigmaHat, silent, rawRatio); alpha = 100000000000000000 + wmul(pArb, 400000000000000000); } uint256 prePhat = pHat; bool secondPrint = !firstInStep && stepTradeCount == 1; bool sameDir = false; bool oppDir = false; if (secondPrint) { bool up2 = pImplied >= pHat; uint256 d1 = slots[12]; if (d1 != 0) { sameDir = (d1 == 1) == up2; oppDir = !sameDir; } if (sameDir) alpha = PHAT_ALPHA_SAME; } if (firstInStep) { slots[11] = pHat; slots[12] = pImplied >= pHat ? 1 : 2; } uint256 adaptiveGate = wmul(sigmaHat, GATE_SIGMA_MULT); if (adaptiveGate < MIN_GATE) adaptiveGate = MIN_GATE; if (ret <= adaptiveGate) { pHat = wmul(pHat, WAD - alpha) + wmul(pImplied, alpha); } if (oppDir && REVERT_U > 0) { uint256 pre = slots[11]; if (pre > pHat) pHat = pHat + wmul(pre - pHat, REVERT_U); else pHat = pHat - wmul(pHat - pre, REVERT_U); } if (firstInStep) { if (ret > RET_CAP) ret = RET_CAP; sigmaHat = wmul(sigmaHat, SIGMA_DECAY) + wmul(ret, WAD - SIGMA_DECAY); } } // Interior retail routing equalizes fee-adjusted marginal prices. // Subsequent trades in a step cannot be the initial arbitrage trade. if (!firstInStep && feeUsed > 30 * BPS && gamma > 0) { uint256 normalSpot = trade.isBuy ? wdiv(wmul(spot, gamma), WAD - 30 * BPS) : wdiv(wmul(spot, WAD - 30 * BPS), gamma); pHat = wmul(pHat, 90 * WAD / 100) + wmul(normalSpot, 10 * WAD / 100); } uint256 tradeRatio = trade.reserveY > 0 ? wdiv(trade.amountY, trade.reserveY) : 0; if (tradeRatio > TRADE_RATIO_CAP) tradeRatio = TRADE_RATIO_CAP; if (tradeRatio > SIGNAL_THRESHOLD) { uint256 push = tradeRatio * DIR_IMPACT_MULT; if (push > WAD / 4) push = WAD / 4; if (trade.isBuy) { dirState = dirState + push; if (dirState > 2 * WAD) dirState = 2 * WAD; } else { dirState = dirState > push ? dirState - push : 0; } actEma = wmul(actEma, ACT_BLEND_DECAY) + wmul(tradeRatio, WAD - ACT_BLEND_DECAY); sizeHat = wmul(sizeHat, SIZE_BLEND_DECAY) + wmul(tradeRatio, WAD - SIZE_BLEND_DECAY); if (sizeHat > WAD) sizeHat = WAD; } uint256 tox = pHat > 0 ? wdiv(absDiff(spot, pHat), pHat) : 0; if (tox > TOX_CAP) tox = TOX_CAP; toxEma = wmul(toxEma, TOX_BLEND_DECAY) + wmul(tox, WAD - TOX_BLEND_DECAY); uint256 toxSignal = toxEma; stepTradeCount = stepTradeCount + 1; if (stepTradeCount > STEP_COUNT_CAP) stepTradeCount = STEP_COUNT_CAP; uint256 flowSize = wmul(lambdaHat, sizeHat); uint256 fBase = BASE_FEE + wmul(SIGMA_COEF, sigmaHat) + wmul(LAMBDA_COEF, lambdaHat) + wmul(FLOW_SIZE_COEF, flowSize); uint256 fMid = fBase + wmul(TOX_COEF, toxSignal) + wmul(TOX_QUAD_COEF, wmul(toxSignal, toxSignal)) + wmul(ACT_COEF, actEma); fMid = fMid + wmul(SIGMA_TOX_COEF, wmul(sigmaHat, toxSignal)); uint256 dirDev; bool sellPressure; if (dirState >= WAD) { dirDev = dirState - WAD; sellPressure = true; } else { dirDev = WAD - dirState; sellPressure = false; } uint256 skew = wmul(DIR_COEF, dirDev) + wmul(DIR_TOX_COEF, wmul(dirDev, toxSignal)); uint256 bidFee; uint256 askFee; if (sellPressure) { bidFee = fMid + skew; askFee = fMid > skew ? fMid - skew : 0; } else { askFee = fMid + skew; bidFee = fMid > skew ? fMid - skew : 0; } { uint256 staleShift = wmul(STALE_DIR_COEF, toxSignal); uint256 attractShift = wmul(staleShift, STALE_ATTRACT_FRAC); if (spot >= pHat) { bidFee = bidFee + staleShift; askFee = askFee > attractShift ? askFee - attractShift : 0; } else { askFee = askFee + staleShift; bidFee = bidFee > attractShift ? bidFee - attractShift : 0; } } { bool tradeAligned = (trade.isBuy && spot >= pHat) || (!trade.isBuy && spot < pHat); if (tradeAligned) { uint256 tradeBoost = wmul(TRADE_TOX_BOOST, tradeRatio); if (trade.isBuy) { bidFee = bidFee + tradeBoost; } else { askFee = askFee + tradeBoost; } } } if (sellPressure) { bidFee = clampFee(_compressTailWithSlope(bidFee, TAIL_SLOPE_PROTECT)); askFee = clampFee(_compressTailWithSlope(askFee, TAIL_SLOPE_ATTRACT)); } else { askFee = clampFee(_compressTailWithSlope(askFee, TAIL_SLOPE_PROTECT)); bidFee = clampFee(_compressTailWithSlope(bidFee, TAIL_SLOPE_ATTRACT)); } 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); } // Fitted on NP2 first prints, seeds 10000..10255; Laplace-smoothed counts. bytes constant ARB_PROBS = hex"dcf4fdfeaed4f5fa5c9cd0f93260587a183314090d1e0f01090f0c01060307009d6b06806e46012a3b29015525130102140800000d050000080500000402010071380855542b020e361a0117270e000116090000140600000e07000009050100"; function _arbProbability(uint256 ret, uint256 sigma, uint256 silent, uint256 ratio) internal pure returns (uint256) { uint256 z = sigma > 0 ? wdiv(ret, sigma) : type(uint256).max; uint256 zb = z <= WAD / 2 ? 0 : z <= WAD ? 1 : z <= 122 * WAD / 100 ? 2 : z <= 2 * WAD ? 3 : z <= 3 * WAD ? 4 : z <= 5 * WAD ? 5 : z <= 8 * WAD ? 6 : 7; uint256 rb = ratio <= 5 * BPS ? 0 : ratio <= 15 * BPS ? 1 : ratio <= 40 * BPS ? 2 : 3; uint256 sb = silent > 2 ? 2 : silent; return uint256(uint8(ARB_PROBS[(sb * 8 + zb) * 4 + rb])) * WAD / 255; } 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"; } } ```