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",
|
"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.",
|
"_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_usd": 66.42,
|
||||||
"bankroll_pct": 0.04,
|
"bankroll_pct": 0.04,
|
||||||
"price_guard_abs": 0.05,
|
"price_guard_abs": 0.05,
|
||||||
"fak_retry_s": 10,
|
"fak_retry_s": 10,
|
||||||
"fak_retry_niche_s": {"crypto": 4, "esports": 10, "sports": 25,
|
"fak_retry_niche_s": {
|
||||||
"geo": 25, "politics": 25, "other": 25},
|
"crypto": 4,
|
||||||
"taker_fee_rate": 0.03,
|
"esports": 10,
|
||||||
"discord_webhook": "",
|
"sports": 25,
|
||||||
"follow": {
|
"geo": 25,
|
||||||
"buy_only": true,
|
"politics": 25,
|
||||||
"entry_mode": "maker",
|
"other": 25
|
||||||
"exit_mode": "hold",
|
},
|
||||||
"min_their_usd": 25.0,
|
"taker_fee_rate": 0.03,
|
||||||
"min_entry": 0.0,
|
"discord_webhook": "",
|
||||||
"max_entry": 0.95,
|
"follow": {
|
||||||
"class_pct": {
|
"buy_only": true,
|
||||||
"volume": 0.04,
|
"entry_mode": "maker",
|
||||||
"whale": 0.12
|
"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": {
|
"mirror_sell_min_p": 0.9
|
||||||
"max_trade_usd": 1000000.0,
|
},
|
||||||
"max_position_usd": 1000000.0,
|
"risk": {
|
||||||
"daily_spend_cap_usd": 1000000.0,
|
"max_trade_usd": 1000000.0,
|
||||||
"max_total_exposure_usd": 1000000.0,
|
"max_position_usd": 1000000.0,
|
||||||
"max_open_positions": 1000,
|
"daily_spend_cap_usd": 1000000.0,
|
||||||
"max_per_event": 0,
|
"max_total_exposure_usd": 1000000.0,
|
||||||
"min_price": 0.0,
|
"max_open_positions": 1000,
|
||||||
"max_price": 0.99,
|
"max_per_event": 0,
|
||||||
"min_order_usd": 1.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": "",
|
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
||||||
"funder_address": "",
|
"name": "0xbadaf319",
|
||||||
"signature_type": 1,
|
"class": "volume",
|
||||||
"order_type": "FAK",
|
"floor": 41.21
|
||||||
"auto_redeem": false,
|
|
||||||
"rpc_url": ""
|
|
||||||
},
|
},
|
||||||
"wallets": [
|
{
|
||||||
{
|
"wallet": "0xd7d36345c4aab150e59577e360696b01d01d698b",
|
||||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
"name": "gkmgkldfmg",
|
||||||
"name": "Kruto2027",
|
"class": "volume",
|
||||||
"class": "volume",
|
"floor": 470.51
|
||||||
"floor": 80.0
|
},
|
||||||
},
|
{
|
||||||
{
|
"wallet": "0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343",
|
||||||
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
"name": "1kto1m",
|
||||||
"name": "0xbadaf319",
|
"class": "volume",
|
||||||
"class": "volume",
|
"floor": 328.77
|
||||||
"floor": 41.21
|
},
|
||||||
},
|
{
|
||||||
{
|
"wallet": "0x215adbb63b47d0ca92f849fe2c2dc1adb0f6254c",
|
||||||
"wallet": "0xd7d36345c4aab150e59577e360696b01d01d698b",
|
"name": "BikesAreTheBikes",
|
||||||
"name": "gkmgkldfmg",
|
"class": "volume",
|
||||||
"class": "volume",
|
"floor": 227.2
|
||||||
"floor": 470.51
|
},
|
||||||
},
|
{
|
||||||
{
|
"wallet": "0x921433c93558b9a4ba807ec824d02aad7ea2ddbf",
|
||||||
"wallet": "0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343",
|
"name": "AIcAIc",
|
||||||
"name": "1kto1m",
|
"class": "volume",
|
||||||
"class": "volume",
|
"floor": 341.23
|
||||||
"floor": 328.77
|
},
|
||||||
},
|
{
|
||||||
{
|
"wallet": "0x40ce68f1564f3c751b12d88a393d8cc0651dbf90",
|
||||||
"wallet": "0x215adbb63b47d0ca92f849fe2c2dc1adb0f6254c",
|
"name": "JuiceFarm",
|
||||||
"name": "BikesAreTheBikes",
|
"class": "volume"
|
||||||
"class": "volume",
|
}
|
||||||
"floor": 227.2
|
],
|
||||||
},
|
"feed_path": "live/copybot_live_real.json",
|
||||||
{
|
"fill_log": "copybot_fills.live.jsonl",
|
||||||
"wallet": "0x921433c93558b9a4ba807ec824d02aad7ea2ddbf",
|
"shadow_log": "rtds_shadow.live.jsonl"
|
||||||
"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")
|
price = self._live_price(token, "sell")
|
||||||
if price is None:
|
if price is None:
|
||||||
return
|
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
|
if any(po.get("token") == token
|
||||||
for po in self.state.get("pending_orders", [])):
|
for po in self.state.get("pending_orders", [])):
|
||||||
self.log(f"EXIT {label} — skip (in-play hold already pending on "
|
self.log(f"EXIT {label} — skip (in-play hold already pending on "
|
||||||
|
|||||||
+70
-69
@@ -1,73 +1,74 @@
|
|||||||
{
|
{
|
||||||
"mode": "paper",
|
"mode": "paper",
|
||||||
"bankroll_usd": 1000.0,
|
"bankroll_usd": 1000.0,
|
||||||
"bankroll_pct": 0.04,
|
"bankroll_pct": 0.04,
|
||||||
"wallets": [
|
"wallets": [
|
||||||
{
|
{
|
||||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
||||||
"name": "Kruto2027",
|
"name": "Kruto2027",
|
||||||
"class": "volume",
|
"class": "volume",
|
||||||
"floor": 80.0,
|
"floor": 80.0,
|
||||||
"floor_pin": 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
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"risk": {
|
{
|
||||||
"max_trade_usd": 1000000.0,
|
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
||||||
"max_position_usd": 1000000.0,
|
"name": "0xbadaf319",
|
||||||
"daily_spend_cap_usd": 1000000.0,
|
"class": "volume",
|
||||||
"max_total_exposure_usd": 1000000.0,
|
"floor": 30.71
|
||||||
"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
|
{
|
||||||
|
"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 = {}
|
_FOLLOW = {}
|
||||||
ENTRY_MODE = os.environ.get("BT_ENTRY_MODE", _FOLLOW.get("entry_mode", "taker"))
|
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"))
|
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):
|
def entry_model(p, stake):
|
||||||
@@ -525,7 +536,7 @@ def main():
|
|||||||
cash -= cost; fees_paid += fee; perW[b["wallet"]]["bets"] += 1
|
cash -= cost; fees_paid += fee; perW[b["wallet"]]["bets"] += 1
|
||||||
shares = stake / p_eff # lag-adjusted entry price
|
shares = stake / p_eff # lag-adjusted entry price
|
||||||
if b["kind"] == "res":
|
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
|
# the signal SOLD pre-resolution -> mirror the exit, like the
|
||||||
# live bot: their exit price with the slippage haircut against
|
# live bot: their exit price with the slippage haircut against
|
||||||
# us, minus the taker fee (sells pay it; redeems don't)
|
# us, minus the taker fee (sells pay it; redeems don't)
|
||||||
@@ -581,7 +592,7 @@ def main():
|
|||||||
stake = m.get("stake") or STAKE_MIN
|
stake = m.get("stake") or STAKE_MIN
|
||||||
p_eff, fee, cost = entry_model(m["p"], stake)
|
p_eff, fee, cost = entry_model(m["p"], stake)
|
||||||
shares = stake / p_eff
|
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))
|
xp = max(0.001, m["exit_p"] * (1 - SLIP))
|
||||||
return shares * xp - shares * FEE_RATE * xp * (1 - xp) - cost
|
return shares * xp - shares * FEE_RATE * xp * (1 - xp) - cost
|
||||||
if m.get("wp") is not None or m.get("won") is not None:
|
if m.get("wp") is not None or m.get("won") is not None:
|
||||||
@@ -600,12 +611,12 @@ def main():
|
|||||||
return m.get("won")
|
return m.get("won")
|
||||||
|
|
||||||
def _missed_won(m):
|
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 None # mirrored exit: truth is the price
|
||||||
return _truth_won(m)
|
return _truth_won(m)
|
||||||
|
|
||||||
def _missed_status(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"
|
return "sold"
|
||||||
w = _truth_won(m)
|
w = _truth_won(m)
|
||||||
if w is None:
|
if w is None:
|
||||||
@@ -683,6 +694,7 @@ def main():
|
|||||||
for m in missed[:60]],
|
for m in missed[:60]],
|
||||||
"missed_pnl": round(sum(hypo_pnl(m) for m in missed), 2),
|
"missed_pnl": round(sum(hypo_pnl(m) for m in missed), 2),
|
||||||
"entry_mode": ENTRY_MODE, "exit_mode": EXIT_MODE,
|
"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"),
|
json.dump(out, open(os.path.join(HERE, OUT) if not os.path.isabs(OUT) else OUT, "w"),
|
||||||
separators=(",", ":"))
|
separators=(",", ":"))
|
||||||
|
|||||||
@@ -37,6 +37,28 @@ def main():
|
|||||||
db = tape.connect()
|
db = tape.connect()
|
||||||
tape.build_resolved(db)
|
tape.build_resolved(db)
|
||||||
pays = fwd.payouts_for(db, [str(s["token"]) for s in sells])
|
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"):
|
for book in ("live", "paper"):
|
||||||
ss = [s for s in sells if s["_book"] == book]
|
ss = [s for s in sells if s["_book"] == book]
|
||||||
n = unk = refund = right = wrong = 0
|
n = unk = refund = right = wrong = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user