GoshawkVortexAI commited on
Commit
e46c461
·
verified ·
1 Parent(s): 1c31a80

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -20
app.py CHANGED
@@ -510,14 +510,17 @@ def make_features(df: pd.DataFrame):
510
  return f.replace([np.inf, -np.inf], np.nan), atr
511
 
512
 
513
- def _models():
 
 
 
 
 
 
514
  return {
515
- "hgb": HistGradientBoostingClassifier(max_depth=3, learning_rate=0.04, max_iter=250,
516
- l2_regularization=2.0, min_samples_leaf=40,
517
- random_state=42),
518
- "et": ExtraTreesClassifier(n_estimators=300, max_depth=6, min_samples_leaf=30,
519
- max_features=0.5, n_jobs=-1, random_state=42),
520
- "lr": make_pipeline(StandardScaler(), LogisticRegression(C=0.05, max_iter=500)),
521
  }
522
 
523
 
@@ -537,14 +540,14 @@ def _select(Xtr, ftr, k, thr=0.95):
537
  return keep
538
 
539
 
540
- def _fit_predict(Xtr, ytr, Xte, w=None, ftr=None, k=0, with_train=False):
541
  """Ensemble: HGB + ExtraTrees + Logistic -> olasılık ortalaması.
542
  k>0: fold-içi özellik seçimi. with_train: eğitim verisi üzerindeki olasılıkları da döndür (aşırı uyum ölçümü için)."""
543
  if k and ftr is not None:
544
  sel = _select(Xtr, ftr, k)
545
  Xtr, Xte = Xtr[sel], Xte[sel]
546
  ps, pt = [], []
547
- for name, m in _models().items():
548
  if name == "lr":
549
  A, B = Xtr.fillna(0), Xte.fillna(0)
550
  m.fit(A, ytr, logisticregression__sample_weight=w)
@@ -598,7 +601,71 @@ def _nested(p, f, sizes, horizon, fee, grid=(0.52, 0.54, 0.56, 0.58, 0.60, 0.62)
598
  return _stats(np.concatenate(trades) if trades else np.array([]))
599
 
600
 
601
- def _walk(X, Y, F, W, horizon, n_folds, k):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
602
  """Purged expanding-window walk-forward. Döner: OOS olasılık, satır indeksi, fold boyutları, ort. train-test AUC farkı."""
603
  n = len(X)
604
  edges = np.linspace(int(n * 0.4), n, n_folds + 1).astype(int)
@@ -608,7 +675,7 @@ def _walk(X, Y, F, W, horizon, n_folds, k):
608
  if tr_end < 80:
609
  continue
610
  p, ptr = _fit_predict(X.iloc[:tr_end], Y.iloc[:tr_end], X.iloc[te0:te1],
611
- W[:tr_end], F[:tr_end], k, with_train=True)
612
  try:
613
  gaps.append(roc_auc_score(Y.iloc[:tr_end], ptr) - roc_auc_score(Y.iloc[te0:te1], p))
614
  except ValueError:
@@ -623,7 +690,7 @@ def _walk(X, Y, F, W, horizon, n_folds, k):
623
 
624
 
625
  def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int = 5, ext=None,
626
- max_feat: int = 40):
627
  feats, atr = make_features(df)
628
  n_ext = 0
629
  if ext is not None:
@@ -663,14 +730,20 @@ def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int =
663
  n_dev = int(n_all * 0.8)
664
  Xd, Yd, Fd, Wd = X.iloc[:n_dev], Y.iloc[:n_dev], F[:n_dev], W[:n_dev]
665
 
666
- oos_p, oos_idx, sizes, gap = _walk(Xd, Yd, Fd, Wd, horizon, n_folds, max_feat)
 
 
 
 
 
 
667
  yo, fo = Yd.values[oos_idx], Fd[oos_idx]
668
 
669
  # Teşhis (ablation): yalnızca temel (fiyat/hacim) özelliklerle aynı test
670
  base_cols = [c for c in cols if not c.startswith(("ts_", "dv_"))]
671
  auc_base = None
672
  if len(base_cols) < len(cols):
673
- pb, ib, _, _ = _walk(Xd[base_cols], Yd, Fd, Wd, horizon, n_folds, max_feat)
674
  try:
675
  auc_base = float(roc_auc_score(Yd.values[ib], pb))
676
  except ValueError:
@@ -701,7 +774,7 @@ def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int =
701
  ho = None
702
  tr_end = n_dev - horizon
703
  if tr_end >= 200 and n_all - n_dev >= 100:
704
- ph = _fit_predict(X.iloc[:tr_end], Y.iloc[:tr_end], X.iloc[n_dev:], W[:tr_end], F[:tr_end], max_feat)
705
  try:
706
  auc_h = float(roc_auc_score(Y.values[n_dev:], ph))
707
  except ValueError:
@@ -710,7 +783,7 @@ def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int =
710
  **_stats(_trades(ph, F[n_dev:], horizon, best["thr"], fee))}
711
 
712
  # Son model: tüm veriyle, canlı tahmin
713
- final_p = _fit_predict(X, Y, feats.iloc[[-1]][cols].ffill().fillna(0), W, F, max_feat)
714
  p_up = float(final_p[0])
715
 
716
  price, a = float(close.iloc[-1]), float(atr.iloc[-1])
@@ -721,7 +794,7 @@ def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int =
721
  "center": center, "low": center - move, "high": center + move,
722
  "thr": best["thr"], "bt": best, "n_oos": len(oos_p), "fee": fee,
723
  "n_ext": n_ext, "n_feat": len(cols), "auc_base": auc_base, "max_feat": max_feat,
724
- "gap": gap, "auc_p": auc_p, "nested": nested, "holdout": ho,
725
  }
726
 
727
 
@@ -772,7 +845,7 @@ def _root(e):
772
  return f"{type(e).__name__}: {e}"
773
 
774
 
775
- def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True, max_feat=40):
776
  try:
777
  ohlcv, deep, ind, extra = asyncio.run(run_async(symbol, timeframe, exchange, horizon, limit))
778
  df = to_df(ohlcv)
@@ -783,7 +856,8 @@ def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True, max_feat=40
783
  ext = indicator_features(ind, df)
784
  if dfeat is not None:
785
  ext = dfeat if ext is None else pd.concat([ext, dfeat], axis=1)
786
- res = predict(df, int(horizon), ext=ext, max_feat=int(max_feat))
 
787
  except BaseException as e: # noqa: BLE001
788
  import traceback
789
  traceback.print_exc()
@@ -799,6 +873,16 @@ def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True, max_feat=40
799
  ("Hold-out AUC > 0.50", ho is not None and ho["auc"] > 0.50),
800
  ("Hold-out net getiri > 0 (≥20 işlem)", ho is not None and ho["n"] >= 20 and ho["mean"] > 0),
801
  ]
 
 
 
 
 
 
 
 
 
 
802
  passed = sum(ok for _, ok in checks)
803
  edge = f"✅ {passed}/5 kriter" if passed >= 4 else f"⚠️ {passed}/5 kriter — kanıtlanamadı"
804
  checks_md = "**Aşırı uyum / güvenilirlik göstergesi**\n\n" + "\n".join(
@@ -817,6 +901,7 @@ def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True, max_feat=40
817
  | Ort. net getiri / işlem | {bt['mean']:.3%} (t = {bt['t']:.2f}, ücret {res['fee']:.2%}/işlem) |
818
  | Özellik sayısı | {res['n_feat']} (Tickscope/türev: {res['n_ext']}) → fold başına en fazla {res['max_feat']} seçilir |
819
  | AUC yalnızca temel özelliklerle | {('%.3f' % res['auc_base']) if res['auc_base'] is not None else '—'} (ek veri kaynakları yardım ediyor mu?) |
 
820
  | Train–test AUC farkı (aşırı uyum) | {res['gap']:.3f} |
821
  | AUC anlamlılığı (tek yönlü p) | {res['auc_p']:.3f} |
822
  | Nested (dürüst) net getiri / işlem | {nest['mean']:.3%} (t = {nest['t']:.2f}, {nest['n']} işlem) |
@@ -856,12 +941,16 @@ with gr.Blocks(title="Tickscope Tahmin") as demo:
856
  limit = gr.Slider(300, 20000, value=5000, step=100, label="Mum sayısı (1000 üstü sayfalanır)")
857
  use_deriv = gr.Checkbox(True, label="Türev verisi (LiquidVision MCP: funding/OI) — mumları geçmişin kapsadığı aralığa kırpabilir")
858
  max_feat = gr.Slider(10, 150, value=40, step=5, label="Maks. özellik (fold içinde seçilir; az = daha az aşırı uyum)")
 
 
 
859
  btn = gr.Button("Tahmin et", variant="primary")
860
  out_md = gr.Markdown()
861
  out_plot = gr.Plot()
862
  with gr.Accordion("Tickscope ham çıktı (deep_analyze / indicators)", open=False):
863
  out_raw = gr.Code(language="json")
864
- btn.click(run, [symbol, timeframe, exchange, horizon, limit, use_deriv, max_feat], [out_md, out_plot, out_raw])
865
 
866
  if __name__ == "__main__":
867
  demo.launch()
 
 
510
  return f.replace([np.inf, -np.inf], np.nan), atr
511
 
512
 
513
+ def _models(hp=None):
514
+ """hp (Optuna'dan): {"hgb": {...}, "et": {...}, "lr": {"C": ...}}; yoksa varsayılanlar."""
515
+ hp = hp or {}
516
+ h = {"max_depth": 3, "learning_rate": 0.04, "max_iter": 250, "l2_regularization": 2.0,
517
+ "min_samples_leaf": 40, **hp.get("hgb", {})}
518
+ e = {"n_estimators": 300, "max_depth": 6, "min_samples_leaf": 30, "max_features": 0.5, **hp.get("et", {})}
519
+ c = hp.get("lr", {}).get("C", 0.05)
520
  return {
521
+ "hgb": HistGradientBoostingClassifier(**h, random_state=42),
522
+ "et": ExtraTreesClassifier(**e, n_jobs=-1, random_state=42),
523
+ "lr": make_pipeline(StandardScaler(), LogisticRegression(C=c, max_iter=500)),
 
 
 
524
  }
525
 
526
 
 
540
  return keep
541
 
542
 
543
+ def _fit_predict(Xtr, ytr, Xte, w=None, ftr=None, k=0, with_train=False, hp=None):
544
  """Ensemble: HGB + ExtraTrees + Logistic -> olasılık ortalaması.
545
  k>0: fold-içi özellik seçimi. with_train: eğitim verisi üzerindeki olasılıkları da döndür (aşırı uyum ölçümü için)."""
546
  if k and ftr is not None:
547
  sel = _select(Xtr, ftr, k)
548
  Xtr, Xte = Xtr[sel], Xte[sel]
549
  ps, pt = [], []
550
+ for name, m in _models(hp).items():
551
  if name == "lr":
552
  A, B = Xtr.fillna(0), Xte.fillna(0)
553
  m.fit(A, ytr, logisticregression__sample_weight=w)
 
601
  return _stats(np.concatenate(trades) if trades else np.array([]))
602
 
603
 
604
+ class _Fixed:
605
+ """En iyi parametreleri aynı `suggest_*` arayüzüyle geri üretmek için."""
606
+ def __init__(self, params):
607
+ self.p = params
608
+
609
+ def suggest_int(self, name, *a, **k):
610
+ return self.p[name]
611
+
612
+ def suggest_float(self, name, *a, **k):
613
+ return self.p[name]
614
+
615
+
616
+ def _hp_space(t):
617
+ return {
618
+ "hgb": {"max_depth": t.suggest_int("hgb_depth", 2, 5),
619
+ "learning_rate": t.suggest_float("hgb_lr", 0.01, 0.1, log=True),
620
+ "max_iter": t.suggest_int("hgb_iter", 100, 400, step=50),
621
+ "l2_regularization": t.suggest_float("hgb_l2", 0.5, 10.0, log=True),
622
+ "min_samples_leaf": t.suggest_int("hgb_leaf", 20, 200, step=20)},
623
+ "et": {"max_depth": t.suggest_int("et_depth", 3, 10),
624
+ "min_samples_leaf": t.suggest_int("et_leaf", 10, 100, step=10),
625
+ "max_features": t.suggest_float("et_mf", 0.2, 0.8)},
626
+ "lr": {"C": t.suggest_float("lr_C", 0.005, 0.5, log=True)},
627
+ }
628
+
629
+
630
+ def _tune(X, Y, F, W, horizon, n_trials, timeout=240):
631
+ """Optuna: hiperparametre + özellik sayısı (k) ayarı. YALNIZCA geliştirme verisinin ilk %40'ında
632
+ (iç walk-forward, 3 dilim) yapılır; asıl walk-forward OOS ve hold-out'a hiç dokunmaz.
633
+ Amaç: ortalama AUC − 0.5·std (kararsız/şanslı ayarları cezalandırır)."""
634
+ try:
635
+ import optuna
636
+ except Exception: # noqa: BLE001
637
+ return {"error": "optuna kurulu değil (pip install optuna); varsayılan ayarlar kullanıldı"}
638
+ m = int(len(X) * 0.4)
639
+ if m < 500:
640
+ return {"error": f"Ayar bölgesi çok küçük ({m} bar < 500); Optuna atlandı, varsayılanlar kullanıldı"}
641
+ Xt, Yt, Ft, Wt = X.iloc[:m], Y.iloc[:m], F[:m], W[:m]
642
+ edges = np.linspace(int(m * 0.4), m, 4).astype(int)
643
+
644
+ def obj(trial):
645
+ hp = _hp_space(trial)
646
+ k = trial.suggest_int("k", 10, 80, step=10)
647
+ aucs = []
648
+ for j in range(3):
649
+ tr_end, te0, te1 = edges[j] - horizon, edges[j], edges[j + 1]
650
+ if tr_end < 80:
651
+ continue
652
+ p = _fit_predict(Xt.iloc[:tr_end], Yt.iloc[:tr_end], Xt.iloc[te0:te1],
653
+ Wt[:tr_end], Ft[:tr_end], k, hp=hp)
654
+ try:
655
+ aucs.append(roc_auc_score(Yt.iloc[te0:te1], p))
656
+ except ValueError:
657
+ pass
658
+ return float(np.mean(aucs) - 0.5 * np.std(aucs)) if aucs else 0.5
659
+
660
+ optuna.logging.set_verbosity(optuna.logging.WARNING)
661
+ study = optuna.create_study(direction="maximize", sampler=optuna.samplers.TPESampler(seed=42))
662
+ study.optimize(obj, n_trials=int(n_trials), timeout=timeout)
663
+ bp = study.best_params
664
+ return {"hp": _hp_space(_Fixed(bp)), "k": int(bp["k"]), "score": float(study.best_value),
665
+ "trials": len(study.trials), "region": m}
666
+
667
+
668
+ def _walk(X, Y, F, W, horizon, n_folds, k, hp=None):
669
  """Purged expanding-window walk-forward. Döner: OOS olasılık, satır indeksi, fold boyutları, ort. train-test AUC farkı."""
670
  n = len(X)
671
  edges = np.linspace(int(n * 0.4), n, n_folds + 1).astype(int)
 
675
  if tr_end < 80:
676
  continue
677
  p, ptr = _fit_predict(X.iloc[:tr_end], Y.iloc[:tr_end], X.iloc[te0:te1],
678
+ W[:tr_end], F[:tr_end], k, with_train=True, hp=hp)
679
  try:
680
  gaps.append(roc_auc_score(Y.iloc[:tr_end], ptr) - roc_auc_score(Y.iloc[te0:te1], p))
681
  except ValueError:
 
690
 
691
 
692
  def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int = 5, ext=None,
693
+ max_feat: int = 40, use_optuna: bool = False, n_trials: int = 25):
694
  feats, atr = make_features(df)
695
  n_ext = 0
696
  if ext is not None:
 
730
  n_dev = int(n_all * 0.8)
731
  Xd, Yd, Fd, Wd = X.iloc[:n_dev], Y.iloc[:n_dev], F[:n_dev], W[:n_dev]
732
 
733
+ hp, tuned = None, None
734
+ if use_optuna:
735
+ tuned = _tune(Xd, Yd, Fd, Wd, horizon, n_trials)
736
+ if "error" not in tuned:
737
+ hp, max_feat = tuned["hp"], tuned["k"]
738
+
739
+ oos_p, oos_idx, sizes, gap = _walk(Xd, Yd, Fd, Wd, horizon, n_folds, max_feat, hp)
740
  yo, fo = Yd.values[oos_idx], Fd[oos_idx]
741
 
742
  # Teşhis (ablation): yalnızca temel (fiyat/hacim) özelliklerle aynı test
743
  base_cols = [c for c in cols if not c.startswith(("ts_", "dv_"))]
744
  auc_base = None
745
  if len(base_cols) < len(cols):
746
+ pb, ib, _, _ = _walk(Xd[base_cols], Yd, Fd, Wd, horizon, n_folds, max_feat, hp)
747
  try:
748
  auc_base = float(roc_auc_score(Yd.values[ib], pb))
749
  except ValueError:
 
774
  ho = None
775
  tr_end = n_dev - horizon
776
  if tr_end >= 200 and n_all - n_dev >= 100:
777
+ ph = _fit_predict(X.iloc[:tr_end], Y.iloc[:tr_end], X.iloc[n_dev:], W[:tr_end], F[:tr_end], max_feat, hp=hp)
778
  try:
779
  auc_h = float(roc_auc_score(Y.values[n_dev:], ph))
780
  except ValueError:
 
783
  **_stats(_trades(ph, F[n_dev:], horizon, best["thr"], fee))}
784
 
785
  # Son model: tüm veriyle, canlı tahmin
786
+ final_p = _fit_predict(X, Y, feats.iloc[[-1]][cols].ffill().fillna(0), W, F, max_feat, hp=hp)
787
  p_up = float(final_p[0])
788
 
789
  price, a = float(close.iloc[-1]), float(atr.iloc[-1])
 
794
  "center": center, "low": center - move, "high": center + move,
795
  "thr": best["thr"], "bt": best, "n_oos": len(oos_p), "fee": fee,
796
  "n_ext": n_ext, "n_feat": len(cols), "auc_base": auc_base, "max_feat": max_feat,
797
+ "gap": gap, "auc_p": auc_p, "nested": nested, "holdout": ho, "tuned": tuned,
798
  }
799
 
800
 
 
845
  return f"{type(e).__name__}: {e}"
846
 
847
 
848
+ def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True, max_feat=40, use_optuna=False, n_trials=25):
849
  try:
850
  ohlcv, deep, ind, extra = asyncio.run(run_async(symbol, timeframe, exchange, horizon, limit))
851
  df = to_df(ohlcv)
 
856
  ext = indicator_features(ind, df)
857
  if dfeat is not None:
858
  ext = dfeat if ext is None else pd.concat([ext, dfeat], axis=1)
859
+ res = predict(df, int(horizon), ext=ext, max_feat=int(max_feat),
860
+ use_optuna=bool(use_optuna), n_trials=int(n_trials))
861
  except BaseException as e: # noqa: BLE001
862
  import traceback
863
  traceback.print_exc()
 
873
  ("Hold-out AUC > 0.50", ho is not None and ho["auc"] > 0.50),
874
  ("Hold-out net getiri > 0 (≥20 işlem)", ho is not None and ho["n"] >= 20 and ho["mean"] > 0),
875
  ]
876
+ t = res["tuned"]
877
+ if t is None:
878
+ opt_txt = "kapalı (varsayılan hiperparametreler)"
879
+ elif "error" in t:
880
+ opt_txt = t["error"]
881
+ else:
882
+ g, e = t["hp"]["hgb"], t["hp"]["et"]
883
+ opt_txt = (f"{t['trials']} deneme · iç skor {t['score']:.3f} · k={t['k']} · "
884
+ f"HGB derinlik {g['max_depth']}, lr {g['learning_rate']:.3f} · ET derinlik {e['max_depth']} · "
885
+ f"LR C={t['hp']['lr']['C']:.3f} (yalnızca ilk %40'ta ayarlandı)")
886
  passed = sum(ok for _, ok in checks)
887
  edge = f"✅ {passed}/5 kriter" if passed >= 4 else f"⚠️ {passed}/5 kriter — kanıtlanamadı"
888
  checks_md = "**Aşırı uyum / güvenilirlik göstergesi**\n\n" + "\n".join(
 
901
  | Ort. net getiri / işlem | {bt['mean']:.3%} (t = {bt['t']:.2f}, ücret {res['fee']:.2%}/işlem) |
902
  | Özellik sayısı | {res['n_feat']} (Tickscope/türev: {res['n_ext']}) → fold başına en fazla {res['max_feat']} seçilir |
903
  | AUC yalnızca temel özelliklerle | {('%.3f' % res['auc_base']) if res['auc_base'] is not None else '—'} (ek veri kaynakları yardım ediyor mu?) |
904
+ | Optuna | {opt_txt} |
905
  | Train–test AUC farkı (aşırı uyum) | {res['gap']:.3f} |
906
  | AUC anlamlılığı (tek yönlü p) | {res['auc_p']:.3f} |
907
  | Nested (dürüst) net getiri / işlem | {nest['mean']:.3%} (t = {nest['t']:.2f}, {nest['n']} işlem) |
 
941
  limit = gr.Slider(300, 20000, value=5000, step=100, label="Mum sayısı (1000 üstü sayfalanır)")
942
  use_deriv = gr.Checkbox(True, label="Türev verisi (LiquidVision MCP: funding/OI) — mumları geçmişin kapsadığı aralığa kırpabilir")
943
  max_feat = gr.Slider(10, 150, value=40, step=5, label="Maks. özellik (fold içinde seçilir; az = daha az aşırı uyum)")
944
+ with gr.Row():
945
+ use_optuna = gr.Checkbox(False, label="Optuna ile hiperparametre ayarı (OOS/hold-out'a dokunmaz; yavaş)")
946
+ n_trials = gr.Slider(10, 100, value=25, step=5, label="Optuna deneme sayısı")
947
  btn = gr.Button("Tahmin et", variant="primary")
948
  out_md = gr.Markdown()
949
  out_plot = gr.Plot()
950
  with gr.Accordion("Tickscope ham çıktı (deep_analyze / indicators)", open=False):
951
  out_raw = gr.Code(language="json")
952
+ btn.click(run, [symbol, timeframe, exchange, horizon, limit, use_deriv, max_feat, use_optuna, n_trials], [out_md, out_plot, out_raw])
953
 
954
  if __name__ == "__main__":
955
  demo.launch()
956
+