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:
Binary file not shown.
Binary file not shown.
38
bot.log
38
bot.log
@@ -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
34
bot.py
@@ -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
|
||||
|
||||
|
||||
28
btc.json
Normal file
28
btc.json
Normal 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"
|
||||
}
|
||||
30
dashboard.py
30
dashboard.py
@@ -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"],
|
||||
|
||||
29
xrp.json
Normal file
29
xrp.json
Normal 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"
|
||||
}
|
||||
Reference in New Issue
Block a user