GoshawkVortexAI commited on
Commit
5a14709
·
verified ·
1 Parent(s): 4494a62

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -60
app.py CHANGED
@@ -1,11 +1,11 @@
1
  """
2
- RCEA Lite v1.0 — Regime-Conditioned Evidence Accumulation
3
- Causal HMM regime detection. No supervised ML.
4
  """
5
 
6
  import math, time, warnings
7
  from datetime import datetime, timezone
8
- from typing import List, Optional, Tuple
9
 
10
  import numpy as np
11
  import pandas as pd
@@ -16,7 +16,7 @@ from hmmlearn import hmm
16
  warnings.filterwarnings("ignore")
17
 
18
  # ---------- Config ----------
19
- HOSTS = ["https://api.binance.com", "https://data-api.binance.vision", "https://api.binance.us"]
20
  TIMEOUT, RETRIES, DELAY = 20, 3, 1.5
21
  COMMISSION, SLIPPAGE = 0.001, 0.0005
22
  COST = 2 * (COMMISSION + SLIPPAGE)
@@ -28,6 +28,9 @@ LEV_PRE = ("3L", "3S", "5L", "5S", "BULL", "BEAR", "UP", "DOWN")
28
  LEV_SUF = ("3LUSDT", "3SUSDT", "5LUSDT", "5SUSDT", "BULLUSDT", "BEARUSDT", "UPUSDT", "DOWNUSDT")
29
  FEAT_COLS = ["ret", "atr_norm", "ema_diff_norm", "efficiency", "rsi_dev", "dist_ma"]
30
 
 
 
 
31
 
32
  # ---------- Helpers ----------
33
  def _f(x):
@@ -64,69 +67,99 @@ def _rsi(c, p=14):
64
  return 100.0 - 100.0 / (1.0 + rs)
65
 
66
 
67
- # ---------- Binance ----------
68
  def _req(path, params, timeout=TIMEOUT):
69
  last = None
70
  for attempt in range(RETRIES):
71
- for base in HOSTS:
72
- try:
73
- r = requests.get(base + path, params=params, timeout=timeout,
74
- headers={"User-Agent": "RCEA-Lite/1.0"})
75
- if r.status_code == 200:
76
- d = r.json()
77
- if isinstance(d, list): return d
78
- last = f"non-list {type(d).__name__}"
79
- elif r.status_code in (418, 429):
80
- last = f"HTTP {r.status_code}"; time.sleep(5 * (attempt + 1))
81
- elif r.status_code >= 500:
82
- last = f"HTTP {r.status_code}"; time.sleep(DELAY * (attempt + 1))
83
- else:
84
- last = f"HTTP {r.status_code}: {r.text[:100]}"; break
85
- except requests.RequestException as e:
86
- last = f"{type(e).__name__}: {e}"
87
  time.sleep(DELAY * (attempt + 1))
88
  print(f"[RCEA] {path} failed: {last}", flush=True)
89
  return None
90
 
91
 
92
  def fetch_pairs(n=30):
93
- info = _req("/api/v3/exchangeInfo", {}, timeout=30)
94
- if not info: return []
95
- syms = set()
96
- for s in info:
97
- if s.get("status") != "TRADING" or s.get("quoteAsset") != "USDT": continue
98
- sym, base = s.get("symbol", ""), s.get("baseAsset", "").upper()
99
- if not sym or not base: continue
100
- if any(base.startswith(p) for p in LEV_PRE): continue
101
- if any(sym.upper().endswith(p) for p in LEV_SUF): continue
102
- syms.add(sym)
103
- tick = _req("/api/v3/ticker/24hr", {})
104
- if not tick: return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  cand = []
106
  for t in tick:
107
- s = t.get("symbol")
108
- if s not in syms: continue
109
- q = _f(t.get("quoteVolume"))
110
- if q and q > 0: cand.append((s, q))
 
 
111
  cand.sort(key=lambda x: x[1], reverse=True)
112
  out = [c[0] for c in cand[:n]]
113
- print(f"[RCEA] {len(out)} pairs resolved", flush=True)
114
  return out
115
 
116
 
117
- def fetch_klines(sym, interval, limit=1000):
118
- raw = _req("/api/v3/klines", {"symbol": sym, "interval": interval, "limit": min(limit, 1000)})
119
- if not raw: return None
120
- now = int(datetime.now(timezone.utc).timestamp() * 1000)
 
 
 
121
  rows = []
122
  for k in raw:
123
- if not isinstance(k, list) or len(k) < 7: continue
124
- if int(k[6]) >= now: continue
 
 
 
125
  o, h, l, c, v = _f(k[1]), _f(k[2]), _f(k[3]), _f(k[4]), _f(k[5])
126
- if None in (o, h, l, c, v) or min(o, h, l, c) <= 0: continue
 
127
  rows.append({"open": o, "high": h, "low": l, "close": c, "volume": v})
128
- if len(rows) < MIN_HMM: return None
129
- return pd.DataFrame(rows)
 
 
 
130
 
131
 
132
  # ---------- Features ----------
@@ -227,7 +260,7 @@ def evidence(f, rp, lam):
227
  return acc, pl, 1.0 - pl
228
 
229
 
230
- # ---------- Signal + backtest ----------
231
  def latest_signal(f, rp, acc, pl, ps, thr, mconf):
232
  i = len(f) - 1
233
  if i < MIN_SIG: return None
@@ -336,7 +369,7 @@ def coin_table(trades):
336
  return pd.DataFrame(rows).sort_values("TotalNet", ascending=False)
337
 
338
 
339
- # ---------- Pipeline (always 6 outputs) ----------
340
  def run(timeframe, n_coins, thr, lam, mconf, maxhold, progress=gr.Progress()):
341
  def err(msg, detail=""):
342
  return msg, detail, pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame()
@@ -347,15 +380,15 @@ def run(timeframe, n_coins, thr, lam, mconf, maxhold, progress=gr.Progress()):
347
  mconf = max(0.0, min(1.0, float(mconf)))
348
  maxhold = max(5, min(200, int(maxhold)))
349
 
350
- if timeframe not in ("15m", "1h", "4h", "1d"):
351
  return err("ERROR: Invalid timeframe")
352
 
353
- progress(0.02, desc="Fetching USDT pairs...")
354
  try: pairs = fetch_pairs(n_coins)
355
  except Exception as e: return err(f"ERROR fetching pairs: {e}")
356
  if not pairs:
357
- return err("ERROR: No USDT pairs found.\n\n"
358
- "Possible causes: Binance API blocked from this region, rate limit, or timeout. "
359
  "Check Space logs for `[RCEA] ...` messages.")
360
 
361
  signals, trades, analyzed, failed = [], [], 0, []
@@ -378,8 +411,8 @@ def run(timeframe, n_coins, thr, lam, mconf, maxhold, progress=gr.Progress()):
378
  print(f"[RCEA] {sym}: {e}", flush=True); failed.append(sym)
379
  progress(1.0, desc="Done")
380
 
381
- scan = ["# RCEA Lite Market Scan Report",
382
- f"**Timeframe:** {timeframe}",
383
  f"**Coins requested:** {n_coins}",
384
  f"**Analyzed:** {analyzed}",
385
  f"**Failed:** {len(failed)}",
@@ -443,9 +476,9 @@ def run(timeframe, n_coins, thr, lam, mconf, maxhold, progress=gr.Progress()):
443
 
444
 
445
  # ---------- UI ----------
446
- with gr.Blocks(title="RCEA Lite v1.0") as demo:
447
- gr.Markdown("# RCEA Lite v1.0 — Regime-Conditioned Evidence Accumulation\n"
448
- "HMM regime detection (causal forward filtering) · No supervised ML")
449
  with gr.Row():
450
  with gr.Column(scale=1):
451
  tf = gr.Dropdown(["15m", "1h", "4h", "1d"], value="1h", label="Timeframe")
@@ -465,8 +498,7 @@ with gr.Blocks(title="RCEA Lite v1.0") as demo:
465
  with gr.Tab("🪙 Coins"): coin_tbl = gr.Dataframe(interactive=False)
466
  btn.click(run, inputs=[tf, nc, thr_i, lam_i, mc_i, mh_i],
467
  outputs=[scan_out, met_out, sig_tbl, tr_tbl, reg_tbl, coin_tbl])
468
- gr.Markdown("---\n**Disclaimer:** Research tool. Net of 0.10% commission + 0.05% slippage per side. "
469
- "No supervised ML prediction is used.")
470
 
471
 
472
  if __name__ == "__main__":
 
1
  """
2
+ RCEA Lite v1.0 — OKX Edition
3
+ Regime-Conditioned Evidence Accumulation. Causal HMM. No supervised ML.
4
  """
5
 
6
  import math, time, warnings
7
  from datetime import datetime, timezone
8
+ from typing import List, Optional
9
 
10
  import numpy as np
11
  import pandas as pd
 
16
  warnings.filterwarnings("ignore")
17
 
18
  # ---------- Config ----------
19
+ OKX_BASE = "https://www.okx.com"
20
  TIMEOUT, RETRIES, DELAY = 20, 3, 1.5
21
  COMMISSION, SLIPPAGE = 0.001, 0.0005
22
  COST = 2 * (COMMISSION + SLIPPAGE)
 
28
  LEV_SUF = ("3LUSDT", "3SUSDT", "5LUSDT", "5SUSDT", "BULLUSDT", "BEARUSDT", "UPUSDT", "DOWNUSDT")
29
  FEAT_COLS = ["ret", "atr_norm", "ema_diff_norm", "efficiency", "rsi_dev", "dist_ma"]
30
 
31
+ # OKX timeframe mapping: UI -> OKX bar string
32
+ TF_MAP = {"15m": "15m", "1h": "1H", "4h": "4H", "1d": "1D"}
33
+
34
 
35
  # ---------- Helpers ----------
36
  def _f(x):
 
67
  return 100.0 - 100.0 / (1.0 + rs)
68
 
69
 
70
+ # ---------- OKX API ----------
71
  def _req(path, params, timeout=TIMEOUT):
72
  last = None
73
  for attempt in range(RETRIES):
74
+ try:
75
+ r = requests.get(OKX_BASE + path, params=params, timeout=timeout,
76
+ headers={"User-Agent": "RCEA-Lite/1.0"})
77
+ if r.status_code == 200:
78
+ j = r.json()
79
+ if j.get("code") == "0":
80
+ return j.get("data")
81
+ last = f"code={j.get('code')} msg={j.get('msg', '')[:100]}"
82
+ elif r.status_code in (429, 418):
83
+ last = f"HTTP {r.status_code}"; time.sleep(5 * (attempt + 1))
84
+ elif r.status_code >= 500:
85
+ last = f"HTTP {r.status_code}"; time.sleep(DELAY * (attempt + 1))
86
+ else:
87
+ last = f"HTTP {r.status_code}: {r.text[:100]}"; break
88
+ except requests.RequestException as e:
89
+ last = f"{type(e).__name__}: {e}"
90
  time.sleep(DELAY * (attempt + 1))
91
  print(f"[RCEA] {path} failed: {last}", flush=True)
92
  return None
93
 
94
 
95
  def fetch_pairs(n=30):
96
+ """Fetch USDT spot pairs ranked by 24h quote volume on OKX."""
97
+ # 1) Instruments: get tradable SPOT USDT pairs
98
+ inst = _req("/api/v5/public/instruments", {"instType": "SPOT"})
99
+ if not inst:
100
+ print("[RCEA] instruments unavailable", flush=True)
101
+ return []
102
+
103
+ usdt_syms = set()
104
+ for i in inst:
105
+ if i.get("state") != "live" or i.get("quoteCcy") != "USDT":
106
+ continue
107
+ sym, base = i.get("instId", ""), i.get("baseCcy", "").upper()
108
+ if not sym or not base:
109
+ continue
110
+ if any(base.startswith(p) for p in LEV_PRE):
111
+ continue
112
+ if any(sym.upper().endswith(p) for p in LEV_SUF):
113
+ continue
114
+ usdt_syms.add(sym)
115
+
116
+ if not usdt_syms:
117
+ print("[RCEA] no USDT spot pairs found", flush=True)
118
+ return []
119
+
120
+ # 2) Tickers: rank by 24h quote volume (volCcy24h)
121
+ tick = _req("/api/v5/market/tickers", {"instType": "SPOT"})
122
+ if not tick:
123
+ print("[RCEA] tickers unavailable", flush=True)
124
+ return []
125
+
126
  cand = []
127
  for t in tick:
128
+ sym = t.get("instId", "")
129
+ if sym not in usdt_syms:
130
+ continue
131
+ q = _f(t.get("volCcy24h"))
132
+ if q and q > 0:
133
+ cand.append((sym, q))
134
  cand.sort(key=lambda x: x[1], reverse=True)
135
  out = [c[0] for c in cand[:n]]
136
+ print(f"[RCEA] {len(out)} OKX USDT pairs resolved", flush=True)
137
  return out
138
 
139
 
140
+ def fetch_klines(inst_id, timeframe, limit=1000):
141
+ """Fetch OHLCV from OKX. Excludes unconfirmed candles (confirm=0)."""
142
+ bar = TF_MAP[timeframe]
143
+ raw = _req("/api/v5/market/candles", {"instId": inst_id, "bar": bar, "limit": "300"})
144
+ if not raw:
145
+ return None
146
+
147
  rows = []
148
  for k in raw:
149
+ if not isinstance(k, list) or len(k) < 9:
150
+ continue
151
+ # OKX kline format: [ts, o, h, l, c, vol, volCcy, volCcyQuote, confirm]
152
+ if k[8] != "1": # skip unconfirmed candle
153
+ continue
154
  o, h, l, c, v = _f(k[1]), _f(k[2]), _f(k[3]), _f(k[4]), _f(k[5])
155
+ if None in (o, h, l, c, v) or min(o, h, l, c) <= 0:
156
+ continue
157
  rows.append({"open": o, "high": h, "low": l, "close": c, "volume": v})
158
+
159
+ if len(rows) < MIN_HMM:
160
+ return None
161
+ # OKX returns newest first — reverse to chronological
162
+ return pd.DataFrame(rows[::-1]).reset_index(drop=True)
163
 
164
 
165
  # ---------- Features ----------
 
260
  return acc, pl, 1.0 - pl
261
 
262
 
263
+ # ---------- Signal + Backtest ----------
264
  def latest_signal(f, rp, acc, pl, ps, thr, mconf):
265
  i = len(f) - 1
266
  if i < MIN_SIG: return None
 
369
  return pd.DataFrame(rows).sort_values("TotalNet", ascending=False)
370
 
371
 
372
+ # ---------- Pipeline ----------
373
  def run(timeframe, n_coins, thr, lam, mconf, maxhold, progress=gr.Progress()):
374
  def err(msg, detail=""):
375
  return msg, detail, pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame()
 
380
  mconf = max(0.0, min(1.0, float(mconf)))
381
  maxhold = max(5, min(200, int(maxhold)))
382
 
383
+ if timeframe not in TF_MAP:
384
  return err("ERROR: Invalid timeframe")
385
 
386
+ progress(0.02, desc="Fetching OKX USDT pairs...")
387
  try: pairs = fetch_pairs(n_coins)
388
  except Exception as e: return err(f"ERROR fetching pairs: {e}")
389
  if not pairs:
390
+ return err("ERROR: No OKX USDT pairs found.\n\n"
391
+ "Possible causes: OKX API blocked from this region, rate limit, or timeout. "
392
  "Check Space logs for `[RCEA] ...` messages.")
393
 
394
  signals, trades, analyzed, failed = [], [], 0, []
 
411
  print(f"[RCEA] {sym}: {e}", flush=True); failed.append(sym)
412
  progress(1.0, desc="Done")
413
 
414
+ scan = ["# RCEA Lite — OKX Market Scan",
415
+ f"**Timeframe:** {timeframe} | **OKX bar:** {TF_MAP[timeframe]}",
416
  f"**Coins requested:** {n_coins}",
417
  f"**Analyzed:** {analyzed}",
418
  f"**Failed:** {len(failed)}",
 
476
 
477
 
478
  # ---------- UI ----------
479
+ with gr.Blocks(title="RCEA Lite v1.0 — OKX") as demo:
480
+ gr.Markdown("# RCEA Lite v1.0 — OKX Edition\n"
481
+ "HMM regime detection (causal forward filtering) · No supervised ML · Data: OKX")
482
  with gr.Row():
483
  with gr.Column(scale=1):
484
  tf = gr.Dropdown(["15m", "1h", "4h", "1d"], value="1h", label="Timeframe")
 
498
  with gr.Tab("🪙 Coins"): coin_tbl = gr.Dataframe(interactive=False)
499
  btn.click(run, inputs=[tf, nc, thr_i, lam_i, mc_i, mh_i],
500
  outputs=[scan_out, met_out, sig_tbl, tr_tbl, reg_tbl, coin_tbl])
501
+ gr.Markdown("---\n**Data source:** OKX public REST API. Net of 0.10% commission + 0.05% slippage per side.")
 
502
 
503
 
504
  if __name__ == "__main__":