mirror of
https://github.com/jaxperro/winning-wallet-finder.git
synced 2026-07-27 15:57:47 +00:00
band-guarded mirror exits (#21 successor): mirror sells >=90c only, sub-band sells ignored+ledgered; sell-band study slice; backtest parity
Sell-band evidence: every band <90c LOSES by mirroring (<30c +$79/sell given up, 50-70c +$7.29); >=90c SAVES (-$0.85/sell, n=68). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+91
-84
@@ -1,89 +1,96 @@
|
||||
{
|
||||
"mode": "live",
|
||||
"_comment": "Live-test config, SECRET-FREE and committed. The Fly live worker (wwf-copybot-live, COPYBOT_ROLE=live) reads THIS file and takes secrets from env (LIVE_PRIVATE_KEY/LIVE_FUNDER_ADDRESS + LIVE_CONFIRM to arm). Mac use: cp -> config.live.json and fill live.*. RULES: bankroll_usd MUST equal actual equity at rebase time (the 1.4 cash-anchor checks state cash vs chain). 2026-07-10 USER decision: hard caps retired \u2014 sizing is paper-parity 4%-of-equity (floored at the venue $1 min order); follow set mirrors copybot.paper.json (Set E) so paper and live books stay comparable.",
|
||||
"bankroll_usd": 66.42,
|
||||
"bankroll_pct": 0.04,
|
||||
"price_guard_abs": 0.05,
|
||||
"fak_retry_s": 10,
|
||||
"fak_retry_niche_s": {"crypto": 4, "esports": 10, "sports": 25,
|
||||
"geo": 25, "politics": 25, "other": 25},
|
||||
"taker_fee_rate": 0.03,
|
||||
"discord_webhook": "",
|
||||
"follow": {
|
||||
"buy_only": true,
|
||||
"entry_mode": "maker",
|
||||
"exit_mode": "hold",
|
||||
"min_their_usd": 25.0,
|
||||
"min_entry": 0.0,
|
||||
"max_entry": 0.95,
|
||||
"class_pct": {
|
||||
"volume": 0.04,
|
||||
"whale": 0.12
|
||||
}
|
||||
"mode": "live",
|
||||
"_comment": "Live-test config, SECRET-FREE and committed. The Fly live worker (wwf-copybot-live, COPYBOT_ROLE=live) reads THIS file and takes secrets from env (LIVE_PRIVATE_KEY/LIVE_FUNDER_ADDRESS + LIVE_CONFIRM to arm). Mac use: cp -> config.live.json and fill live.*. RULES: bankroll_usd MUST equal actual equity at rebase time (the 1.4 cash-anchor checks state cash vs chain). 2026-07-10 USER decision: hard caps retired \u2014 sizing is paper-parity 4%-of-equity (floored at the venue $1 min order); follow set mirrors copybot.paper.json (Set E) so paper and live books stay comparable.",
|
||||
"bankroll_usd": 66.42,
|
||||
"bankroll_pct": 0.04,
|
||||
"price_guard_abs": 0.05,
|
||||
"fak_retry_s": 10,
|
||||
"fak_retry_niche_s": {
|
||||
"crypto": 4,
|
||||
"esports": 10,
|
||||
"sports": 25,
|
||||
"geo": 25,
|
||||
"politics": 25,
|
||||
"other": 25
|
||||
},
|
||||
"taker_fee_rate": 0.03,
|
||||
"discord_webhook": "",
|
||||
"follow": {
|
||||
"buy_only": true,
|
||||
"entry_mode": "maker",
|
||||
"exit_mode": "mirror",
|
||||
"min_their_usd": 25.0,
|
||||
"min_entry": 0.0,
|
||||
"max_entry": 0.95,
|
||||
"class_pct": {
|
||||
"volume": 0.04,
|
||||
"whale": 0.12
|
||||
},
|
||||
"risk": {
|
||||
"max_trade_usd": 1000000.0,
|
||||
"max_position_usd": 1000000.0,
|
||||
"daily_spend_cap_usd": 1000000.0,
|
||||
"max_total_exposure_usd": 1000000.0,
|
||||
"max_open_positions": 1000,
|
||||
"max_per_event": 0,
|
||||
"min_price": 0.0,
|
||||
"max_price": 0.99,
|
||||
"min_order_usd": 1.0
|
||||
"mirror_sell_min_p": 0.9
|
||||
},
|
||||
"risk": {
|
||||
"max_trade_usd": 1000000.0,
|
||||
"max_position_usd": 1000000.0,
|
||||
"daily_spend_cap_usd": 1000000.0,
|
||||
"max_total_exposure_usd": 1000000.0,
|
||||
"max_open_positions": 1000,
|
||||
"max_per_event": 0,
|
||||
"min_price": 0.0,
|
||||
"max_price": 0.99,
|
||||
"min_order_usd": 1.0
|
||||
},
|
||||
"live": {
|
||||
"private_key": "",
|
||||
"funder_address": "",
|
||||
"signature_type": 1,
|
||||
"order_type": "FAK",
|
||||
"auto_redeem": false,
|
||||
"rpc_url": ""
|
||||
},
|
||||
"wallets": [
|
||||
{
|
||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
||||
"name": "Kruto2027",
|
||||
"class": "volume",
|
||||
"floor": 80.0
|
||||
},
|
||||
"live": {
|
||||
"private_key": "",
|
||||
"funder_address": "",
|
||||
"signature_type": 1,
|
||||
"order_type": "FAK",
|
||||
"auto_redeem": false,
|
||||
"rpc_url": ""
|
||||
{
|
||||
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
||||
"name": "0xbadaf319",
|
||||
"class": "volume",
|
||||
"floor": 41.21
|
||||
},
|
||||
"wallets": [
|
||||
{
|
||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
||||
"name": "Kruto2027",
|
||||
"class": "volume",
|
||||
"floor": 80.0
|
||||
},
|
||||
{
|
||||
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
||||
"name": "0xbadaf319",
|
||||
"class": "volume",
|
||||
"floor": 41.21
|
||||
},
|
||||
{
|
||||
"wallet": "0xd7d36345c4aab150e59577e360696b01d01d698b",
|
||||
"name": "gkmgkldfmg",
|
||||
"class": "volume",
|
||||
"floor": 470.51
|
||||
},
|
||||
{
|
||||
"wallet": "0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343",
|
||||
"name": "1kto1m",
|
||||
"class": "volume",
|
||||
"floor": 328.77
|
||||
},
|
||||
{
|
||||
"wallet": "0x215adbb63b47d0ca92f849fe2c2dc1adb0f6254c",
|
||||
"name": "BikesAreTheBikes",
|
||||
"class": "volume",
|
||||
"floor": 227.2
|
||||
},
|
||||
{
|
||||
"wallet": "0x921433c93558b9a4ba807ec824d02aad7ea2ddbf",
|
||||
"name": "AIcAIc",
|
||||
"class": "volume",
|
||||
"floor": 341.23
|
||||
},
|
||||
{
|
||||
"wallet": "0x40ce68f1564f3c751b12d88a393d8cc0651dbf90",
|
||||
"name": "JuiceFarm",
|
||||
"class": "volume"
|
||||
}
|
||||
],
|
||||
"feed_path": "live/copybot_live_real.json",
|
||||
"fill_log": "copybot_fills.live.jsonl",
|
||||
"shadow_log": "rtds_shadow.live.jsonl"
|
||||
{
|
||||
"wallet": "0xd7d36345c4aab150e59577e360696b01d01d698b",
|
||||
"name": "gkmgkldfmg",
|
||||
"class": "volume",
|
||||
"floor": 470.51
|
||||
},
|
||||
{
|
||||
"wallet": "0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343",
|
||||
"name": "1kto1m",
|
||||
"class": "volume",
|
||||
"floor": 328.77
|
||||
},
|
||||
{
|
||||
"wallet": "0x215adbb63b47d0ca92f849fe2c2dc1adb0f6254c",
|
||||
"name": "BikesAreTheBikes",
|
||||
"class": "volume",
|
||||
"floor": 227.2
|
||||
},
|
||||
{
|
||||
"wallet": "0x921433c93558b9a4ba807ec824d02aad7ea2ddbf",
|
||||
"name": "AIcAIc",
|
||||
"class": "volume",
|
||||
"floor": 341.23
|
||||
},
|
||||
{
|
||||
"wallet": "0x40ce68f1564f3c751b12d88a393d8cc0651dbf90",
|
||||
"name": "JuiceFarm",
|
||||
"class": "volume"
|
||||
}
|
||||
],
|
||||
"feed_path": "live/copybot_live_real.json",
|
||||
"fill_log": "copybot_fills.live.jsonl",
|
||||
"shadow_log": "rtds_shadow.live.jsonl"
|
||||
}
|
||||
@@ -732,6 +732,29 @@ class CopyTrader:
|
||||
price = self._live_price(token, "sell")
|
||||
if price is None:
|
||||
return
|
||||
# band-guarded mirroring (2026-07-23, #21 successor): the sell-band
|
||||
# study (research/sell_mirror_study.py) showed mirrored exits LOSE
|
||||
# in every band below 90c (exits gave up value; <30c +$79/sell left
|
||||
# on the table) and SAVE money at >=90c (near-resolution
|
||||
# profit-taking, n=68 -$0.85/sell). Sells below the floor are
|
||||
# ignored-and-ledgered exactly like hold mode, so the
|
||||
# counterfactual stream keeps accruing.
|
||||
min_p = (self.cfg.get("follow") or {}).get("mirror_sell_min_p", 0.0)
|
||||
if price < min_p:
|
||||
self.log(f"EXIT {label} — BAND-SKIP (sell {price:.2f} < "
|
||||
f"{min_p:.2f} floor, their frac {frac:.2f})")
|
||||
if self.on_ignored_exit:
|
||||
self.on_ignored_exit({
|
||||
"ts": round(time.time(), 1), "token": str(token),
|
||||
"their_size": their_size, "their_prev": their_prev,
|
||||
"frac": round(frac, 4), "reason": "band",
|
||||
"sell_px": round(price, 4),
|
||||
"shares_held": round(mine.get("shares", 0), 4),
|
||||
"cost_held": round(mine.get("cost", 0), 2),
|
||||
"title": (mine.get("title") or "")[:80],
|
||||
"outcome": mine.get("outcome"),
|
||||
"wallet": mine.get("wallet", "")})
|
||||
return
|
||||
if any(po.get("token") == token
|
||||
for po in self.state.get("pending_orders", [])):
|
||||
self.log(f"EXIT {label} — skip (in-play hold already pending on "
|
||||
|
||||
+70
-69
@@ -1,73 +1,74 @@
|
||||
{
|
||||
"mode": "paper",
|
||||
"bankroll_usd": 1000.0,
|
||||
"bankroll_pct": 0.04,
|
||||
"wallets": [
|
||||
{
|
||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
||||
"name": "Kruto2027",
|
||||
"class": "volume",
|
||||
"floor": 80.0,
|
||||
"floor_pin": 80.0
|
||||
},
|
||||
{
|
||||
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
||||
"name": "0xbadaf319",
|
||||
"class": "volume",
|
||||
"floor": 30.71
|
||||
},
|
||||
{
|
||||
"wallet": "0xd7d36345c4aab150e59577e360696b01d01d698b",
|
||||
"name": "gkmgkldfmg",
|
||||
"class": "volume",
|
||||
"floor": 470.51
|
||||
},
|
||||
{
|
||||
"wallet": "0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343",
|
||||
"name": "1kto1m",
|
||||
"class": "volume",
|
||||
"floor": 314.43
|
||||
},
|
||||
{
|
||||
"wallet": "0x215adbb63b47d0ca92f849fe2c2dc1adb0f6254c",
|
||||
"name": "BikesAreTheBikes",
|
||||
"class": "volume",
|
||||
"floor": 219.66
|
||||
},
|
||||
{
|
||||
"wallet": "0x921433c93558b9a4ba807ec824d02aad7ea2ddbf",
|
||||
"name": "AIcAIc",
|
||||
"class": "volume",
|
||||
"floor": 375.06
|
||||
},
|
||||
{
|
||||
"wallet": "0x40ce68f1564f3c751b12d88a393d8cc0651dbf90",
|
||||
"name": "JuiceFarm",
|
||||
"class": "volume"
|
||||
}
|
||||
],
|
||||
"follow": {
|
||||
"buy_only": true,
|
||||
"entry_mode": "maker",
|
||||
"exit_mode": "hold",
|
||||
"min_their_usd": 25.0,
|
||||
"min_entry": 0.0,
|
||||
"max_entry": 0.95,
|
||||
"class_pct": {
|
||||
"volume": 0.04,
|
||||
"whale": 0.12
|
||||
}
|
||||
"mode": "paper",
|
||||
"bankroll_usd": 1000.0,
|
||||
"bankroll_pct": 0.04,
|
||||
"wallets": [
|
||||
{
|
||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
||||
"name": "Kruto2027",
|
||||
"class": "volume",
|
||||
"floor": 80.0,
|
||||
"floor_pin": 80.0
|
||||
},
|
||||
"risk": {
|
||||
"max_trade_usd": 1000000.0,
|
||||
"max_position_usd": 1000000.0,
|
||||
"daily_spend_cap_usd": 1000000.0,
|
||||
"max_total_exposure_usd": 1000000.0,
|
||||
"max_open_positions": 1000,
|
||||
"min_price": 0.0,
|
||||
"max_price": 0.99,
|
||||
"min_order_usd": 5.0,
|
||||
"max_per_event": 0
|
||||
{
|
||||
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
||||
"name": "0xbadaf319",
|
||||
"class": "volume",
|
||||
"floor": 30.71
|
||||
},
|
||||
"price_guard_abs": 0.05
|
||||
{
|
||||
"wallet": "0xd7d36345c4aab150e59577e360696b01d01d698b",
|
||||
"name": "gkmgkldfmg",
|
||||
"class": "volume",
|
||||
"floor": 470.51
|
||||
},
|
||||
{
|
||||
"wallet": "0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343",
|
||||
"name": "1kto1m",
|
||||
"class": "volume",
|
||||
"floor": 314.43
|
||||
},
|
||||
{
|
||||
"wallet": "0x215adbb63b47d0ca92f849fe2c2dc1adb0f6254c",
|
||||
"name": "BikesAreTheBikes",
|
||||
"class": "volume",
|
||||
"floor": 219.66
|
||||
},
|
||||
{
|
||||
"wallet": "0x921433c93558b9a4ba807ec824d02aad7ea2ddbf",
|
||||
"name": "AIcAIc",
|
||||
"class": "volume",
|
||||
"floor": 375.06
|
||||
},
|
||||
{
|
||||
"wallet": "0x40ce68f1564f3c751b12d88a393d8cc0651dbf90",
|
||||
"name": "JuiceFarm",
|
||||
"class": "volume"
|
||||
}
|
||||
],
|
||||
"follow": {
|
||||
"buy_only": true,
|
||||
"entry_mode": "maker",
|
||||
"exit_mode": "mirror",
|
||||
"min_their_usd": 25.0,
|
||||
"min_entry": 0.0,
|
||||
"max_entry": 0.95,
|
||||
"class_pct": {
|
||||
"volume": 0.04,
|
||||
"whale": 0.12
|
||||
},
|
||||
"mirror_sell_min_p": 0.9
|
||||
},
|
||||
"risk": {
|
||||
"max_trade_usd": 1000000.0,
|
||||
"max_position_usd": 1000000.0,
|
||||
"daily_spend_cap_usd": 1000000.0,
|
||||
"max_total_exposure_usd": 1000000.0,
|
||||
"max_open_positions": 1000,
|
||||
"min_price": 0.0,
|
||||
"max_price": 0.99,
|
||||
"min_order_usd": 5.0,
|
||||
"max_per_event": 0
|
||||
},
|
||||
"price_guard_abs": 0.05
|
||||
}
|
||||
+16
-4
@@ -148,6 +148,17 @@ except Exception:
|
||||
_FOLLOW = {}
|
||||
ENTRY_MODE = os.environ.get("BT_ENTRY_MODE", _FOLLOW.get("entry_mode", "taker"))
|
||||
EXIT_MODE = os.environ.get("BT_EXIT_MODE", _FOLLOW.get("exit_mode", "mirror"))
|
||||
# band-guarded mirroring (#21 successor): sells below this price are NOT
|
||||
# mirrored — held to truth like hold mode (sell-band study: every band
|
||||
# <90c lost by mirroring; >=90c saved). 0.0 = mirror everything.
|
||||
MIRROR_SELL_MIN_P = float(os.environ.get(
|
||||
"BT_MIRROR_SELL_MIN_P", _FOLLOW.get("mirror_sell_min_p", 0.0)))
|
||||
|
||||
|
||||
def _mirrors(exit_p):
|
||||
"""Would the bot mirror a sell at this price under the current flags?"""
|
||||
return (EXIT_MODE != "hold"
|
||||
and (exit_p or 0) >= MIRROR_SELL_MIN_P)
|
||||
|
||||
|
||||
def entry_model(p, stake):
|
||||
@@ -525,7 +536,7 @@ def main():
|
||||
cash -= cost; fees_paid += fee; perW[b["wallet"]]["bets"] += 1
|
||||
shares = stake / p_eff # lag-adjusted entry price
|
||||
if b["kind"] == "res":
|
||||
if b.get("exit_t") and EXIT_MODE != "hold":
|
||||
if b.get("exit_t") and _mirrors(b.get("exit_p")):
|
||||
# the signal SOLD pre-resolution -> mirror the exit, like the
|
||||
# live bot: their exit price with the slippage haircut against
|
||||
# us, minus the taker fee (sells pay it; redeems don't)
|
||||
@@ -581,7 +592,7 @@ def main():
|
||||
stake = m.get("stake") or STAKE_MIN
|
||||
p_eff, fee, cost = entry_model(m["p"], stake)
|
||||
shares = stake / p_eff
|
||||
if m.get("exit_t") and EXIT_MODE != "hold": # would have mirrored
|
||||
if m.get("exit_t") and _mirrors(m.get("exit_p")): # would have mirrored
|
||||
xp = max(0.001, m["exit_p"] * (1 - SLIP))
|
||||
return shares * xp - shares * FEE_RATE * xp * (1 - xp) - cost
|
||||
if m.get("wp") is not None or m.get("won") is not None:
|
||||
@@ -600,12 +611,12 @@ def main():
|
||||
return m.get("won")
|
||||
|
||||
def _missed_won(m):
|
||||
if m.get("exit_t") and EXIT_MODE != "hold":
|
||||
if m.get("exit_t") and _mirrors(m.get("exit_p")):
|
||||
return None # mirrored exit: truth is the price
|
||||
return _truth_won(m)
|
||||
|
||||
def _missed_status(m):
|
||||
if m.get("exit_t") and EXIT_MODE != "hold":
|
||||
if m.get("exit_t") and _mirrors(m.get("exit_p")):
|
||||
return "sold"
|
||||
w = _truth_won(m)
|
||||
if w is None:
|
||||
@@ -683,6 +694,7 @@ def main():
|
||||
for m in missed[:60]],
|
||||
"missed_pnl": round(sum(hypo_pnl(m) for m in missed), 2),
|
||||
"entry_mode": ENTRY_MODE, "exit_mode": EXIT_MODE,
|
||||
"mirror_sell_min_p": MIRROR_SELL_MIN_P,
|
||||
}
|
||||
json.dump(out, open(os.path.join(HERE, OUT) if not os.path.isabs(OUT) else OUT, "w"),
|
||||
separators=(",", ":"))
|
||||
|
||||
@@ -37,6 +37,28 @@ def main():
|
||||
db = tape.connect()
|
||||
tape.build_resolved(db)
|
||||
pays = fwd.payouts_for(db, [str(s["token"]) for s in sells])
|
||||
# sell-price band slice: WHERE is mirroring unprofitable? delta>0 = the
|
||||
# exit gave up value (holding won); delta<0 = the exit saved us.
|
||||
BANDS = [(0, .30, "<30c"), (.30, .50, "30-50c"), (.50, .70, "50-70c"),
|
||||
(.70, .90, "70-90c"), (.90, 1.01, ">=90c")]
|
||||
print("\n== SELL-PRICE BANDS (both books pooled, chain-graded) ==")
|
||||
for lo, hi, tag in BANDS:
|
||||
bn = bd = 0
|
||||
for s2 in sells:
|
||||
p2 = pays.get(str(s2["token"]))
|
||||
if p2 is None or p2 == 0.5:
|
||||
continue
|
||||
sp = s2.get("price") or 0
|
||||
if not (lo <= sp < hi):
|
||||
continue
|
||||
sh2 = s2.get("shares") or 0
|
||||
bn += 1
|
||||
bd += (p2 - sp) * sh2
|
||||
if bn:
|
||||
lbl = ("SKIP-MIRROR (exits gave up value)" if bd > 0
|
||||
else "mirror OK (exits saved money)")
|
||||
print(f" {tag:>7}: n={bn:>3} · net delta {bd:+8.2f} "
|
||||
f"(${bd/bn:+.2f}/sell) -> {lbl}")
|
||||
for book in ("live", "paper"):
|
||||
ss = [s for s in sells if s["_book"] == book]
|
||||
n = unk = refund = right = wrong = 0
|
||||
|
||||
Reference in New Issue
Block a user