chore: add $500 capital sweep scripts (conservative + moonshot)
scripts/sweep_500.py: Parameter grid for capital-preservation oriented configs. Risk filters: max DD <= 30%, max single loss <= 16% of capital. scripts/sweep_moonshot.py: Aggressive grid for one-trade-hits-big strategy. Looser DD ceiling (50%), prioritizes biggest single-trade upside. Both run on the local v4 dataset to inform initial subscription parameter choices for live trading. Re-run after v5 accumulates enough signals (~6 weeks) to recalibrate. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
Moonshot sweep: maximize SINGLE-TRADE upside, accept higher drawdown.
|
||||
|
||||
Strategy hypothesis:
|
||||
- User does NOT care about monthly stability.
|
||||
- User wants 1-2 trades a month that individually return 20-50%+ of capital.
|
||||
- Acceptable trade-off: more losing months, occasional -30% drawdowns,
|
||||
in exchange for fat-tail capture of the right signals.
|
||||
|
||||
Sim differences vs sweep_500:
|
||||
- Higher leverage tier (up to 30×)
|
||||
- Larger per-trade margin (% of capital)
|
||||
- Higher conf floors (90, 95) — only the cleanest signals
|
||||
- Wider TP grid (1% up to 5% to capture biggest moves)
|
||||
- Tight SL (0.5% / 1% only — cut losers fast)
|
||||
- Looser DD filter (allow up to 50% account drawdown)
|
||||
- Reports BIGGEST winning trade explicitly
|
||||
"""
|
||||
import sqlite3
|
||||
import statistics
|
||||
from itertools import product
|
||||
from pathlib import Path
|
||||
|
||||
DB = Path(__file__).resolve().parents[1] / "trumpsignal.db"
|
||||
CAPITAL = 500.0
|
||||
FEE_BPS_RT = 0.0009
|
||||
|
||||
|
||||
def fetch_signals(conf_floor: int):
|
||||
con = sqlite3.connect(DB)
|
||||
con.row_factory = sqlite3.Row
|
||||
rows = con.execute("""
|
||||
SELECT id, signal, ai_confidence, price_impact_m1h
|
||||
FROM posts
|
||||
WHERE signal IN ('buy', 'short')
|
||||
AND price_at_post IS NOT NULL
|
||||
AND price_impact_m1h IS NOT NULL
|
||||
AND ai_confidence >= ?
|
||||
ORDER BY published_at
|
||||
""", (conf_floor,)).fetchall()
|
||||
con.close()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
def simulate(signals, margin: float, leverage: int, tp_pct: float, sl_pct: float):
|
||||
"""Return list of per-trade $ PnL."""
|
||||
notional = margin * leverage
|
||||
pnls: list[float] = []
|
||||
for s in signals:
|
||||
peak = s["price_impact_m1h"]
|
||||
if peak >= tp_pct:
|
||||
gross = tp_pct
|
||||
elif peak < 0:
|
||||
gross = -sl_pct
|
||||
else:
|
||||
gross = peak / 2
|
||||
net_pct = gross / 100 - FEE_BPS_RT
|
||||
pnls.append(notional * net_pct)
|
||||
return pnls
|
||||
|
||||
|
||||
def metrics(pnls):
|
||||
n = len(pnls)
|
||||
if n == 0:
|
||||
return None
|
||||
total = sum(pnls)
|
||||
wins = sum(1 for p in pnls if p > 0)
|
||||
biggest_win = max(pnls)
|
||||
biggest_loss = min(pnls)
|
||||
equity, peak, maxdd = CAPITAL, CAPITAL, 0.0
|
||||
for p in pnls:
|
||||
equity += p
|
||||
peak = max(peak, equity)
|
||||
maxdd = max(maxdd, peak - equity)
|
||||
return {
|
||||
"n": n,
|
||||
"wins": wins,
|
||||
"win_rate": round(wins/n*100, 1),
|
||||
"total": round(total, 2),
|
||||
"biggest_win": round(biggest_win, 2),
|
||||
"biggest_loss": round(biggest_loss, 2),
|
||||
"max_dd": round(maxdd, 2),
|
||||
"max_dd_pct": round(maxdd/CAPITAL*100, 1),
|
||||
"roi_pct": round(total/CAPITAL*100, 1),
|
||||
# "best-trade-as-%-of-capital" — the moonshot metric
|
||||
"best_trade_pct": round(biggest_win/CAPITAL*100, 1),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
print("$500 MOONSHOT SWEEP — maximize single-trade upside")
|
||||
print("=" * 100)
|
||||
|
||||
# Aggressive grid
|
||||
confs = [80, 85, 90, 95]
|
||||
margins = [50, 100, 150, 200]
|
||||
levs = [10, 15, 20, 25, 30]
|
||||
tps = [1.0, 1.5, 2.0, 3.0, 5.0]
|
||||
sls = [0.5, 1.0, 1.5]
|
||||
|
||||
results = []
|
||||
for conf in confs:
|
||||
sigs = fetch_signals(conf)
|
||||
if not sigs:
|
||||
continue
|
||||
for margin, lev, tp, sl in product(margins, levs, tps, sls):
|
||||
# Notional cap: don't exceed 12× capital total exposure
|
||||
if margin * lev > CAPITAL * 12:
|
||||
continue
|
||||
pnls = simulate(sigs, margin, lev, tp, sl)
|
||||
m = metrics(pnls)
|
||||
m.update({
|
||||
"conf": conf, "margin": margin, "lev": lev,
|
||||
"tp": tp, "sl": sl,
|
||||
"max_loss_per_trade": round(margin * lev * (sl/100 + FEE_BPS_RT), 2),
|
||||
})
|
||||
results.append(m)
|
||||
|
||||
# Soft filter: at least 5 trades, profitable, drawdown <50% (acceptable for moonshot)
|
||||
candidates = [r for r in results
|
||||
if r["n"] >= 5 and r["total"] > 0 and r["max_dd_pct"] <= 50]
|
||||
|
||||
print(f"\n{len(candidates)} viable combos (n>=5, profitable, DD<=50%)")
|
||||
print()
|
||||
|
||||
# ── 1. Highest single-trade upside ─────────────────────────────
|
||||
print("=" * 100)
|
||||
print("TOP 10 — BIGGEST SINGLE WINNING TRADE (the 'one trade hits big' goal)")
|
||||
print("=" * 100)
|
||||
print(f"{'conf':>4} {'margin':>6} {'lev':>3} {'TP%':>4} {'SL%':>4} "
|
||||
f"{'n':>3} {'win%':>5} {'best_win$':>9} {'best%cap':>9} "
|
||||
f"{'maxLoss$':>8} {'total$':>8} {'DD%':>5}")
|
||||
for r in sorted(candidates, key=lambda x: -x["biggest_win"])[:10]:
|
||||
print(f"{r['conf']:>4} ${r['margin']:>5} {r['lev']:>3}× "
|
||||
f"{r['tp']:>4.1f} {r['sl']:>4.1f} "
|
||||
f"{r['n']:>3} {r['win_rate']:>4.1f}% "
|
||||
f"${r['biggest_win']:>+7.2f} {r['best_trade_pct']:>+7.1f}% "
|
||||
f"${r['max_loss_per_trade']:>6.2f} ${r['total']:>+6.2f} "
|
||||
f"{r['max_dd_pct']:>4.1f}%")
|
||||
|
||||
# ── 2. Best total ROI accepting bigger drawdown ────────────────
|
||||
print()
|
||||
print("=" * 100)
|
||||
print("TOP 10 — TOTAL ROI (any drawdown ≤ 50%)")
|
||||
print("=" * 100)
|
||||
print(f"{'conf':>4} {'margin':>6} {'lev':>3} {'TP%':>4} {'SL%':>4} "
|
||||
f"{'n':>3} {'win%':>5} {'total$':>8} {'ROI%':>6} "
|
||||
f"{'maxLoss$':>8} {'maxDD%':>6} {'best%':>6}")
|
||||
for r in sorted(candidates, key=lambda x: -x["roi_pct"])[:10]:
|
||||
print(f"{r['conf']:>4} ${r['margin']:>5} {r['lev']:>3}× "
|
||||
f"{r['tp']:>4.1f} {r['sl']:>4.1f} "
|
||||
f"{r['n']:>3} {r['win_rate']:>4.1f}% "
|
||||
f"${r['total']:>+6.2f} {r['roi_pct']:>+5.1f}% "
|
||||
f"${r['max_loss_per_trade']:>6.2f} {r['max_dd_pct']:>5.1f}% "
|
||||
f"{r['best_trade_pct']:>+5.1f}%")
|
||||
|
||||
# ── 3. The "Black Swan" candidate — biggest in absolute $ terms ─
|
||||
print()
|
||||
print("=" * 100)
|
||||
print("DATA REALITY CHECK — biggest |move| observed across all signals")
|
||||
print("=" * 100)
|
||||
con = sqlite3.connect(DB)
|
||||
rows = con.execute("""
|
||||
SELECT id, signal, ai_confidence, price_impact_m1h
|
||||
FROM posts WHERE signal IN ('buy','short')
|
||||
AND price_impact_m1h IS NOT NULL
|
||||
ORDER BY ABS(price_impact_m1h) DESC LIMIT 5
|
||||
""").fetchall()
|
||||
con.close()
|
||||
print(f" {'rank':>4} {'id':>5} {'sig':>5} {'conf':>4} {'1h move':>10}")
|
||||
for i, r in enumerate(rows, 1):
|
||||
print(f" {i:>4} {r[0]:>5} {r[1]:>5} {r[2]:>4} {r[3]:>+9.3f}%")
|
||||
print()
|
||||
print("→ With $200 margin × 30× = $6000 notional, the 2.36% best move")
|
||||
print(f" would have netted: ${6000*0.0236-6000*0.0009:.2f} on a single trade")
|
||||
print(f" = {(6000*0.0236-6000*0.0009)/CAPITAL*100:.1f}% of $500 capital from ONE trade")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user