GoshawkVortexAI commited on
Commit
973dcb4
·
verified ·
1 Parent(s): 5a14709

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -26
app.py CHANGED
@@ -23,13 +23,13 @@ COST = 2 * (COMMISSION + SLIPPAGE)
23
  REGIME_NAMES = ["TREND", "MEAN_REVERSION", "COMPRESSION"]
24
  HMM_K = 3
25
  TRAIN_FRAC, PURGE, EMBARGO = 0.60, 5, 3
26
- MIN_HMM, MIN_SIG = 200, 100
27
  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
- # OKX timeframe mapping: UI -> OKX bar string
32
  TF_MAP = {"15m": "15m", "1h": "1H", "4h": "4H", "1d": "1D"}
 
33
 
34
 
35
  # ---------- Helpers ----------
@@ -94,7 +94,6 @@ def _req(path, params, timeout=TIMEOUT):
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)
@@ -117,7 +116,6 @@ def fetch_pairs(n=30):
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)
@@ -137,29 +135,58 @@ def fetch_pairs(n=30):
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 ----------
@@ -279,7 +306,13 @@ def backtest(sym, f, rp, acc, pl, ps, thr, mconf, maxhold):
279
  atr = f["atr"].values
280
  trades = []; in_pos = False; ei = eprice = d = sp = tp = 0
281
  reg = ""; rconf = 0.0
282
- start = max(MIN_SIG, int(n * TRAIN_FRAC) + PURGE + EMBARGO)
 
 
 
 
 
 
283
  for i in range(start, n):
284
  if not in_pos:
285
  p = rp[i]; dom = int(np.argmax(p)); conf = float(p[dom])
@@ -420,7 +453,8 @@ def run(timeframe, n_coins, thr, lam, mconf, maxhold, progress=gr.Progress()):
420
  f"**Threshold:** {thr} | **λ:** {lam} | **MinConf:** {mconf} | **MaxHold:** {maxhold}",
421
  f"**UTC:** {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}"]
422
  if len(trades) < 30:
423
- scan.append(f"\n⚠️ Only {len(trades)} trades — target ≥30. Metrics are descriptive only.")
 
424
  scan_report = "\n".join(scan)
425
 
426
  if signals:
@@ -498,7 +532,8 @@ with gr.Blocks(title="RCEA Lite v1.0 — OKX") as demo:
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__":
 
23
  REGIME_NAMES = ["TREND", "MEAN_REVERSION", "COMPRESSION"]
24
  HMM_K = 3
25
  TRAIN_FRAC, PURGE, EMBARGO = 0.60, 5, 3
26
+ MIN_HMM, MIN_SIG = 150, 80
27
  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
  TF_MAP = {"15m": "15m", "1h": "1H", "4h": "4H", "1d": "1D"}
32
+ TARGET_BARS = 1000
33
 
34
 
35
  # ---------- Helpers ----------
 
94
 
95
  def fetch_pairs(n=30):
96
  """Fetch USDT spot pairs ranked by 24h quote volume on OKX."""
 
97
  inst = _req("/api/v5/public/instruments", {"instType": "SPOT"})
98
  if not inst:
99
  print("[RCEA] instruments unavailable", flush=True)
 
116
  print("[RCEA] no USDT spot pairs found", flush=True)
117
  return []
118
 
 
119
  tick = _req("/api/v5/market/tickers", {"instType": "SPOT"})
120
  if not tick:
121
  print("[RCEA] tickers unavailable", flush=True)
 
135
  return out
136
 
137
 
138
+ def fetch_klines(inst_id, timeframe, target_bars=TARGET_BARS):
139
+ """
140
+ Fetch OHLCV from OKX with pagination.
141
+ OKX returns max 300 bars per request, newest first.
142
+ Uses 'after' = oldest timestamp so far to page back in time.
143
+ """
144
  bar = TF_MAP[timeframe]
145
+ all_rows = []
146
+ after = None
147
+ max_pages = 10
148
+
149
+ for page in range(max_pages):
150
+ params = {"instId": inst_id, "bar": bar, "limit": "300"}
151
+ if after:
152
+ params["after"] = str(after)
153
+ raw = _req("/api/v5/market/candles", params)
154
+ if not raw:
155
+ break
156
+
157
+ got_new = 0
158
+ for k in raw:
159
+ if not isinstance(k, list) or len(k) < 9:
160
+ continue
161
+ if k[8] != "1": # skip unconfirmed
162
+ continue
163
+ o, h, l, c, v = _f(k[1]), _f(k[2]), _f(k[3]), _f(k[4]), _f(k[5])
164
+ if None in (o, h, l, c, v) or min(o, h, l, c) <= 0:
165
+ continue
166
+ all_rows.append({"ts": int(k[0]),
167
+ "open": o, "high": h, "low": l, "close": c, "volume": v})
168
+ got_new += 1
169
+
170
+ if got_new == 0:
171
+ break
172
 
173
+ try:
174
+ after = int(raw[-1][0])
175
+ except (IndexError, ValueError):
176
+ break
177
+
178
+ if len(all_rows) >= target_bars:
179
+ break
180
+ time.sleep(0.12)
 
 
 
181
 
182
+ if len(all_rows) < MIN_HMM:
183
  return None
184
+
185
+ df = (pd.DataFrame(all_rows)
186
+ .drop_duplicates(subset=["ts"])
187
+ .sort_values("ts")
188
+ .reset_index(drop=True))
189
+ return df[["open", "high", "low", "close", "volume"]]
190
 
191
 
192
  # ---------- Features ----------
 
306
  atr = f["atr"].values
307
  trades = []; in_pos = False; ei = eprice = d = sp = tp = 0
308
  reg = ""; rconf = 0.0
309
+
310
+ # Start right after HMM training window + purge + embargo.
311
+ train_end = int(n * TRAIN_FRAC)
312
+ start = min(n - 2, train_end + PURGE + EMBARGO)
313
+ if start < MIN_SIG:
314
+ start = MIN_SIG
315
+
316
  for i in range(start, n):
317
  if not in_pos:
318
  p = rp[i]; dom = int(np.argmax(p)); conf = float(p[dom])
 
453
  f"**Threshold:** {thr} | **λ:** {lam} | **MinConf:** {mconf} | **MaxHold:** {maxhold}",
454
  f"**UTC:** {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}"]
455
  if len(trades) < 30:
456
+ scan.append(f"\n⚠️ Only {len(trades)} trades — target ≥30. "
457
+ f"Try lowering threshold to ~0.10 or increasing coins.")
458
  scan_report = "\n".join(scan)
459
 
460
  if signals:
 
532
  with gr.Tab("🪙 Coins"): coin_tbl = gr.Dataframe(interactive=False)
533
  btn.click(run, inputs=[tf, nc, thr_i, lam_i, mc_i, mh_i],
534
  outputs=[scan_out, met_out, sig_tbl, tr_tbl, reg_tbl, coin_tbl])
535
+ gr.Markdown("---\n**Data source:** OKX public REST API. "
536
+ "Net of 0.10% commission + 0.05% slippage per side.")
537
 
538
 
539
  if __name__ == "__main__":