mirror of
https://github.com/jaxperro/winning-wallet-finder.git
synced 2026-07-28 00:07:47 +00:00
profit ratchet: stakes pin at $250, surplus swept to a banked reserve
Once working equity exceeds stake_cap/pct ($6,250 at 4%), surplus cash sweeps to state["reserve"]/portfolio reserve - banked, never bet, immune to drawdown - keeping stakes at the $250 level where marketable fills sit inside typical book depth. Feed/summary gain reserve; realized = cash+exposure+reserve-bank. June backfill: equity $15,684 (+1468%), $9,185 banked, next stake pinned $250. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+24
-6
@@ -54,6 +54,10 @@ DEFAULT_CONFIG = {
|
|||||||
"bankroll_pct": 0.02, # fraction of CURRENT equity per new entry
|
"bankroll_pct": 0.02, # fraction of CURRENT equity per new entry
|
||||||
# (compounds up and down; falls back to a flat
|
# (compounds up and down; falls back to a flat
|
||||||
# fraction of bankroll_usd when cash isn't tracked)
|
# fraction of bankroll_usd when cash isn't tracked)
|
||||||
|
"stake_cap_usd": 0, # >0: pin stakes at this size once the book grows
|
||||||
|
# past cap/bankroll_pct — surplus cash is SWEPT to
|
||||||
|
# state["reserve"], a banked pot that never bets
|
||||||
|
# (profit ratchet + keeps fills inside book depth)
|
||||||
"price_guard_pct": 0.05, # skip if price moved >5% from their fill
|
"price_guard_pct": 0.05, # skip if price moved >5% from their fill
|
||||||
"risk": {
|
"risk": {
|
||||||
"max_trade_usd": 50.0, # hard ceiling on any single copy
|
"max_trade_usd": 50.0, # hard ceiling on any single copy
|
||||||
@@ -264,20 +268,34 @@ class CopyTrader:
|
|||||||
DD_FACTOR = 0.5 # …bet half size until equity recovers
|
DD_FACTOR = 0.5 # …bet half size until equity recovers
|
||||||
|
|
||||||
def stake_usd(self):
|
def stake_usd(self):
|
||||||
"""Next bet size = bankroll_pct × current equity (cash + open cost basis),
|
"""Next bet size = bankroll_pct × current WORKING equity (cash + open cost
|
||||||
so stakes compound with the book in both directions; halved while in a
|
basis), so stakes compound with the book in both directions; halved while
|
||||||
>20% drawdown from the high-water mark. Falls back to the flat static
|
in a >20% drawdown from the high-water mark. Falls back to the flat static
|
||||||
stake when cash isn't tracked (legacy poll CLI)."""
|
stake when cash isn't tracked (legacy poll CLI).
|
||||||
|
|
||||||
|
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."""
|
||||||
cash = self.state.get("cash")
|
cash = self.state.get("cash")
|
||||||
if cash is None:
|
if cash is None:
|
||||||
return self.cfg["bankroll_usd"] * self.cfg["bankroll_pct"]
|
return self.cfg["bankroll_usd"] * self.cfg["bankroll_pct"]
|
||||||
|
frac = self.cfg["bankroll_pct"]
|
||||||
|
cap = self.cfg.get("stake_cap_usd") or 0
|
||||||
eq = cash + self.open_exposure()
|
eq = cash + self.open_exposure()
|
||||||
|
if cap and frac > 0 and eq > cap / frac:
|
||||||
|
sweep = min(cash, eq - cap / frac)
|
||||||
|
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)
|
hwm = max(self.state.get("hwm", 0.0), eq)
|
||||||
self.state["hwm"] = hwm
|
self.state["hwm"] = hwm
|
||||||
frac = self.cfg["bankroll_pct"]
|
|
||||||
if eq < self.DD_THRESHOLD * hwm:
|
if eq < self.DD_THRESHOLD * hwm:
|
||||||
frac *= self.DD_FACTOR
|
frac *= self.DD_FACTOR
|
||||||
return frac * eq
|
stake = frac * eq
|
||||||
|
return min(stake, cap) if cap else stake
|
||||||
|
|
||||||
def record_miss(self, wallet, token, cond, title, outcome, price, want, reason):
|
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 —
|
"""A bet the strategy WOULD have copied but the book couldn't take —
|
||||||
|
|||||||
+8
-4
@@ -407,7 +407,9 @@ class Copybot:
|
|||||||
"event_cap": self.engine.risk.get("max_per_event"),
|
"event_cap": self.engine.risk.get("max_per_event"),
|
||||||
"hwm": round(st.get("hwm", 0.0), 2),
|
"hwm": round(st.get("hwm", 0.0), 2),
|
||||||
"cash": round(cash, 2), "deployed": round(exp, 2),
|
"cash": round(cash, 2), "deployed": round(exp, 2),
|
||||||
"realized": round(cash + exp - bank, 2), "open_count": len(mp),
|
"reserve": round(st.get("reserve", 0.0), 2), # banked profit, never bet
|
||||||
|
"realized": round(cash + exp + st.get("reserve", 0.0) - bank, 2),
|
||||||
|
"open_count": len(mp),
|
||||||
"fees_paid": round(st.get("fees_paid", 0.0), 2),
|
"fees_paid": round(st.get("fees_paid", 0.0), 2),
|
||||||
"fee_rate": self.fee_rate,
|
"fee_rate": self.fee_rate,
|
||||||
"lag": {"n": lag.get("n", 0),
|
"lag": {"n": lag.get("n", 0),
|
||||||
@@ -504,15 +506,17 @@ class Copybot:
|
|||||||
stake = self.engine.stake_usd() # dynamic: pct of current equity
|
stake = self.engine.stake_usd() # dynamic: pct of current equity
|
||||||
exp = self.engine.open_exposure()
|
exp = self.engine.open_exposure()
|
||||||
cash = self.engine.state.get("cash", bank)
|
cash = self.engine.state.get("cash", bank)
|
||||||
realized = cash + exp - bank # see _drain_fills / settle_resolved
|
reserve = self.engine.state.get("reserve", 0.0)
|
||||||
|
realized = cash + exp + reserve - bank # see _drain_fills / settle_resolved
|
||||||
n = len(self.engine.state["my_pos"])
|
n = len(self.engine.state["my_pos"])
|
||||||
lag = self.engine.state.get("lag", {})
|
lag = self.engine.state.get("lag", {})
|
||||||
lagstr = ""
|
lagstr = ""
|
||||||
if lag.get("n"):
|
if lag.get("n"):
|
||||||
lagstr = (f" · {lag['n']} copies avg lag {lag['sum_s']/lag['n']:.0f}s "
|
lagstr = (f" · {lag['n']} copies avg lag {lag['sum_s']/lag['n']:.0f}s "
|
||||||
f"slip {lag['sum_slip_pct']/lag['n']:+.1%}")
|
f"slip {lag['sum_slip_pct']/lag['n']:+.1%}")
|
||||||
log(f"[{cycle}] open {n} · deployed ${exp:,.0f} · free ${cash:,.0f}/${bank:,.0f} "
|
bankstr = f" · banked ${reserve:,.0f}" if reserve else ""
|
||||||
f"· realized ${realized:+,.2f}{lagstr}"
|
log(f"[{cycle}] open {n} · deployed ${exp:,.0f} · free ${cash:,.0f}/${bank:,.0f}"
|
||||||
|
f"{bankstr} · realized ${realized:+,.2f}{lagstr}"
|
||||||
+ (f" · CAN'T OPEN (free < ${stake:,.0f} stake — bets missed)"
|
+ (f" · CAN'T OPEN (free < ${stake:,.0f} stake — bets missed)"
|
||||||
if cash < stake else ""))
|
if cash < stake else ""))
|
||||||
|
|
||||||
|
|||||||
@@ -55,5 +55,6 @@
|
|||||||
"max_price": 0.99,
|
"max_price": 0.99,
|
||||||
"min_order_usd": 5.0,
|
"min_order_usd": 5.0,
|
||||||
"max_per_event": 0
|
"max_per_event": 0
|
||||||
}
|
},
|
||||||
|
"stake_cap_usd": 250.0
|
||||||
}
|
}
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+21
-9
@@ -47,7 +47,10 @@ GAMMA = "https://gamma-api.polymarket.com"
|
|||||||
# event (a game's markets settle together — one correlated bet, not N diversified
|
# event (a game's markets settle together — one correlated bet, not N diversified
|
||||||
# ones); 0 = off, mirror every conviction trade.
|
# ones); 0 = off, mirror every conviction trade.
|
||||||
PCT = 0.04
|
PCT = 0.04
|
||||||
STAKE_MIN, STAKE_CAP = 5.0, float("inf") # uncapped — 4% of equity rides fully
|
# stakes pin at STAKE_CAP (where marketable fills still sit inside typical book
|
||||||
|
# depth); once working equity exceeds STAKE_CAP/PCT, surplus cash is SWEPT into
|
||||||
|
# a banked reserve — a profit ratchet that never bets and can't be drawn down.
|
||||||
|
STAKE_MIN, STAKE_CAP = 5.0, 250.0
|
||||||
EVENT_CAP = 0
|
EVENT_CAP = 0
|
||||||
DD_THRESHOLD, DD_FACTOR = 0.80, 0.5
|
DD_THRESHOLD, DD_FACTOR = 0.80, 0.5
|
||||||
# skip entries above this price. High-price favorites win pennies and lose
|
# skip entries above this price. High-price favorites win pennies and lose
|
||||||
@@ -207,6 +210,7 @@ def main():
|
|||||||
fees_paid = 0.0
|
fees_paid = 0.0
|
||||||
hwm = BANK
|
hwm = BANK
|
||||||
capped = 0
|
capped = 0
|
||||||
|
reserve = 0.0
|
||||||
held = [] # (free_t, cost, payoff) cost = stake + entry fee; payoff paid at free_t
|
held = [] # (free_t, cost, payoff) cost = stake + entry fee; payoff paid at free_t
|
||||||
perW = {w["wallet"]: {"name": w["name"], "wallet": w["wallet"], "bets": 0,
|
perW = {w["wallet"]: {"name": w["name"], "wallet": w["wallet"], "bets": 0,
|
||||||
"won": 0, "lost": 0,
|
"won": 0, "lost": 0,
|
||||||
@@ -214,9 +218,15 @@ def main():
|
|||||||
resolved, current, missed = [], [], []
|
resolved, current, missed = [], [], []
|
||||||
|
|
||||||
def cur_stake():
|
def cur_stake():
|
||||||
"""PCT of current equity, drawdown-braked, clamped — the copybot's rule."""
|
"""PCT of current WORKING equity, drawdown-braked, clamped — the copybot's
|
||||||
nonlocal hwm
|
rule. Sweeps surplus cash to the banked reserve first, so working equity
|
||||||
|
stays at the level where stakes ~= STAKE_CAP."""
|
||||||
|
nonlocal hwm, cash, reserve
|
||||||
eq = cash + sum(c for _, c, _, _ in held)
|
eq = cash + sum(c for _, c, _, _ in held)
|
||||||
|
excess = eq - STAKE_CAP / PCT
|
||||||
|
if excess > 0:
|
||||||
|
sweep = min(cash, excess)
|
||||||
|
cash -= sweep; reserve += sweep; eq -= sweep
|
||||||
hwm = max(hwm, eq)
|
hwm = max(hwm, eq)
|
||||||
frac = PCT * (DD_FACTOR if eq < DD_THRESHOLD * hwm else 1.0)
|
frac = PCT * (DD_FACTOR if eq < DD_THRESHOLD * hwm else 1.0)
|
||||||
return max(STAKE_MIN, min(STAKE_CAP, frac * eq))
|
return max(STAKE_MIN, min(STAKE_CAP, frac * eq))
|
||||||
@@ -296,16 +306,18 @@ def main():
|
|||||||
for w in WALLETS:
|
for w in WALLETS:
|
||||||
t = cache.conv_cutoff(b["size"] for b in cache.get_bets(w["wallet"]) if (b["size"] or 0) > 0)
|
t = cache.conv_cutoff(b["size"] for b in cache.get_bets(w["wallet"]) if (b["size"] or 0) > 0)
|
||||||
conv_thr[w["wallet"]] = round(t) if t != float("inf") else 1e12
|
conv_thr[w["wallet"]] = round(t) if t != float("inf") else 1e12
|
||||||
equity = cash + invested
|
equity = cash + invested + reserve
|
||||||
out = {
|
out = {
|
||||||
"started": START, "updated": now,
|
"started": START, "updated": now,
|
||||||
"bank": BANK, "stake": round(cur_stake(), 2), # the NEXT bet's size
|
"bank": BANK, "stake": round(cur_stake(), 2), # the NEXT bet's size
|
||||||
"stake_pct": PCT, "event_cap": EVENT_CAP, "hwm": round(hwm, 2),
|
"stake_pct": PCT, "stake_cap": STAKE_CAP, "event_cap": EVENT_CAP,
|
||||||
|
"hwm": round(hwm, 2),
|
||||||
"dd_threshold": DD_THRESHOLD, "capped_count": capped,
|
"dd_threshold": DD_THRESHOLD, "capped_count": capped,
|
||||||
"max_entry": MAX_ENTRY,
|
"max_entry": MAX_ENTRY,
|
||||||
"fee_rate": FEE_RATE, "slip": SLIP, "lag_est_s": LAG_EST_S,
|
"fee_rate": FEE_RATE, "slip": SLIP, "lag_est_s": LAG_EST_S,
|
||||||
"fees_paid": round(fees_paid, 2),
|
"fees_paid": round(fees_paid, 2),
|
||||||
"equity": round(equity, 2), "liquid": round(cash, 2), "invested": round(invested, 2),
|
"equity": round(equity, 2), "liquid": round(cash, 2), "invested": round(invested, 2),
|
||||||
|
"reserve": round(reserve, 2), # banked profit, never bet
|
||||||
"realized": round(realized, 2), "pnl": round(equity - BANK, 2),
|
"realized": round(realized, 2), "pnl": round(equity - BANK, 2),
|
||||||
"unreal": round(invested - open_cost, 2),
|
"unreal": round(invested - open_cost, 2),
|
||||||
"resolved_count": len(resolved), "wins": wins, "losses": len(resolved) - wins,
|
"resolved_count": len(resolved), "wins": wins, "losses": len(resolved) - wins,
|
||||||
@@ -330,10 +342,10 @@ def main():
|
|||||||
json.dump(out, open(os.path.join(HERE, OUT) if not os.path.isabs(OUT) else OUT, "w"),
|
json.dump(out, open(os.path.join(HERE, OUT) if not os.path.isabs(OUT) else OUT, "w"),
|
||||||
separators=(",", ":"))
|
separators=(",", ":"))
|
||||||
save_slug_cache()
|
save_slug_cache()
|
||||||
print(f"portfolio: equity ${equity:,.0f} ({(equity-BANK)/BANK*100:+.0f}%) | realized ${realized:+,.0f} "
|
print(f"portfolio: equity ${equity:,.0f} ({(equity-BANK)/BANK*100:+.0f}%) | banked ${reserve:,.0f} "
|
||||||
f"| fees ${fees_paid:,.0f} | next stake ${cur_stake():,.0f} | {len(resolved)} resolved "
|
f"| realized ${realized:+,.0f} | fees ${fees_paid:,.0f} | next stake ${cur_stake():,.0f} "
|
||||||
f"({wins}W/{len(resolved)-wins}L) | {len(current)} open | {len(missed)} missed "
|
f"| {len(resolved)} resolved ({wins}W/{len(resolved)-wins}L) | {len(current)} open "
|
||||||
f"({capped} event-capped) | -> portfolio.json", flush=True)
|
f"| {len(missed)} missed ({capped} event-capped) | -> portfolio.json", flush=True)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user