{
"blocked": null,
"replica_source": "\"\"\"Fast numba replica of the AMM fee challenge market (amm_sim_rs @ 6b6921f8).\n\nrun_market(TH, seed, mean_size, rate, sigma, n_steps) -> (edge, retail_edge, arb_edge, retail_volume)\nfor the strategy pool (\"submission\"), which competes against a Vanilla 30 bps pool.\nRNG: numpy's legacy Mersenne Twister inside numba (np.random.seed); the Rust engine uses\nPcg64 + rand_distr, so paths differ and only distributions match.\n\"\"\"\nimport numpy as np\nfrom numba import njit\n\nWAD = 1e18\nMAX_FEE = 0.1\n\n\n\n\n@njit(cache=True, inline='always')\ndef _wad(v):\n    # Wad::from_f64 truncates to an integer number of 1e-18 units; to_f64 divides back.\n    return np.trunc(v * 1e18) / 1e18\n\n\n@njit(cache=True, inline='always')\ndef _clamp(f):\n    f = _wad(f)\n    if f < 0.0:\n        return 0.0\n    if f > MAX_FEE:\n        return MAX_FEE\n    return f\n\n\n# STRATEGY BEGIN\n# Port of CH (ChengHua926/amm-challenge @ d34a5199, contracts/src/Strategy.sol, \"no_cubic\").\n# Integer WAD arithmetic is replaced by float64 (wmul/wdiv truncation not reproduced).\nTH_NAMES = [\"ELAPSED_CAP\", \"SIGNAL_THRESHOLD\", \"DIR_DECAY\", \"ACT_DECAY\", \"SIZE_DECAY\", \"TOX_DECAY\",\n            \"SIGMA_DECAY\", \"LAMBDA_DECAY\", \"SIZE_BLEND_DECAY\", \"TOX_BLEND_DECAY\", \"ACT_BLEND_DECAY\",\n            \"PHAT_ALPHA\", \"PHAT_ALPHA_RETAIL\", \"DIR_IMPACT_MULT\", \"GATE_SIGMA_MULT\", \"MIN_GATE\",\n            \"TRADE_TOX_BOOST\", \"STALE_ATTRACT_FRAC\", \"RET_CAP\", \"TOX_CAP\", \"TRADE_RATIO_CAP\",\n            \"LAMBDA_CAP\", \"STEP_COUNT_CAP\", \"BASE_FEE\", \"SIGMA_COEF\", \"LAMBDA_COEF\", \"FLOW_SIZE_COEF\",\n            \"TOX_COEF\", \"TOX_QUAD_COEF\", \"ACT_COEF\", \"DIR_COEF\", \"DIR_TOX_COEF\", \"STALE_DIR_COEF\",\n            \"SIGMA_TOX_COEF\", \"TAIL_KNEE\", \"TAIL_SLOPE_PROTECT\", \"TAIL_SLOPE_ATTRACT\"]\nB = 1e-4\nTH_CH = np.array([8, 1 / 650, 0.75, 0.53, 0.53, 0.91, 0.69, 0.99, 0.828, 0.051, 0.985, 0.27, 0.06, 4,\n                  7.2, 0.03, 3500 * B, 1.05, 1 / 35, 1 / 5, 1 / 5, 1.82, 64, 4 * B, 0.2, 12 * B, 4842 * B,\n                  300 * B, 15000 * B, 91843 * B, 20 * B, 100 * B, 6850 * B, 500 * B, 600 * B, 0.93, 0.85],\n                 dtype=np.float64)\nI = {n: i for i, n in enumerate(TH_NAMES)}\n\n\n@njit(cache=True)\ndef strat_init(S, TH, x0, y0):\n    base = TH[23]\n    S[0] = base; S[1] = base; S[2] = 0.0; S[3] = 1.0; S[4] = 0.0\n    S[5] = y0 / x0 if x0 > 0 else 100.0\n    S[6] = 0.00095; S[7] = 0.8; S[8] = 0.002; S[9] = 0.0; S[10] = 0.0\n    return base, base\n\n\n@njit(cache=True, inline='always')\ndef _tail(fee, knee, slope):\n    if fee <= knee:\n        return fee\n    return knee + (fee - knee) * slope\n\n\n@njit(cache=True)\ndef strat_after(S, TH, is_buy, amount_x, amount_y, timestamp, reserve_x, reserve_y):\n    prev_bid = S[0]; prev_ask = S[1]; last_ts = S[2]; dir_s = S[3]; act = S[4]; phat = S[5]\n    sig = S[6]; lam = S[7]; size = S[8]; tox_ema = S[9]; cnt = S[10]\n    if timestamp > last_ts:\n        el_raw = timestamp - last_ts\n        el = min(el_raw, TH[0])\n        m = TH[2] ** el\n        if dir_s >= 1.0:\n            dir_s = 1.0 + (dir_s - 1.0) * m\n        else:\n            below = (1.0 - dir_s) * m\n            dir_s = 1.0 - below if below < 1.0 else 0.0\n        act *= TH[3] ** el\n        size *= TH[4] ** el\n        tox_ema *= TH[5] ** el\n        if cnt > 0 and el_raw > 0:\n            li = min(cnt / el_raw, TH[21])\n            lam = lam * TH[7] + li * (1.0 - TH[7])\n        cnt = 0.0\n    first = cnt == 0\n    spot = reserve_y / reserve_x if reserve_x > 0 else phat\n    if phat == 0:\n        phat = spot\n    fee_used = prev_bid if is_buy else prev_ask\n    gamma = 1.0 - fee_used if fee_used < 1.0 else 0.0\n    if gamma == 0:\n        pimp = spot\n    else:\n        pimp = spot * gamma if is_buy else spot / gamma\n    ret = abs(pimp - phat) / phat if phat > 0 else 0.0\n    alpha = TH[11] if first else TH[12]\n    gate = max(sig * TH[14], TH[15])\n    if ret <= gate:\n        phat = phat * (1.0 - alpha) + pimp * alpha\n    if first:\n        ret = min(ret, TH[18])\n        sig = sig * TH[6] + ret * (1.0 - TH[6])\n    tr = amount_y / reserve_y if reserve_y > 0 else 0.0\n    tr = min(tr, TH[20])\n    if tr > TH[1]:\n        push = min(tr * TH[13], 0.25)\n        if is_buy:\n            dir_s = min(dir_s + push, 2.0)\n        else:\n            dir_s = dir_s - push if dir_s > push else 0.0\n        act = act * TH[10] + tr * (1.0 - TH[10])\n        size = min(size * TH[8] + tr * (1.0 - TH[8]), 1.0)\n    tox = abs(spot - phat) / phat if phat > 0 else 0.0\n    tox = min(tox, TH[19])\n    tox_ema = tox_ema * TH[9] + tox * (1.0 - TH[9])\n    ts = tox_ema\n    cnt = min(cnt + 1.0, TH[22])\n    flow = lam * size\n    fbase = TH[23] + TH[24] * sig + TH[25] * lam + TH[26] * flow\n    fmid = fbase + TH[27] * ts + TH[28] * ts * ts + TH[29] * act + TH[33] * sig * ts\n    if dir_s >= 1.0:\n        dev = dir_s - 1.0; sellp = True\n    else:\n        dev = 1.0 - dir_s; sellp = False\n    skew = TH[30] * dev + TH[31] * dev * ts\n    if sellp:\n        bid = fmid + skew; ask = fmid - skew if fmid > skew else 0.0\n    else:\n        ask = fmid + skew; bid = fmid - skew if fmid > skew else 0.0\n    stale = TH[32] * ts\n    attr = stale * TH[17]\n    if spot >= phat:\n        bid += stale; ask = ask - attr if ask > attr else 0.0\n    else:\n        ask += stale; bid = bid - attr if bid > attr else 0.0\n    if (is_buy and spot >= phat) or ((not is_buy) and spot < phat):\n        boost = TH[16] * tr\n        if is_buy:\n            bid += boost\n        else:\n            ask += boost\n    if sellp:\n        bid = min(_tail(bid, TH[34], TH[35]), MAX_FEE); ask = min(_tail(ask, TH[34], TH[36]), MAX_FEE)\n    else:\n        ask = min(_tail(ask, TH[34], TH[35]), MAX_FEE); bid = min(_tail(bid, TH[34], TH[36]), MAX_FEE)\n    S[0] = bid; S[1] = ask; S[2] = timestamp; S[3] = dir_s; S[4] = act; S[5] = phat\n    S[6] = sig; S[7] = lam; S[8] = size; S[9] = tox_ema; S[10] = cnt\n    return bid, ask\n# STRATEGY END\n\n\nVANILLA = 0.003\n\n\n@njit(cache=True)\ndef _notify(S, TH, P, is_buy, ax, ay, t):\n    # P = [rx, ry, bid, ask]; strategy pool only (the Vanilla pool's fees never change)\n    b, a = strat_after(S, TH, is_buy, _wad(ax), _wad(ay), float(t), _wad(P[0]), _wad(P[1]))\n    P[2] = _clamp(b); P[3] = _clamp(a)\n\n\n@njit(cache=True)\ndef _buy_x(P, amount_x):  # AMM buys X (trader sells X); returns y_out (0 = no trade)\n    if amount_x <= 0.0:\n        return 0.0\n    fee = P[2]\n    gamma = min(max(1.0 - fee, 0.0), 1.0)\n    if gamma <= 0.0:\n        return 0.0\n    k = P[0] * P[1]\n    y_out = P[1] - k / (P[0] + amount_x * gamma)\n    if y_out <= 0.0:\n        return 0.0\n    P[0] += amount_x - amount_x * fee\n    P[1] -= y_out\n    return y_out\n\n\n@njit(cache=True)\ndef _sell_x_total_y(P, amount_x):  # quote for AMM selling amount_x; total y in\n    if amount_x <= 0.0 or amount_x >= P[0]:\n        return 0.0\n    k = P[0] * P[1]\n    gamma = min(max(1.0 - P[3], 0.0), 1.0)\n    if gamma <= 0.0:\n        return 0.0\n    net_y = k / (P[0] - amount_x) - P[1]\n    if net_y <= 0.0:\n        return 0.0\n    return net_y / gamma\n\n\n@njit(cache=True)\ndef _x_for_y(P, amount_y):  # AMM sells X for amount_y in; returns x_out\n    if amount_y <= 0.0:\n        return 0.0\n    k = P[0] * P[1]\n    fee = P[3]\n    gamma = min(max(1.0 - fee, 0.0), 1.0)\n    if gamma <= 0.0:\n        return 0.0\n    x_out = P[0] - k / (P[1] + amount_y * gamma)\n    if x_out <= 0.0:\n        return 0.0\n    P[0] -= x_out\n    P[1] += amount_y - amount_y * fee\n    return x_out\n\n\n@njit(cache=True)\ndef _arb(P, p, t, S, TH, is_strat, out):\n    # out[0] += -profit (edge), out[1] += amount_y ; returns nothing\n    rx = P[0]; ry = P[1]\n    spot = ry / rx\n    if spot < p:\n        k = rx * ry\n        gamma = 1.0 - P[3]\n        if gamma <= 0.0 or p <= 0.0:\n            return\n        ax = rx - np.sqrt(k / (gamma * p))\n        if ax <= 0.0:\n            return\n        ax = min(ax, rx * 0.99)\n        ty = _sell_x_total_y(P, ax)\n        if ty <= 0.0:\n            return\n        profit = ax * p - ty\n        if profit <= 0.0:\n            return\n        fee_amt = ty - (k / (rx - ax) - ry)\n        P[0] -= ax\n        P[1] += ty - fee_amt\n        if is_strat:\n            _notify(S, TH, P, False, ax, ty, t)\n        out[0] -= profit\n    elif spot > p:\n        k = rx * ry\n        gamma = 1.0 - P[2]\n        if gamma <= 0.0 or p <= 0.0:\n            return\n        ax = (np.sqrt(k * gamma / p) - rx) / gamma\n        if ax <= 0.0:\n            return\n        # quote\n        g2 = min(max(1.0 - P[2], 0.0), 1.0)\n        if g2 <= 0.0:\n            return\n        y_out = ry - k / (rx + ax * g2)\n        if y_out <= 0.0:\n            return\n        profit = y_out - ax * p\n        if profit <= 0.0:\n            return\n        _buy_x(P, ax)\n        if is_strat:\n            _notify(S, TH, P, True, ax, y_out, t)\n        out[0] -= profit\n\n\n@njit(cache=True)\ndef run_market(TH, seed, mean_size, rate, sigma, n_steps):\n    np.random.seed(seed)\n    S = np.zeros(40)\n    A = np.zeros(4)  # strategy pool\n    Vp = np.zeros(4)  # vanilla pool\n    A[0] = 100.0; A[1] = 10000.0\n    Vp[0] = 100.0; Vp[1] = 10000.0; Vp[2] = VANILLA; Vp[3] = VANILLA\n    b, a = strat_init(S, TH, _wad(100.0), _wad(10000.0))\n    A[2] = _clamp(b); A[3] = _clamp(a)\n    p = 100.0\n    drift = -0.5 * sigma * sigma\n    sz_sig = 1.2\n    mu = np.log(max(mean_size, 0.01)) - 0.5 * sz_sig * sz_sig\n    lam = max(rate, 0.01)\n    arb_s = np.zeros(1); arb_v = np.zeros(1)\n    retail_edge = 0.0\n    retail_vol = 0.0\n    for t in range(n_steps):\n        p *= np.exp(drift + sigma * np.random.standard_normal())\n        _arb(A, p, t, S, TH, True, arb_s)\n        _arb(Vp, p, t, S, TH, False, arb_v)\n        n = np.random.poisson(lam)\n        for _ in range(n):\n            size = np.exp(mu + sz_sig * np.random.standard_normal())\n            buy = np.random.random() < 0.5\n            if buy:\n                g1 = 1.0 - A[3]; g2 = 1.0 - Vp[3]\n                a1 = np.sqrt(A[0] * g1 * A[1]); a2 = np.sqrt(Vp[0] * g2 * Vp[1])\n                if a2 == 0.0:\n                    y1 = size\n                else:\n                    r = a1 / a2\n                    den = g1 + r * g2\n                    y1 = size / 2.0 if den == 0.0 else (r * (Vp[1] + g2 * size) - A[1]) / den\n                    y1 = min(max(y1, 0.0), size)\n                y2 = size - y1\n                if y1 > 1e-4:\n                    xo = _x_for_y(A, y1)\n                    if xo > 0.0:\n                        _notify(S, TH, A, False, xo, y1, t)\n                        retail_vol += y1\n                        retail_edge += y1 - xo * p\n                if y2 > 1e-4:\n                    _x_for_y(Vp, y2)\n            else:\n                tx = size / p\n                g1 = 1.0 - A[2]; g2 = 1.0 - Vp[2]\n                b1 = np.sqrt(A[1] * g1 * A[0]); b2 = np.sqrt(Vp[1] * g2 * Vp[0])\n                if b2 == 0.0:\n                    x1 = tx\n                else:\n                    r = b1 / b2\n                    den = g1 + r * g2\n                    x1 = tx / 2.0 if den == 0.0 else (r * (Vp[0] + g2 * tx) - A[0]) / den\n                    x1 = min(max(x1, 0.0), tx)\n                x2 = tx - x1\n                if x1 > 1e-4:\n                    yo = _buy_x(A, x1)\n                    if yo > 0.0:\n                        _notify(S, TH, A, True, x1, yo, t)\n                        retail_vol += yo\n                        retail_edge += x1 * p - yo\n                if x2 > 1e-4:\n                    _buy_x(Vp, x2)\n    return retail_edge + arb_s[0], retail_edge, arb_s[0], retail_vol\n\n\ndef build_configs(seeds):\n    \"\"\"MatchRunner._build_configs: default_rng(seed) -> mean_size, rate, sigma (in that order).\"\"\"\n    out = []\n    for s in seeds:\n        r = np.random.default_rng(seed=int(s))\n        out.append((int(s), r.uniform(19.0, 21.0), r.uniform(0.6, 1.0), r.uniform(0.000882, 0.001008)))\n    return out\n\n\ndef _worker(args):\n    TH, chunk, n_steps = args\n    return [run_market(TH, s, m, a, g, n_steps) for (s, m, a, g) in chunk]\n\n\ndef run_many(TH, seeds, procs=8, n_steps=10000):\n    \"\"\"Returns float64 array (len(seeds), 4): edge, retail_edge, arb_edge, retail_volume.\"\"\"\n    import multiprocessing as mp\n    cf = build_configs(seeds)\n    TH = np.asarray(TH, dtype=np.float64)\n    if procs <= 1:\n        return np.array(_worker((TH, cf, n_steps)))\n    n = max(1, len(cf) // (procs * 4))\n    chunks = [(TH, cf[i:i + n], n_steps) for i in range(0, len(cf), n)]\n    with mp.get_context('fork').Pool(procs) as pool:\n        res = pool.map(_worker, chunks)\n    return np.array([x for c in res for x in c])\n",
"th_names": [
"ELAPSED_CAP",
"SIGNAL_THRESHOLD",
"DIR_DECAY",
"ACT_DECAY",
"SIZE_DECAY",
"TOX_DECAY",
"SIGMA_DECAY",
"LAMBDA_DECAY",
"SIZE_BLEND_DECAY",
"TOX_BLEND_DECAY",
"ACT_BLEND_DECAY",
"PHAT_ALPHA",
"PHAT_ALPHA_RETAIL",
"DIR_IMPACT_MULT",
"GATE_SIGMA_MULT",
"MIN_GATE",
"TRADE_TOX_BOOST",
"STALE_ATTRACT_FRAC",
"RET_CAP",
"TOX_CAP",
"TRADE_RATIO_CAP",
"LAMBDA_CAP",
"STEP_COUNT_CAP",
"BASE_FEE",
"SIGMA_COEF",
"LAMBDA_COEF",
"FLOW_SIZE_COEF",
"TOX_COEF",
"TOX_QUAD_COEF",
"ACT_COEF",
"DIR_COEF",
"DIR_TOX_COEF",
"STALE_DIR_COEF",
"SIGMA_TOX_COEF",
"TAIL_KNEE",
"TAIL_SLOPE_PROTECT",
"TAIL_SLOPE_ATTRACT"
],
"validation": {
"CH": {
"real": 525.5258821780834,
"replica": 526.9153715025097,
"diff": 1.3894893244263358,
"se_replica_only": 2.7713799632176648,
"replica_retail_arb_vol": [
557.1421667260564,
-30.226795223547327,
76929.74844720587
],
"note": "real mean taken from brief (not re-run: real engine ~0.7 s/market); se is replica-only, real se assumed similar -> two-sample se ~ sqrt(2)x"
},
"flat30_vs_vanilla": {
"real": 347.9224335871958,
"replica": 347.7082543503285,
"diff": -0.21417923686732365,
"se": 4.420454193246556,
"n_real": 256,
"n_replica": 256,
"replica_retail_arb": [
579.441456454673,
-231.73320210434457
]
}
},
"transfer": {
"base+3": {
"real_delta": -0.952649835538776,
"real_se": 0.09576679252215414,
"real_n": 128,
"replica_delta": -0.9244567262250536,
"replica_se": 0.015302396707790341,
"replica_n": 4096,
"replica_retail_delta": -3.9399607361140676,
"replica_arb_delta": 3.0155040098890136,
"replica_delta_first128": -1.0083154369187568
},
"stale_x0.8": {
"real_delta": -7.063215576897394,
"real_se": 0.2248496847366739,
"real_n": 128,
"replica_delta": -6.786955515142919,
"replica_se": 0.03481309849498028,
"replica_n": 4096,
"replica_retail_delta": 9.433848479959815,
"replica_arb_delta": -16.220803995102735,
"replica_delta_first128": -7.0184863700617015
},
"lambda_x1.5": {
"real_delta": -4.057650802713077,
"real_se": 0.21952925302213738,
"real_n": 128,
"replica_delta": -3.9550119797582335,
"replica_se": 0.02830731628274585,
"replica_n": 4096,
"replica_retail_delta": -9.85385001430532,
"replica_arb_delta": 5.898838034547085,
"replica_delta_first128": -4.04566874195167
},
"all3": {
"real_delta": -12.68530434394319,
"real_se": 0.23565258948232612,
"real_n": 128,
"replica_delta": -12.582353409648718,
"replica_se": 0.04115884292516971,
"replica_n": 4096,
"replica_retail_delta": -7.809118121726103,
"replica_arb_delta": -4.773235287922615,
"replica_delta_first128": -12.733629557098995
}
},
"speed": {
"markets_per_s": 164.39586703729447,
"procs": 8,
"n_steps": 10000,
"note": "numba, multiprocessing fork pool; ~175/s on 1000 markets in a separate run; real amm_sim_rs run_batch ~0.7 s/market (~1.4/s) on this machine"
},
"deviations": [
"RNG: numpy legacy MT19937 (np.random.seed(seed)) with one stream for price and retail, instead of Rust Pcg64 seed_from_u64(seed) for price and seed+1 for retail with rand_distr samplers; paths differ, distributions match (GBM exp((mu-sigma^2/2)dt+sigma*sqrt(dt)*Z), Poisson(max(rate,0.01)) arrivals, LogNormal(ln(mean)-0.6^2... i.e. mu=ln(mean)-0.5*1.2^2, sigma 1.2) sizes, side buy if U<0.5).",
"Strategy arithmetic in float64 rather than uint256 WAD wmul/wdiv (CH's per-op truncation to 1e-18 not reproduced); fee outputs and TradeInfo fields are truncated to 1e-18 like Wad::from_f64/to_f64, fees clamped to [0,0.1].",
"No EVM: no gas metering, so OutOfGas halts (which in Rust keep the previous fees) cannot occur; use amm_sim_rs.gas_stats() on the real engine for that.",
"Vanilla pool is hardwired as constant 30 bps and its after_swap is not called (identical result since Vanilla returns constants).",
"Only edges and retail volume are tracked; pnl, per-step capture and average fees are not computed.",
"Engine order kept: per step price step; arb on submission then Vanilla (closed-form optimal size, rx*0.99 cap for buy-arb, profit>0 check); then Poisson retail orders each routed with the two-AMM closed-form split (MIN_AMOUNT 1e-4), buys in Y via quote_x_for_y with ask fee, sells converted size/fair_price X via quote_buy_x with bid fee; fees removed from reserves into separate buckets; strategy after_swap called after each submission trade with post-trade reserves and timestamp=step index."
],
"notes": "Real CH on seeds 0..999 was not re-run (the real engine took >10 min per 1000 markets here); the brief's 525.5258821780834 is used, so the CH diff 1.39 has se ~3.9 (2.77 replica-only x sqrt2). Flat 30 bps (Vanilla vs Vanilla) real and replica both run on seeds 0..255: 347.92 vs 347.71, se 4.42. Transfer: the brief's 'base +3 bps, stale x0.8, lambda x1.5' was run as three single-parameter variants plus all three combined ('all3'); the real deltas are paired vs real CH on seeds 686100..686227; the replica deltas are paired on markets 686100..690195. All four replica deltas fall within about 1.3 real se of the real ones. TH layout: index i = TH_NAMES[i]; WAD constants as fractions (BPS=1e-4). To swap in a strategy, replace the text between the markers; the validation run did this with a flat-fee strategy. run_many returns an array (n,4) of edge, retail_edge, arb_edge, retail_volume. Requires numba (0.68.1 used) and numpy, Python 3.12."
}