diff --git a/archive/copytrade.py b/archive/copytrade.py index 1bc0f0e9..839b7809 100644 --- a/archive/copytrade.py +++ b/archive/copytrade.py @@ -280,39 +280,31 @@ class CopyTrader: pct = pcts.get(self.wallet_class(wallet)) return self.cfg["bankroll_pct"] if pct is None else float(pct) - def stake_usd(self, wallet=None): + def stake_usd(self, wallet=None, their=None): """Next bet size = the wallet's class fraction (stake_frac) × current - WORKING equity (cash + open cost basis), so stakes compound with the book - in both directions; halved while in a >20% drawdown from the high-water - mark. Falls back to the flat static stake when cash isn't tracked - (legacy poll CLI). The sweep threshold below stays on the BASE - bankroll_pct so which wallet happens to trade doesn't change when - profits get banked. - - stake_cap_usd (profit ratchet): once working equity exceeds - cap/bankroll_pct — the level where stakes hit the cap — the surplus CASH - is swept into state["reserve"]: banked, never bet, immune to drawdowns. - Stakes stay pinned ~at the cap, where marketable fills are still inside - typical book depth.""" + WORKING equity (cash + open cost basis), halved in a >20% drawdown from + the high-water mark — and NEVER larger than the followed wallet's own + stake (`their` = the signal's position size so far): when the + percentage works out to more than they actually bet, mirror their + exact amount. The stake cap and banked-reserve sweep are retired + (2026-07-06, with the backtest's banking logic) — the their-bet + ceiling is the liquidity bound now: fills stay within size the market + demonstrably absorbed from the signal itself. Falls back to the flat + static stake when cash isn't tracked (legacy poll CLI).""" frac = self.stake_frac(wallet) cash = self.state.get("cash") if cash is None: - return self.cfg["bankroll_usd"] * frac - base = self.cfg["bankroll_pct"] - cap = self.cfg.get("stake_cap_usd") or 0 - eq = cash + self.open_exposure() - if cap and base > 0 and eq > cap / base: - sweep = min(cash, eq - cap / base) - if sweep > 0: - self.state["cash"] = cash = cash - sweep - self.state["reserve"] = self.state.get("reserve", 0.0) + sweep - eq -= sweep - hwm = max(self.state.get("hwm", 0.0), eq) - self.state["hwm"] = hwm - if eq < self.DD_THRESHOLD * hwm: - frac *= self.DD_FACTOR - stake = frac * eq - return min(stake, cap) if cap else stake + stake = self.cfg["bankroll_usd"] * frac + else: + eq = cash + self.open_exposure() + hwm = max(self.state.get("hwm", 0.0), eq) + self.state["hwm"] = hwm + if eq < self.DD_THRESHOLD * hwm: + frac *= self.DD_FACTOR + stake = frac * eq + if their and stake > their: + stake = their + return stake def record_miss(self, wallet, token, cond, title, outcome, price, want, reason): """A bet the strategy WOULD have copied but the book couldn't take — @@ -453,7 +445,7 @@ class CopyTrader: their_prev = self.state["their_pos"].get(wallet, {}).get(token, 0) frac = their_size / their_prev if their_prev > 0 else 0 want_shares = mine["shares"] * frac - room = self.stake_usd(wallet) - mine["cost"] + room = self.stake_usd(wallet, their_prev + their_size) - mine["cost"] if room < self.risk["min_order_usd"]: self.log(f"ADD {label} — skip (position ${mine['cost']:.0f} already " f"at the stake size)") @@ -461,7 +453,7 @@ class CopyTrader: want_usd = min(want_shares * price, room) kind = "ADD " else: - want_usd = self.stake_usd(wallet) # fraction of current equity (compounds) + want_usd = self.stake_usd(wallet, their_prev + their_size) # class % of equity, capped at their bet kind = "OPEN" pos_cost = mine["cost"] if is_add else 0.0 diff --git a/live/copybot.paper.json b/live/copybot.paper.json index 91dc5b07..da006036 100644 --- a/live/copybot.paper.json +++ b/live/copybot.paper.json @@ -4,14 +4,46 @@ "bankroll_pct": 0.04, "price_guard_pct": 0.05, "wallets": [ - { "wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb", "name": "Kruto2027", "class": "volume" }, - { "wallet": "0x86c878cde72660ec52f5e6f0f0438b76de8fc867", "name": "fortuneking", "class": "volume" }, - { "wallet": "0x41558102a796ba971c7567cad41c307e59f8fa41", "name": "LSB1", "class": "volume" }, - { "wallet": "0xd96750bf8d941a8186e592b0ae6e096da66aa266", "name": "imwalkinghere", "class": "volume" }, - { "wallet": "0xfc81760d44a21acc9fd4b749a5bf9a9b2eeae072", "name": "iohihoo", "class": "volume" }, - { "wallet": "0xf5fe759cece500f58a431ef8dacea321f6e3e23d", "name": "Stavenson", "class": "whale" }, - { "wallet": "0x73afc8160c17830c0c7281a7bf570c871455b880", "name": "0x4bFb-whale", "class": "whale" }, - { "wallet": "0x72e1597864456eda62878413cf3e60c332e4a45d", "name": "ArbTrader", "class": "whale" } + { + "wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb", + "name": "Kruto2027", + "class": "volume" + }, + { + "wallet": "0x86c878cde72660ec52f5e6f0f0438b76de8fc867", + "name": "fortuneking", + "class": "volume" + }, + { + "wallet": "0x41558102a796ba971c7567cad41c307e59f8fa41", + "name": "LSB1", + "class": "volume" + }, + { + "wallet": "0xd96750bf8d941a8186e592b0ae6e096da66aa266", + "name": "imwalkinghere", + "class": "volume" + }, + { + "wallet": "0xfc81760d44a21acc9fd4b749a5bf9a9b2eeae072", + "name": "iohihoo", + "class": "volume" + }, + { + "wallet": "0xf5fe759cece500f58a431ef8dacea321f6e3e23d", + "name": "Stavenson", + "class": "whale" + }, + { + "wallet": "0x73afc8160c17830c0c7281a7bf570c871455b880", + "name": "0x4bFb-whale", + "class": "whale" + }, + { + "wallet": "0x72e1597864456eda62878413cf3e60c332e4a45d", + "name": "ArbTrader", + "class": "whale" + } ], "follow": { "buy_only": true, @@ -33,6 +65,5 @@ "max_price": 0.99, "min_order_usd": 5.0, "max_per_event": 0 - }, - "stake_cap_usd": 250.0 -} + } +} \ No newline at end of file