sizing discipline binds per market: adds capped at the current stake size

Proportional add-mirroring was unbounded after max_position_usd was lifted -
fortuneking doubling into his own bet took one game to $90 on a $42-stake
book (2.15 stakes). Adds now only top a position up to stake_usd(); the
backtest was already one-market-one-stake, so bot and model agree again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jaxperro
2026-07-03 21:06:25 -04:00
parent d11dcf11bd
commit 6fa1ba94bf
2 changed files with 20 additions and 6 deletions
+8 -4
View File
@@ -144,10 +144,14 @@ backtest and bot share:
- **Lag + slippage**: the bot fills at the live CLOB ask at detection (~60s poll), - **Lag + slippage**: the bot fills at the live CLOB ask at detection (~60s poll),
logging per-fill `detect_lag_s` and `slippage_pct`; the backtest applies a logging per-fill `detect_lag_s` and `slippage_pct`; the backtest applies a
+0.5%/~90s haircut. Measured so far: ~48s avg / +0.8% avg slip. +0.5%/~90s haircut. Measured so far: ~48s avg / +0.8% avg slip.
- **Dynamic sizing**: each bet stakes **4% of current equity** (compounds both - **Dynamic sizing**: each bet stakes **4% of current working equity** (compounds
ways), halved while equity is below 80% of its high-water mark. No per-trade both ways), halved while equity is below 80% of its high-water mark. The rule
cap. A per-event correlation cap exists (`risk.max_per_event`) but is **off** binds **per market** — adds that mirror a sharp scaling in can only top a
every conviction trade is followed. position up to the current stake size, never past it.
- **Profit ratchet** (`stake_cap_usd: 250`): stakes pin at $250; once the book
outgrows that level, surplus cash sweeps to a **banked reserve** that never
bets — locked-in profit, and fills stay inside realistic book depth. A
per-event correlation cap exists (`risk.max_per_event`) but is **off**.
- **Entry cap 0.95**: entries above 95¢ are skipped (`follow.max_entry`) — the - **Entry cap 0.95**: entries above 95¢ are skipped (`follow.max_entry`) — the
June sweep showed >95¢ favorites *lower* final equity even while winning June sweep showed >95¢ favorites *lower* final equity even while winning
(slip + fee eat the 13% payouts; the capital compounds better elsewhere). (slip + fee eat the 13% payouts; the capital compounds better elsewhere).
+12 -2
View File
@@ -427,11 +427,21 @@ class CopyTrader:
return return
if is_add: if is_add:
# proportional add: grow my position by the same fraction they did # proportional add: grow my position by the same fraction they did
# but SIZING DISCIPLINE binds per market: total position cost never
# exceeds the current stake rule. Unbounded mirroring once took one
# game to 2.15 stakes ($90 on a $42-stake book) when fortuneking
# doubled into his own bet; the backtest is one-market-one-stake,
# so the bot must be too.
their_prev = self.state["their_pos"].get(wallet, {}).get(token, 0) their_prev = self.state["their_pos"].get(wallet, {}).get(token, 0)
frac = their_size / their_prev if their_prev > 0 else 0 frac = their_size / their_prev if their_prev > 0 else 0
want_shares = mine["shares"] * frac want_shares = mine["shares"] * frac
want_usd = want_shares * price room = self.stake_usd() - 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)")
return
want_usd = min(want_shares * price, room)
kind = "ADD " kind = "ADD "
else: else:
want_usd = self.stake_usd() # fraction of current equity (compounds) want_usd = self.stake_usd() # fraction of current equity (compounds)