#!/usr/bin/env python3 """K4LSH1_OPS — dashboard. Hacker console on :5053. Controls write config.json (bot hot-reloads).""" import json, sqlite3, time, threading from pathlib import Path from flask import Flask, jsonify, request HOME = Path(__file__).parent CFG_PATH = HOME / "config.json" DB_PATH = HOME / "state.db" app = Flask(__name__) HTML = r"""K4LSH1_OPS

K4LSH1_OPS

BTC/USD — kraken

$—

active market

ticker
question
closes in
YES —¢NO —¢

account

balance
open positions
bets placed (session)

AI decision stream — ollama local

system events

controls

STRATEGY MODE
MAX STAKE / BET: 50¢

order blotter

MODE
BTC
24H
FEAR/GREED
OPEN BETS
RESOLVED P&L
WIN RATE
AI CONF GATE
NEXT MARKET
""" FLEET_HTML = r"""K4LSH1 FLEET

K4LSH1_FLEET

""" def cfg(): return json.loads(CFG_PATH.read_text()) @app.route("/") def index(): return HTML @app.route("/api/state") def state(): c = cfg() conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row 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 age = None row = conn.execute("SELECT MAX(ts) FROM ticks").fetchone() if row and row[0]: age = int(now - row[0]) dec = [{"t": time.strftime("%H:%M:%S", time.localtime(r["ts"])), "ticker": r["ticker"], "momentum": r["momentum"], "llm_vote": r["llm_vote"], "llm_conf": r["llm_conf"], "llm_why": r["llm_why"], "final": r["final"], "reason": r["reason"]} for r in conn.execute("SELECT * FROM decisions ORDER BY id DESC LIMIT 30")] orders = [{"t": time.strftime("%H:%M:%S", time.localtime(r["ts"])), "ticker": r["ticker"], "side": r["side"], "count": r["count"], "price_cents": r["price_cents"], "dry": r["dry"]} for r in conn.execute("SELECT * FROM orders ORDER BY id DESC LIMIT 20")] events = [{"t": time.strftime("%H:%M:%S", time.localtime(r[0])), "level": r[1], "msg": r[2]} for r in conn.execute("SELECT * FROM events ORDER BY ts DESC LIMIT 12")] bets = conn.execute("SELECT COUNT(*) FROM orders").fetchone()[0] perf_rows = conn.execute("SELECT status, COALESCE(pnl_cents,0) FROM orders WHERE status IN ('won','lost')").fetchall() won = sum(1 for s,p in perf_rows if s == 'won') pnl = sum(p for s,p in perf_rows) open_bets = conn.execute("SELECT COUNT(*) FROM orders WHERE status IN ('placed','posted')").fetchone()[0] kv = {r[0]: r[1] for r in conn.execute("SELECT k, v FROM kv")} conn.close() # live market + balance (proxy through bot's client without importing heavy code) market, balance, positions = {}, None, 0 try: import sys sys.path.insert(0, str(HOME)) from bot import Kalshi, btc_price, DEFAULT_CFG kx = Kalshi(c["api_key_id"], c["key_path"]) mkts = kx.markets(c.get("series", "KXBTC15M"), "open", 2) if mkts: m = sorted(mkts, key=lambda x: x["close_time"])[-1] from datetime import datetime close_ts = datetime.fromisoformat(m["close_time"].replace("Z","+00:00")).timestamp() book = kx.orderbook(m["ticker"]) market = {"ticker": m["ticker"], "title": m.get("title",""), "mins_left": (close_ts-time.time())/60, "yes_ask": (book.get("yes") or [[None]])[0][0], "no_ask": (book.get("no") or [[None]])[0][0]} b = kx.balance() balance = b.get("balance", 0) # available cash (cents) positions = len(kx.positions()) except Exception as e: market = {"error": str(e)[:80]} if last_price is None: try: from bot import btc_price last_price = btc_price() except Exception: pass try: from bot import intel as bot_intel ix = bot_intel() except Exception: ix = {} # momentum label mom = "FLAT" if len(ticks) > 10: r = (ticks[-1]-ticks[0])/ticks[0]*100 mom = f"{'UP' if r>0 else 'DOWN'} {abs(r):.3f}%/30m" return jsonify({"cfg": c, "btc": last_price, "price_age": age, "spark": ticks[-120:], "momentum": mom, "market": market, "balance": balance, "positions": positions, "bets_session": bets, "decisions": dec, "orders": orders, "events": events, "intel": ix, "perf": {"open": open_bets, "resolved": len(perf_rows), "won": won, "pnl": pnl, "min_conf": kv.get("min_conf", c.get("min_conf", 0.55))}}) @app.route("/api/control", methods=["POST"]) def control(): c = cfg() d = request.get_json(force=True) if d.get("mode") in ("AUTO","DOWN_SPAM","UP_SPAM","OFF"): c["mode"] = d["mode"] if isinstance(d.get("stake_cents"), int): c["stake_cents"] = max(1, min(99, d["stake_cents"])) c["kill"] = False CFG_PATH.write_text(json.dumps(c, indent=1)) return jsonify({"ok": True}) @app.route("/api/toggle-dry", methods=["POST"]) def toggle_dry(): c = cfg(); c["dry_run"] = not c["dry_run"]; c["kill"] = False CFG_PATH.write_text(json.dumps(c, indent=1)) return jsonify({"ok": True, "dry_run": c["dry_run"]}) @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"} result = {} for label, color in COINS.items(): coin = label[1:] # strip leading emoji fpath = HOME / f"{coin.lower()}.json" if not fpath.exists(): continue c = json.loads(fpath.read_text()) if c.get("mode","OFF") == "OFF": continue db_path = HOME / c.get("db_path", f"{coin.lower()}.db") if not db_path.exists(): continue conn = sqlite3.connect(str(db_path)) conn.row_factory = sqlite3.Row 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 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) chg24 = c.get("chg24_cache", 0) result[coin] = {"label": label, "color": color, "series": c["series"], "price": last_price, "mode": c["mode"], "live_bets": live_bets, "total_bets": total_bets, "pnl": pnl, "won": won, "stake": c.get("stake_cents", 10), "swing": c.get("swing_threshold_pct", 0.005), "rsi": c_rsi, "mom": f"{c_mom} {c_score:.3f}%" if c_mom != "FLAT" else c_mom, "chg24": chg24, "decisions": decs, "orders": ords} conn.close() return jsonify(result) @app.route("/fleet") def fleet_page(): return FLEET_HTML def kill(): c = cfg(); c["kill"] = True CFG_PATH.write_text(json.dumps(c, indent=1)) return jsonify({"ok": True}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5053, debug=False)