{
 "credit": "Taylor (gtmcknight), github.com/gtmcknight/amm-sim@4812a6382311624e1506aca131ca35c378f930d9 research/solved: the solved policy, replica, Gaussian filter, fee rule, qm tables and exact-arithmetic arb/retail tell that LEAN, the fast replica and this test are built on.",
 "item": "d-ideas/fill-size",
 "mech": "fill-size: the size of a fill read as retail, relative to a running mean of our retail fills, shifts the next fees on the same side and on the other side",
 "already_present": false,
 "already_present_where": "Not present. Read: leanbase.py, stalerival.py (packed in the LEANBASE artifact efb8e653...), real.py, tellpol.py (Taylor's files). Fill size appears in three places, none of them this mechanism: (1) real.py RealPolicy.on_trade uses the spot move of a step's first own trade, d=|ln(s/sp)|, in a lognormal retail likelihood (LD_M, LD_S) to decide arb or retail; that is a classifier input on first trades only, against a fixed law, and the fee never reads it. (2) real.py has an option fill=1 (order-size law in the same likelihood); LEAN does not set it and the fast replica does not port it. (3) tellpol.py/leanbase.py use the paid amount for the exact grid test and for the chance a retail amount lands on the grid. The fee is a table lookup at (ln s - m, ln rival - m, phase, sd, synced): the fill moves s and the rival estimate, so its size is in the state only through the post-trade spots. No running mean of fills is kept anywhere and no fee term reads a fill size. Tested as stated, no nearest variant needed.",
 "definition": {
  "formula": "At an afterSwap callback that LEAN reads as retail (not the arbitrage; table phase 1 or 2): q = amountY (the callback's wad / 1e18); r = q / M; z = min(r, C) - c0 (form log: clip(ln r, -C, C) - c0); then M <- M + beta*(q - M). Fee of the side just traded (bid when isBuy, else ask) += k_same*1e-4*z; fee of the other side += k_other*1e-4*z. Added to LEAN's two table fees after the rival pull's lookup and before LEAN's own clamp and rounding (wad in [0, 1e17], stepped off Vanilla's gamma); the rounded corrected fees are the fees in force kept by the filter and the tell. Callbacks read as arbitrage: no correction, M unchanged. The correction lasts until the next callback, where fees come from the tables again.",
  "parameters": {
   "k_same_bps": "bps of same-side fee per unit z",
   "k_other_bps": "bps of other-side fee per unit z",
   "beta": "rate of the running mean",
   "C": "clip of r (linear) or of |ln r| (log)",
   "M0": "initial running mean, 11.0 Y",
   "c0": "fixed centre so that mean z is about 0",
   "form": "linear or log"
  },
  "state_kept": "one number: M, the running mean of amountY over fills read as retail (initial 11.0)",
  "inputs": "side (isBuy), amountY, and LEAN's own arb/retail reading; nothing a contract is not handed",
  "calibration": "M0 and c0 are constants fixed before any setting was run, from LEAN alone (no mechanism) on replica seeds 40000..40047: callbacks per run 10915, read as retail 7139; their amountY: mean 11.06, median 5.94, p10 1.44, p90 24.2, p99 82.5; per-run mean 10.29..11.82. Mean of min(r,3) at beta 1/32 = 0.862, of min(r,5) = 0.944, of clip(ln r,-2,2) = -0.540."
 },
 "method": {
  "replica": "fastlean/fastlean.py from artifact a88ff982eba7e078bc6ccb1555b90a211c00c9fd76ec90a62ab13a4074f2c3d0 (source sha256 d9c758bb280fe24e09c039496f84ab0804173ff70cec24c332f8683f28efa582, verified), edited by patch_zb64",
  "parity": "unedited file: mean edge on seeds 64000..64063 and 10000..10063 = 527.8724874783702, the published value; edited file with fs=None and with the mechanism on at k=0: max |edge diff| 0.0 on those 128 seeds",
  "choice": "seeds 64000..65023 in rounds of 128, each setting minus LEAN (a0=0.35 ainf=0.35) on the same seeds; dropped once mean_diff + 3*se < 0; se = sd(paired diffs, n-1)/sqrt(n)",
  "order": "S1-S4 first; S5-S8 chosen after seeing S1-S4; S9-S12 after seeing S5-S8. Every setting started at round 1 on the same seeds. 12 settings in total.",
  "environment": "CPython 3.12.3, numpy 2.2.6, llvmlite 0.44.0, numba 0.61.2, Linux x86_64"
 },
 "settings_tried": [
  {
   "id": "S1",
   "why": "axis probe",
   "parameters": {
    "k_same_bps": 2.0,
    "k_other_bps": 0.0,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 128,
   "mean_diff": -0.579514,
   "se": 0.025395,
   "seeds_positive": 2,
   "dropped_early": true
  },
  {
   "id": "S2",
   "why": "axis probe",
   "parameters": {
    "k_same_bps": -2.0,
    "k_other_bps": 0.0,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 128,
   "mean_diff": -0.242794,
   "se": 0.039419,
   "seeds_positive": 22,
   "dropped_early": true
  },
  {
   "id": "S3",
   "why": "axis probe",
   "parameters": {
    "k_same_bps": 0.0,
    "k_other_bps": 2.0,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 128,
   "mean_diff": -0.228603,
   "se": 0.028363,
   "seeds_positive": 34,
   "dropped_early": true
  },
  {
   "id": "S4",
   "why": "axis probe",
   "parameters": {
    "k_same_bps": 0.0,
    "k_other_bps": -2.0,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 128,
   "mean_diff": -0.413201,
   "se": 0.032231,
   "seeds_positive": 17,
   "dropped_early": true
  },
  {
   "id": "S5",
   "why": "quadratic optimum extrapolated from S1-S4",
   "parameters": {
    "k_same_bps": -0.4,
    "k_other_bps": 0.3,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 256,
   "mean_diff": -0.033869,
   "se": 0.009967,
   "seeds_positive": 111,
   "dropped_early": true
  },
  {
   "id": "S6",
   "why": "twice S5",
   "parameters": {
    "k_same_bps": -0.8,
    "k_other_bps": 0.6,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 128,
   "mean_diff": -0.058419,
   "se": 0.017783,
   "seeds_positive": 46,
   "dropped_early": true
  },
  {
   "id": "S7",
   "why": "S5 direction, log form",
   "parameters": {
    "k_same_bps": -0.35,
    "k_other_bps": 0.25,
    "beta": 0.03125,
    "C": 2.0,
    "M0": 11.0,
    "c0": -0.54,
    "form": "log"
   },
   "n": 256,
   "mean_diff": -0.03283,
   "se": 0.010872,
   "seeds_positive": 96,
   "dropped_early": true
  },
  {
   "id": "S8",
   "why": "S5 direction, clip 5",
   "parameters": {
    "k_same_bps": -0.3,
    "k_other_bps": 0.22,
    "beta": 0.03125,
    "C": 5.0,
    "M0": 11.0,
    "c0": 0.944,
    "form": "linear"
   },
   "n": 512,
   "mean_diff": -0.025386,
   "se": 0.007417,
   "seeds_positive": 221,
   "dropped_early": true
  },
  {
   "id": "S9",
   "why": "small axis probe",
   "parameters": {
    "k_same_bps": -0.25,
    "k_other_bps": 0.0,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 128,
   "mean_diff": -0.033336,
   "se": 0.009872,
   "seeds_positive": 46,
   "dropped_early": true
  },
  {
   "id": "S10",
   "why": "small axis probe",
   "parameters": {
    "k_same_bps": 0.25,
    "k_other_bps": 0.0,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 128,
   "mean_diff": -0.049318,
   "se": 0.010254,
   "seeds_positive": 36,
   "dropped_early": true
  },
  {
   "id": "S11",
   "why": "small axis probe",
   "parameters": {
    "k_same_bps": 0.0,
    "k_other_bps": 0.25,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 1024,
   "mean_diff": -0.00231,
   "se": 0.004762,
   "seeds_positive": 520,
   "dropped_early": false
  },
  {
   "id": "S12",
   "why": "small axis probe",
   "parameters": {
    "k_same_bps": 0.0,
    "k_other_bps": -0.25,
    "beta": 0.03125,
    "C": 3.0,
    "M0": 11.0,
    "c0": 0.862,
    "form": "linear"
   },
   "n": 256,
   "mean_diff": -0.033677,
   "se": 0.009129,
   "seeds_positive": 97,
   "dropped_early": true
  }
 ],
 "chosen": {
  "id": "S11",
  "parameters": {
   "k_same_bps": 0.0,
   "k_other_bps": 0.25,
   "beta": 0.03125,
   "C": 3.0,
   "M0": 11.0,
   "c0": 0.862,
   "form": "linear"
  },
  "why": "the only one of the 12 not dropped; its choice-block mean is not positive (-0.0023 se 0.0048 on 1,024 seeds)",
  "choice_trajectory_n_mean_se": [
   [
    128,
    0.010816,
    0.0122
   ],
   [
    256,
    0.004278,
    0.009178
   ],
   [
    384,
    0.003166,
    0.007401
   ],
   [
    512,
    0.002388,
    0.006432
   ],
   [
    640,
    -0.002812,
    0.005911
   ],
   [
    768,
    -0.003674,
    0.005506
   ],
   [
    896,
    -0.00264,
    0.005123
   ],
   [
    1024,
    -0.00231,
    0.004762
   ]
  ]
 },
 "report": {
  "seeds": "10000..12047, run once",
  "vs_lean": {
   "n": 2048,
   "mean_diff": 0.00227,
   "se": 0.003436,
   "seeds_positive": 1050,
   "base_mean_edge": 536.603391,
   "mech_mean_edge": 536.605661
  },
  "on_age": {
   "n": 2048,
   "mean_diff": 0.000241,
   "se": 0.003519,
   "seeds_positive": 1048,
   "base_mean_edge": 536.6231,
   "mech_mean_edge": 536.623341
  },
  "baselines": "LEAN: a0=0.35 ainf=0.35 tau=2; AGE: a0=0 ainf=0.7 tau=1",
  "by_product_age_minus_lean_replica": {
   "n": 2048,
   "mean_diff": 0.019709,
   "se": 0.004066,
   "seeds_positive": 1140
  }
 },
 "promote": false,
 "reading": [
  "No gain. Chosen setting: +0.0023 se 0.0034 over LEAN and +0.0002 se 0.0035 on AGE (2,048 seeds each); both far below +0.03, and mean + 2*se is below +0.01 in both.",
  "Same side: every sign and size lost. +2 bps/unit -0.58, -2 bps/unit -0.24, +0.25 -0.049 (se 0.010), -0.25 -0.033 (se 0.010), each on 128 seeds.",
  "Other side: -2 and -0.25 lost; +2 lost; +0.25 was neutral (-0.0023 se 0.0048 on 1,024 choice seeds).",
  "The +/-2 probes looked like a gradient (same side -0.084 se 0.011 per bps, other side +0.046 se 0.011, 128 seeds). Its extrapolated optimum (S5) and three variants (S6-S8) all lost, so that asymmetry is not a slope at zero that can be harvested; a skewed z with a non-quadratic loss would produce it.",
  "LEAN's tables are already at their optimum against a fill-size term of this kind, on the same side and the other side, in linear and log form."
 ],
 "checks": {
  "continuity_outside_the_12_settings": {
   "what": "implementation check, not a candidate and not used to choose: near-zero k on seeds 40000..40255 (in neither block), paired with LEAN",
   "rows": {
    "k_same=+1e-4": {
     "n": 256,
     "mean_diff": -0.002854,
     "se": 0.005246,
     "sd_per_seed": 0.083933,
     "seeds_changed": 256,
     "max_abs": 0.546466
    },
    "k_same=-1e-4": {
     "n": 256,
     "mean_diff": -0.005784,
     "se": 0.004968,
     "sd_per_seed": 0.07949,
     "seeds_changed": 256,
     "max_abs": 0.562757
    },
    "k_other=+1e-4": {
     "n": 256,
     "mean_diff": -0.014586,
     "se": 0.006318,
     "sd_per_seed": 0.101093,
     "seeds_changed": 256,
     "max_abs": 0.957647
    },
    "k_same=+1e-9": {
     "n": 256,
     "mean_diff": -0.00758,
     "se": 0.005515,
     "sd_per_seed": 0.088245,
     "seeds_changed": 256,
     "max_abs": 0.71268
    }
   },
   "reading": "A fee change of about 1e-13 (k=1e-9 bps per unit, some 1e5 wad units) changes the edge of every one of the 256 seeds, sd about 0.09 per seed. The replica run is chaotic in the fees: the paired se of ANY mechanism against LEAN has a floor near 0.09/sqrt(n) (0.008 at 128 seeds, 0.003 at 1,024) however small the mechanism. The four means are -0.003 to -0.015 (average -0.0077); they share one LEAN baseline, so they are not independent, and as a group they are about 1.8 se from zero under that shared-baseline reading: no step at k=0 is shown, and the 1,024- and 2,048-seed rows of the chosen setting show none. Likely cause, not verified: the tell reads the low bits of the amounts (a retail amount lands on the arbitrage grid by chance), so any fee change redraws which few retail trades do."
  }
 },
 "contract_cost": {
  "measured": false,
  "storage_words": "0 new words if M is packed into slot 2 of QLX5 (fb | fa<<64 | t<<128 | cnt<<192 with cnt masked to 32 bits and the synced flag in bit 255: bits 224..254 are free, 31 bits, enough for M in 1e-6 Y up to 2,147 Y); 1 new word otherwise",
  "gas": "estimate, not measured: packed, about 200-400 gas on a retail-read callback (unpack, one division, clip, running-mean update, two multiplications, two additions, repack) and about 30 on an arb-read one, so peak about 208,400-208,800 of 245,000. In a slot of its own, about 5,000 more per callback that touches it under EIP-2929 pricing (cold SLOAD 2,100 + SSTORE reset 2,900), if the engine charges cold access per callback: about 213,700. The log form needs the contract's ln (a 14-term series loop), several thousand gas; the chosen form is linear.",
  "runtime_bytes": "estimate: 120-220 bytes, 22,440 -> about 22,600-22,660 of 24,576",
  "characters": "estimate: 300-450 source characters, 47,230 -> about 47,550-47,680 of 50,000",
  "basis": "QLX5 source (sha256 e183e5bc3fb794b7067703b10a7da9920267aa5f4caa968e16ca101b02338f40, 47,230 characters, decoded from code_zb64.QLX5 of artifact 1ef80bfb6d6791b877b1f4048a5e63cf2ae54153e6e8763ae3f8ab13132dfb15) read for its slot layout; that artifact gives peak_gas 208366, runtime_bytes 22440, source_chars 47230. No Solidity was written or compiled here."
 },
 "patch_zb64": "eNq1Vttu20YQfddXTJMHkRGpkLrachXYDiLUQGygiAEjEARhSS6ttXlRSMqq/PU9s0tdmxZB29CAtTszO2d2dm6u65J4H4uySqTIdov2ctNotVoUfJ91eUluzx86vk8t/Tuky8sGvSX3P370+dPV3fXVl0+srIwpTnJRDXpTvzMbkUepQz69ONShsrNwqEtx4FCPYkFWka+ySEYUS1mSyijOi1DaDvWpXDo0oKWaJ7iBQ0P6Y+3QGW3WZC0L+aLyVUmFLGXxImktotLW2ApKGPkMwPg8qsxxn8Ks0hZsslBGbESpEsm0HsGIShbzimGDNcMKoMAko5jBaxHxKBvuW7qpUXpAuXtwaHLt4OBcZQo6hFmQ/iY7Xwwh++A5BAcI/AiVxQ5VYuXAG/XJWOhFo/XvEPz+D0OAvJDwtkoSt1SvcsSYQ8ozdvHzvBSpJCtYlrSUBa0Y6xVuOAcrx8EC/vQokJXAAp5N1JI+YtmhW8D6XQrh2EJSyLsev2lKlkeJyqTgs5TkjzYj7vApleFCZKqEoKpkSpGrIinK9zuJCxhH92KT5AVZj1UaPmfqcVHZTdiYJyrc2CNE3vR8RmMqVlmmskcoFRndUh7zbRnvK4kUAVfVJI3PUSQiEvxbCZW0ia4qEhSKJAlE+HzCHlEBBKPmK72nW4deQUhVZuFqH21ycW3GsvS1/ZF2j5VkBLb7cScCd95Sa6y9SO/I2mp06da+MMYh/mo7SziDnlZlpS1GuliBimiNJ2QkVc6D1cYhmZQShj7brLd+xHfkS7eHn1f95KSfT+uD47SYoRzKBRKmy11OQzBMRLpsN+gye1KVFYpwIcf3xUoi5yIZ1w+gA8sqEXKlcugGMWuPGjpE8S4ev0sqqkUbb2/5ntf2cE0wfGZgpzcd3mim3nZ5O5n2Z3rXM7uB2fWPRQd62/Z2gENNkB3DPqt3nUar5p8bbahQ9RFlbGR5VVull539srtf9vZLbckN5PRuYHZds9NWwCj8cfHtd8+dAbX6vb7T8XXt3X4qpkWejfYE/iKUyUhAwyLPn1GPBPJ2bR/LxAE/YxRccDnllTBXhMIJw/+ivUsiiyjLK44VUQQj+st3kIyjXeyf5gZyMNkY/Rob6To2zjwk6gTZIDfA39O1QX5va9FozzGnXg8DBEpsPsHKPugL6OB2ocGfHZ/UUq/0a80c1apORfn8dzEBcWQkUz/Qka4TVUx0NbV7QN3GFDulxUyEE+f1BikNmn0EYlL21B79lBMO1l06Amv7shPWf8j458uJv1MWfF+ZPsXdLw74AVF95ro5W3FQh5xgpjhhCnuXPzoRgvU+DcR6n8bQus9iaNH5MDg/d86oNex4zvkuHRBqqyIjBuPGZq1thBL6zTuYe6ZTyeWiA16EqpnOI4XK+i0dN7+l82dcqN9+KvOs6XBZGjfxb56qaEsT3thrd/umOdZLdMhxB/Vo1HAZ/82bN/ciSDCPoPqajtM0o4YowgUBToZVXmx4QqjCRbtcoKJ/0NW1Wi0Tye1nHgiwoPlZlm0obLR+islwUTm+yzNp16//v9tu1MYlphDAkLVtChRgJkCb4F+buCebhuPsRwQzH6Dj8VDAowA3RPuw9Z60fu0oHQL3iBG+fVt7LF/KzMrL9pKrw1OOTrvzIVxl23UE/v6jh76luzN3DziEScu6nzbvHpoz9KTJ9Z40udaku86BVAckHbxDf+AMEbzds4Pg5ck25pKtu+Gcx1rr3j5NLqZzXl0c5JShbdPpBpRs2RZFITbWdD8B8vA3Q1eoNks5hoCeEm0TuJOjMyZ5YPGDB4u3yYT9b3MMdgck4e2XiLDdBjFma4O1hTN7GwovgJl6HHwn/7r8zz8izkxFRb0z8aOr+NRnRotqE19sHUgvevwv64oqSkz2FWFCRy+AxHiMwZSbGHZcaHRTMKI/99qwkw04Kk1XDuExvjh7WMbrfG4evkw9ldtmFNJ160/plMos",
 "patch_format": "base64(zlib(unified diff, -p1, fastlean/fastlean.py)); load(amm_dir, a0=, ainf=, tau=, fs=(k_same, k_other, beta, C, M0, c0, form)); fs=None is LEANBASE bit for bit. Chosen: fs=(0.0, 0.25, 0.03125, 3.0, 11.0, 0.862, 0.0)",
 "not_done": [
  "Replica only: no Solidity, no engine run, no gas or size measurement, no gate, nothing submitted. Contract costs are estimates.",
  "The running-mean rate was not varied: all 12 settings use beta = 1/32 (no fixed-mean, 1/8 or 1/128 variant), so whether the mean is running or fixed was not separated. Per-run fill means span only 10.29..11.82, so the two are close.",
  "The log form and clip 5 were each tried once, in the direction the +/-2 probes suggested, and dropped; they were not tried in any other direction.",
  "Not tested: a term that carries past the next callback (through the next arbitrage), a different k per table phase or per synced flag, a nonlinear (for example large-fill-only) response, fill size measured as spot move or as share of reserves instead of amountY.",
  "S9, S10 and S6 were dropped after one round (128 seeds) and S5, S7, S12 after two; their means carry the bias of stopping at a low point. The large loss at +/-0.25 bps on the same side rests on 128 seeds each and its cause was not localized.",
  "Settings S5-S12 were added after looking at earlier ones on the same choice seeds; the choice block is therefore not an unbiased estimate, the report block is.",
  "The continuity check ran four near-zero settings beyond the 12, on seeds 40000..40255; they were not candidates and did not inform the choice (all 12 had been run).",
  "The kit's setup_fast.sh, ammfast, score.py and engine_eval.py were not used: the replica artifact's own run lines were followed. pip is absent on this machine, so the numpy/llvmlite/numba wheels were downloaded from PyPI (sha256 checked against PyPI's index) and unpacked.",
  "leanbase.py and stalerival.py were read from the LEANBASE artifact's packed reference_zb64, which cannot be quoted from its URL; real.py and tellpol.py from Taylor's repository at commit 66604b3426d8e97a73e2c39940b9b4dece51cc73 as the replica's run lines fetch them (34 files, 0 sha256 mismatches)."
 ]
}
