{"approach":"Online EWMA of squared relative fair-price moves inferred from the first callback at each timestamp; clipped linear 34-50 bps volatility map with capped 2 bps reserve-inventory skew.","tried":["fixed 42 bps","symmetric volatility map","timestamp-gated volatility map with reserve skew"],"next":["Compare EWMA rates 1/8 and 1/32 on fresh paired seeds.","Replace the hard 1% gate with a trade-size/price-consistency classifier.","Sweep volatility-map endpoints and 0-2 bps inventory skew jointly."],"code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport {AMMStrategyBase} from \"./AMMStrategyBase.sol\";\nimport {IAMMStrategy, TradeInfo} from \"./IAMMStrategy.sol\";\n\n/// @notice A volatility-adaptive fee with a deliberately small inventory skew.\n///\n/// The first trade observed at a new timestamp is normally the arbitrage\n/// trade. At the edge of the fee band its post-trade spot and the fee quote\n/// reveal the fair price. The EWMA below is intentionally slow: a retail\n/// trade at a timestamp without an arbitrage trade cannot move the estimate\n/// very much, while the fixed per-simulation sigma is accumulated over many\n/// timestamps.\ncontract Strategy is AMMStrategyBase {\n    // variance is stored as (relative_price_change^2 / WAD).\n    uint256 private constant LOW_VARIANCE = 777_924_000_000;\n    uint256 private constant HIGH_VARIANCE = 1_016_064_000_000;\n    uint256 private constant BASE_LOW = 34 * BPS;\n    uint256 private constant BASE_HIGH = 50 * BPS;\n    uint256 private constant INITIAL_FEE = 42 * BPS;\n    uint256 private constant MAX_SKEW = 2 * BPS;\n\n    function afterInitialize(uint256 initialX, uint256 initialY) external override returns (uint256 bidFee, uint256 askFee) {\n        slots[0] = initialX;\n        slots[1] = initialY;\n        slots[2] = INITIAL_FEE;\n        slots[3] = INITIAL_FEE;\n        slots[4] = 0;\n        slots[5] = 0;\n        slots[6] = type(uint256).max;\n        slots[7] = 0;\n        return (INITIAL_FEE, INITIAL_FEE);\n    }\n\n    function afterSwap(TradeInfo calldata trade) external override returns (uint256 bidFee, uint256 askFee) {\n        uint256 bid = slots[2];\n        uint256 ask = slots[3];\n\n        if (trade.timestamp != slots[6]) {\n            slots[6] = trade.timestamp;\n            uint256 fair;\n            if (trade.isBuy) {\n                fair = (trade.reserveY * (WAD - bid)) / trade.reserveX;\n            } else {\n                fair = (trade.reserveY * WAD) / (trade.reserveX * (WAD - ask));\n            }\n\n            uint256 previous = slots[4];\n            if (previous != 0 && fair != 0) {\n                uint256 delta = absDiff(fair, previous);\n                if (delta <= previous / 100) {\n                    uint256 relative = (delta * WAD) / previous;\n                    uint256 observation = (relative * relative) / WAD;\n                    uint256 variance = slots[5];\n                    if (variance == 0) variance = observation;\n                    else variance = (variance * 15 + observation) / 16;\n                    slots[5] = variance;\n                    slots[7] += 1;\n                }\n            }\n            if (fair != 0) slots[4] = fair;\n        }\n\n        uint256 varianceNow = slots[5];\n        uint256 base;\n        if (varianceNow <= LOW_VARIANCE) base = BASE_LOW;\n        else if (varianceNow >= HIGH_VARIANCE) base = BASE_HIGH;\n        else base = BASE_LOW + ((varianceNow - LOW_VARIANCE) * (BASE_HIGH - BASE_LOW)) / (HIGH_VARIANCE - LOW_VARIANCE);\n\n        uint256 lhs = trade.reserveX * slots[1];\n        uint256 rhs = trade.reserveY * slots[0];\n        if (lhs > rhs) {\n            bid = base + MAX_SKEW;\n            ask = base > MAX_SKEW ? base - MAX_SKEW : 0;\n        } else if (rhs > lhs) {\n            bid = base > MAX_SKEW ? base - MAX_SKEW : 0;\n            ask = base + MAX_SKEW;\n        } else {\n            bid = base;\n            ask = base;\n        }\n\n        slots[2] = clampFee(bid);\n        slots[3] = clampFee(ask);\n        return (slots[2], slots[3]);\n    }\n\n    function getName() external pure override returns (string memory) {\n        return \"Volatility Estimate\";\n    }\n}\n","code_sha256":"9e983e734f1acab0ecca26f453f8f34559a828e39f5f9899db49424b3844b343","score":{"n":0,"mean_edge":null,"note":"Not run: Python/pip and the Rust simulator were unavailable in the checkout."},"paired":{"reference":"contracts/src/StarterStrategy.sol","seed_start":100000,"n":0,"mean_diff":null,"se":null,"worst":[],"note":"Not run: no simulator execution; no score or paired gain claimed."},"seconds_per_simulation":null,"validate_ok":true,"citations":["https://github.com/benedictbrady/amm-challenge/tree/6b6921f8d463314aa7c96234e1be25a25245f704","https://www.paradigm.xyz/puzzles/amm"],"checks":["forge build --root test/scratch/compile --contracts src/Strategy.sol: succeeded; Foundry emitted one non-fatal divide-before-multiply precision warning at Strategy.sol:70.","Pinned SolidityValidator: valid=true, errors=[], warnings=[].","git diff --check: passed."],"changed":["candidates/vol-estimate/Strategy.sol","candidates/vol-estimate/NOTES.md","candidates/vol-estimate/result.json"]}
