feat: 5-coin fleet (DOGE/SOL/ETH/XRP/BTC), enriched context (multi-TF trend, orderbook depth, BTC corr, WR history, regime), memory system (learned patterns), self-adapting overrides, fleet dashboard with per-coin RSI/mom

This commit is contained in:
drjones
2026-08-02 19:25:58 -07:00
parent a4def4f72e
commit 8e860361ec
14 changed files with 153 additions and 6 deletions

Binary file not shown.

Binary file not shown.

38
bot.log
View File

@@ -1714,3 +1714,41 @@ sqlite3.OperationalError: no such column: id
2026-08-02 19:08:26,747 POST NO x1 KXETH15M-26AUG022215-15 @ 50¢ — DOWN: DOWN spike 0.06% conf=0.65 [RSI oversold + Fear, but 15m momentum DOWN suggests fading t]
2026-08-02 19:08:45,920 loop error: name 'llm_vote' is not defined
2026-08-02 19:09:12,858 loop error: name 'llm_vote' is not defined
2026-08-02 19:12:26,076 POST YES x1 KXDOGE15M-26AUG022215-15 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP
2026-08-02 19:12:53,256 POST YES x1 KXSOL15M-26AUG022215-15 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP
2026-08-02 19:13:21,404 POST YES x1 KXETH15M-26AUG022215-15 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP
2026-08-02 19:13:25,646 loop error: name 'llm_vote' is not defined
2026-08-02 19:13:51,321 loop error: name 'llm_vote' is not defined
2026-08-02 19:15:01,182 HEDGE KXBTC15M-TEST: no@45¢ + yes@42¢ → locked 13¢
2026-08-02 19:15:18,405 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:15:23,184 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:15:23,184 staggering start by 20s
2026-08-02 19:15:28,159 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:15:28,159 staggering start by 40s
2026-08-02 19:15:31,318 POST YES x1 KXDOGE15M-26AUG022230-30 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP
2026-08-02 19:15:55,907 POST NO x1 KXSOL15M-26AUG022230-30 @ 50¢ — ⚠ RSI 100 extreme overbought → fade DOWN
2026-08-02 19:16:31,965 POST NO x1 KXETH15M-26AUG022230-30 @ 50¢ — ⚠ RSI 100 extreme overbought → fade DOWN
2026-08-02 19:18:30,203 127.0.0.1 - - [02/Aug/2026 19:18:30] "GET /api/state HTTP/1.1" 200 -
2026-08-02 19:18:33,639 127.0.0.1 - - [02/Aug/2026 19:18:33] "GET /api/state HTTP/1.1" 200 -
2026-08-02 19:18:38,654 127.0.0.1 - - [02/Aug/2026 19:18:38] "GET /api/state HTTP/1.1" 200 -
2026-08-02 19:18:43,929 127.0.0.1 - - [02/Aug/2026 19:18:43] "GET /api/state HTTP/1.1" 200 -
2026-08-02 19:18:48,928 127.0.0.1 - - [02/Aug/2026 19:18:48] "GET /api/state HTTP/1.1" 200 -
2026-08-02 19:18:51,913 127.0.0.1 - - [02/Aug/2026 19:18:51] "GET /api/fleet HTTP/1.1" 200 -
2026-08-02 19:20:28,696 POST YES x1 KXDOGE15M-26AUG022230-30 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP
2026-08-02 19:20:53,823 POST NO x1 KXSOL15M-26AUG022230-30 @ 50¢ — DOWN: UP spike 0.20% conf=0.65 [RSI 78/81 overbought, sharp UP momentum fading]
2026-08-02 19:21:25,982 POST NO x1 KXETH15M-26AUG022230-30 @ 50¢ — ⚠ RSI 94 extreme overbought → fade DOWN
2026-08-02 19:21:49,882 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:21:54,860 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:21:54,860 staggering start by 20s
2026-08-02 19:21:59,877 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:21:59,877 staggering start by 40s
2026-08-02 19:22:02,504 POST YES x1 KXDOGE15M-26AUG022230-30 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP
2026-08-02 19:22:04,906 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:22:04,906 staggering start by 60s
2026-08-02 19:22:09,928 K4LSH1_OPS online — mode=AUTO dry_run=False stake=10¢ max_spend=100¢
2026-08-02 19:22:09,928 staggering start by 80s
2026-08-02 19:22:26,930 POST NO x1 KXSOL15M-26AUG022230-30 @ 50¢ — DOWN: UP spike 0.13% conf=0.65 [RSI(5m)=75 overbought, fading the spike]
2026-08-02 19:23:03,239 POST YES x1 KXETH15M-26AUG022230-30 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP
2026-08-02 19:23:11,364 skip KXXRP15M-26AUG022230-30: no swing (0.000% < 0.005%)
2026-08-02 19:23:40,530 skip KXBTC15M-26AUG022230-30: no swing (0.000% < 0.005%)
2026-08-02 19:25:31,546 POST YES x1 KXDOGE15M-26AUG022230-30 @ 50¢ — ⚠ RSI 0 extreme oversold → fade UP

34
bot.py
View File

@@ -276,6 +276,14 @@ def enriched_context(cfg, price, mom, mom_score, conn):
if "fee_fast" in ix:
lines.append(f"mempool fast fee: {ix['fee_fast']} sat/vB")
# Learned patterns from past bets
learnings = conn.execute("SELECT v FROM kv WHERE k='learnings'").fetchone()
if learnings:
try:
for l in json.loads(learnings[0])[-5:]:
lines.append(f"LEARNED: {l}")
except Exception: pass
lines.append("")
lines.append("You trade 15-minute crypto up/down binary contracts by FADING spikes:")
lines.append("- Sharp UP move → bet DOWN (mean reversion)")
@@ -284,6 +292,8 @@ def enriched_context(cfg, price, mom, mom_score, conn):
lines.append("- Consider: RSI extremes (>80 or <20 = high-prob fade), 24h range position, BTC macro direction")
lines.append("Reply with ONLY JSON: {\"vote\":\"UP\"|\"DOWN\"|\"SKIP\",\"conf\":0.0-1.0,\"why\":\"<10 words>\"}")
return "\n".join(lines)
def llm_vote(cfg, price, mom, mom_score, conn):
"""Two-tier: qwen3.5 (fast gate, 0.4s on MacBook) → ornith (deep verify on RTX 3070).
Gate handles clear signals; borderline calls escalate to ornith for final verdict."""
coin = cfg.get("coin","BTC")
@@ -495,6 +505,30 @@ def adapt_controls(conn, cfg):
conn.commit()
LOG.info(f"🔧 self-adapt: {ov.get('reason')} — override={json.dumps({k:v for k,v in ov.items() if k!='reason'})}")
_last_adapt_ts = time.time()
# extract learnings from resolved bets
lp = (
f"Recent trades: {json.dumps([{'side':r[0],'pnl':r[1],'price':r[2]} for r in resolved[:10]])}. "
"Extract 1-2 actionable trading patterns. Reply with ONLY JSON array of strings, e.g.: "
'["RSI < 20 UP bets won 3/4 times","DOGE fades after 0.1% spike lose 60%"]'
)
try:
r3 = requests.post(fast_url+"/api/generate",
json={"model": cfg.get("fast_llm_model","qwen3.5:4b"), "prompt": lp,
"stream": False, "think": False, "options": {"temperature": 0.1, "num_predict": 60}},
timeout=10)
txt3 = r3.json().get("response","")
s3, e3 = txt3.find("["), txt3.rfind("]")+1
new_learnings = json.loads(txt3[s3:e3]) if s3 >= 0 else []
if new_learnings:
existing = conn.execute("SELECT v FROM kv WHERE k='learnings'").fetchone()
old = json.loads(existing[0]) if existing else []
old.extend(new_learnings)
conn.execute("INSERT OR REPLACE INTO kv(k,v) VALUES ('learnings',?)",
(json.dumps(old[-20:]),)) # keep last 20
conn.commit()
LOG.info(f"🧠 learned: {new_learnings}")
except Exception:
pass
except Exception:
pass # silently skip on failure

BIN
btc.db Normal file

Binary file not shown.

28
btc.json Normal file
View File

@@ -0,0 +1,28 @@
{
"api_key_id": "28d5876b-2ece-4aa3-aa17-ec96e1e706eb",
"key_path": "/Users/drjones/kalshi-bot/kalshi_private_key.pem",
"mode": "AUTO",
"dry_run": false,
"stake_cents": 10,
"max_contracts": 1,
"daily_loss_cap_cents": 500,
"min_conf": 0.5,
"ollama_url": "http://10.30.20.186:11434",
"ollama_model": "ornith:latest",
"swing_threshold_pct": 0.005,
"learning": true,
"kill": false,
"coin": "BTC",
"series": "KXBTC15M",
"start_delay": 0,
"db_path": "btc.db",
"post_price_cents": 50,
"max_spend_cents": 100,
"decision_interval_sec": 300,
"start_delay_sec": 80,
"fast_llm_url": "http://localhost:11434",
"fast_llm_model": "qwen3.5:4b",
"deep_llm_url": "http://10.30.20.186:11434",
"deep_llm_model": "ornith:latest",
"color": "#f7931a"
}

0
btc.lock Normal file
View File

View File

@@ -201,7 +201,7 @@ h1{font-size:1rem;letter-spacing:3px}.dim{color:#0a5f2e}
<div class="fleet" id="cards"></div>
<div id="statusbar"></div>
<script>
const C={DOGE:"#c2a633",SOL:"#9945FF",ETH:"#627EEA"};
const C={DOGE:"#c2a633",SOL:"#9945FF",ETH:"#627EEA",XRP:"#00AAE4",BTC:"#f7931a"};
function fmt(c){return c<0?`<span class="dn">-${Math.abs(c)}¢</span>`:c>0?`<span class="up">+${c}¢</span>`:`<span class="skip">${c}¢</span>`}
function ago(ts){const d=Date.now()/1000-ts;return d<60?Math.floor(d)+'s':d<3600?Math.floor(d/60)+'m':Math.floor(d/3600)+'h'}
async function load(){
@@ -335,7 +335,7 @@ def toggle_dry():
@app.route("/api/fleet")
def fleet_api():
"""All active coins in one call — reads each coin's own DB."""
COINS = {"🐶DOGE": "#c2a633", "🔮SOL": "#9945FF", "💎ETH": "#627EEA"}
COINS = {"🐶DOGE": "#c2a633", "🔮SOL": "#9945FF", "💎ETH": "#627EEA", "🌊XRP": "#00AAE4", "₿BTC": "#f7931a"}
result = {}
for label, color in COINS.items():
coin = label[1:] # strip leading emoji
@@ -350,16 +350,34 @@ def fleet_api():
now = time.time()
ticks = [r[0] for r in conn.execute("SELECT price FROM ticks WHERE ts > ? ORDER BY ts", (now-1800,)).fetchall()]
last_price = ticks[-1] if ticks else None
# enriched per-coin data
# enriched per-coin data — compute RSI + momentum directly (no cross-import)
decs = [dict(r) for r in conn.execute("SELECT * FROM decisions ORDER BY id DESC LIMIT 3")]
ords = [dict(r) for r in conn.execute("SELECT * FROM orders ORDER BY id DESC LIMIT 5")]
live_bets = conn.execute("SELECT COUNT(*) FROM orders WHERE status IN ('placed','posted') AND dry=0").fetchone()[0]
total_bets = conn.execute("SELECT COUNT(*) FROM orders WHERE dry=0").fetchone()[0]
pnl = conn.execute("SELECT COALESCE(SUM(pnl_cents),0) FROM orders WHERE dry=0").fetchone()[0]
won = conn.execute("SELECT COUNT(*) FROM orders WHERE status='won' AND dry=0").fetchone()[0]
import sys; sys.path.insert(0, str(HOME))
from bot import rsi as _rsi, momentum as _mom
c_rsi = _rsi(conn); c_mom, c_score = _mom(conn)
# RSI
rows14 = conn.execute("SELECT price FROM ticks WHERE ts > ? ORDER BY ts DESC LIMIT 15", (now-900,)).fetchall()
c_rsi = None
if len(rows14) >= 15:
px = [r[0] for r in rows14]
gains = [max(0, b-a) for a, b in zip(px[:-1], px[1:])]
losses = [max(0, a-b) for a, b in zip(px[:-1], px[1:])]
ag = sum(gains)/14 if gains else 0; al = sum(losses)/14 if losses else 1e-9
c_rsi = 100 - 100/(1 + ag/al) if al else (100 if ag > 0 else 50)
# Momentum
rows_t = conn.execute("SELECT ts, price FROM ticks WHERE ts > ? ORDER BY ts", (now-1800,)).fetchall()
c_mom, c_score = "FLAT", 0.0
if len(rows_t) >= 4:
now_p = rows_t[-1][1]
ref5 = min(rows_t, key=lambda r: abs(r[0]-(now-300)))[1]
ref15 = min(rows_t, key=lambda r: abs(r[0]-(now-900)))[1]
r5 = (now_p-ref5)/ref5*100 if ref5 else 0; r15 = (now_p-ref15)/ref15*100 if ref15 else 0
sc = r5*0.6 + r15*0.4
if sc > 0.008: c_mom, c_score = "UP", abs(sc)
elif sc < -0.008: c_mom, c_score = "DOWN", abs(sc)
else: c_mom, c_score = "FLAT", abs(sc)
chg24 = c.get("chg24_cache", 0)
result[coin] = {"label": label, "color": color, "series": c["series"],
"price": last_price, "mode": c["mode"],

BIN
doge.db

Binary file not shown.

BIN
eth.db

Binary file not shown.

BIN
sol.db

Binary file not shown.

BIN
xrp.db Normal file

Binary file not shown.

29
xrp.json Normal file
View File

@@ -0,0 +1,29 @@
{
"api_key_id": "28d5876b-2ece-4aa3-aa17-ec96e1e706eb",
"key_path": "/Users/drjones/kalshi-bot/kalshi_private_key.pem",
"mode": "AUTO",
"dry_run": false,
"stake_cents": 10,
"max_contracts": 1,
"daily_loss_cap_cents": 500,
"min_conf": 0.5,
"ollama_url": "http://10.30.20.186:11434",
"ollama_model": "ornith:latest",
"swing_threshold_pct": 0.005,
"learning": true,
"kill": false,
"coin": "XRP",
"series": "KXXRP15M",
"start_delay": 0,
"db_path": "xrp.db",
"post_price_cents": 50,
"proxy": "http://10.30.20.154:3128",
"max_spend_cents": 100,
"decision_interval_sec": 300,
"start_delay_sec": 60,
"fast_llm_url": "http://localhost:11434",
"fast_llm_model": "qwen3.5:4b",
"deep_llm_url": "http://10.30.20.186:11434",
"deep_llm_model": "ornith:latest",
"color": "#00AAE4"
}

0
xrp.lock Normal file
View File