Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -477,8 +477,27 @@ def _models():
|
|
| 477 |
}
|
| 478 |
|
| 479 |
|
| 480 |
-
def
|
| 481 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 482 |
ps = []
|
| 483 |
for name, m in _models().items():
|
| 484 |
if name == "lr":
|
|
@@ -508,7 +527,25 @@ def _backtest(p, fwd, horizon, thr, fee):
|
|
| 508 |
"hit": float((tr > 0).mean()), "pf": float(gp / gl) if gl > 0 else np.inf}
|
| 509 |
|
| 510 |
|
| 511 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 512 |
feats, atr = make_features(df)
|
| 513 |
n_ext = 0
|
| 514 |
if ext is not None:
|
|
@@ -543,24 +580,20 @@ def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int =
|
|
| 543 |
# ve büyük hareketlere daha fazla ağırlık ver.
|
| 544 |
W = np.clip(np.abs(F) / (TM + 1e-12), 0.05, 3.0)
|
| 545 |
|
| 546 |
-
# --- Purged walk-forward (expanding window) ---
|
| 547 |
-
|
| 548 |
-
start = int(n * 0.4)
|
| 549 |
-
edges = np.linspace(start, n, n_folds + 1).astype(int)
|
| 550 |
-
oos_p, oos_idx = [], []
|
| 551 |
-
for k in range(n_folds):
|
| 552 |
-
tr_end, te0, te1 = edges[k] - horizon, edges[k], edges[k + 1]
|
| 553 |
-
if tr_end < 80:
|
| 554 |
-
continue
|
| 555 |
-
p = _fit_predict(X.iloc[:tr_end], Y.iloc[:tr_end], X.iloc[te0:te1], W[:tr_end])
|
| 556 |
-
oos_p.append(p)
|
| 557 |
-
oos_idx.extend(range(te0, te1))
|
| 558 |
-
if not oos_p:
|
| 559 |
-
raise ValueError("Walk-forward için yeterli veri yok. Mum sayısını artır.")
|
| 560 |
-
oos_p = np.concatenate(oos_p)
|
| 561 |
-
oos_idx = np.array(oos_idx)
|
| 562 |
yo, fo = Y.values[oos_idx], F[oos_idx]
|
| 563 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
acc = float(((oos_p > 0.5).astype(int) == yo).mean())
|
| 565 |
base = float(max(yo.mean(), 1 - yo.mean()))
|
| 566 |
try:
|
|
@@ -576,7 +609,7 @@ def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int =
|
|
| 576 |
best = {"thr": thr, **r}
|
| 577 |
|
| 578 |
# Son model: tüm veriyle, canlı tahmin
|
| 579 |
-
final_p = _fit_predict(X, Y, feats.iloc[[-1]][cols].ffill().fillna(0), W)
|
| 580 |
p_up = float(final_p[0])
|
| 581 |
|
| 582 |
price, a = float(close.iloc[-1]), float(atr.iloc[-1])
|
|
@@ -586,7 +619,7 @@ def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int =
|
|
| 586 |
"p_up": p_up, "acc": acc, "baseline": base, "auc": auc, "price": price, "atr": a,
|
| 587 |
"center": center, "low": center - move, "high": center + move,
|
| 588 |
"thr": best["thr"], "bt": best, "n_oos": len(oos_p), "fee": fee,
|
| 589 |
-
"n_ext": n_ext, "n_feat": len(cols),
|
| 590 |
}
|
| 591 |
|
| 592 |
|
|
@@ -631,7 +664,7 @@ def _root(e):
|
|
| 631 |
return f"{type(e).__name__}: {e}"
|
| 632 |
|
| 633 |
|
| 634 |
-
def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True):
|
| 635 |
try:
|
| 636 |
ohlcv, deep, ind, extra = asyncio.run(run_async(symbol, timeframe, exchange, horizon, limit))
|
| 637 |
df = to_df(ohlcv)
|
|
@@ -642,7 +675,7 @@ def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True):
|
|
| 642 |
ext = indicator_features(ind, df)
|
| 643 |
if dfeat is not None:
|
| 644 |
ext = dfeat if ext is None else pd.concat([ext, dfeat], axis=1)
|
| 645 |
-
res = predict(df, int(horizon), ext=ext)
|
| 646 |
except BaseException as e: # noqa: BLE001
|
| 647 |
import traceback
|
| 648 |
traceback.print_exc()
|
|
@@ -662,7 +695,8 @@ def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True):
|
|
| 662 |
| OOS AUC / doğruluk | {res['auc']:.3f} / {res['acc']:.1%} (baseline {res['baseline']:.1%}) |
|
| 663 |
| OOS işlem sayısı | {bt['n']} (hit {bt['hit']:.1%}, PF {bt['pf']:.2f}) |
|
| 664 |
| Ort. net getiri / işlem | {bt['mean']:.3%} (t = {bt['t']:.2f}, ücret {res['fee']:.2%}/işlem) |
|
| 665 |
-
| Özellik sayısı | {res['n_feat']} (Tickscope
|
|
|
|
| 666 |
| Model edge | {edge} |
|
| 667 |
|
| 668 |
*Walk-forward (purged) out-of-sample sonuçlar. Eşik OOS'ta seçildiği için hafif iyimser olabilir. Eğitim/araştırma amaçlıdır; yatırım tavsiyesi değildir.*"""
|
|
@@ -695,12 +729,13 @@ with gr.Blocks(title="Tickscope Tahmin") as demo:
|
|
| 695 |
horizon = gr.Slider(1, 24, value=4, step=1, label="Tahmin ufku (bar)")
|
| 696 |
limit = gr.Slider(300, 20000, value=5000, step=100, label="Mum sayısı (1000 üstü sayfalanır)")
|
| 697 |
use_deriv = gr.Checkbox(True, label="Türev verisi (LiquidVision MCP: funding/OI) — mumları geçmişin kapsadığı aralığa kırpabilir")
|
|
|
|
| 698 |
btn = gr.Button("Tahmin et", variant="primary")
|
| 699 |
out_md = gr.Markdown()
|
| 700 |
out_plot = gr.Plot()
|
| 701 |
with gr.Accordion("Tickscope ham çıktı (deep_analyze / indicators)", open=False):
|
| 702 |
out_raw = gr.Code(language="json")
|
| 703 |
-
btn.click(run, [symbol, timeframe, exchange, horizon, limit, use_deriv], [out_md, out_plot, out_raw])
|
| 704 |
|
| 705 |
if __name__ == "__main__":
|
| 706 |
demo.launch()
|
|
|
|
| 477 |
}
|
| 478 |
|
| 479 |
|
| 480 |
+
def _select(Xtr, ftr, k, thr=0.95):
|
| 481 |
+
"""Yalnızca EĞİTİM verisiyle: |Spearman IC| sırasına göre, birbirine çok benzeyenleri (>thr) eleyerek k özellik seç."""
|
| 482 |
+
if k <= 0 or Xtr.shape[1] <= k:
|
| 483 |
+
return list(Xtr.columns)
|
| 484 |
+
ic = Xtr.rank().corrwith(pd.Series(np.asarray(ftr), index=Xtr.index).rank()).abs().fillna(0)
|
| 485 |
+
ic = ic.sort_values(ascending=False)
|
| 486 |
+
corr = Xtr.corr().abs().fillna(0)
|
| 487 |
+
keep = []
|
| 488 |
+
for c in ic.index:
|
| 489 |
+
if all(corr.at[c, o] < thr for o in keep):
|
| 490 |
+
keep.append(c)
|
| 491 |
+
if len(keep) >= k:
|
| 492 |
+
break
|
| 493 |
+
return keep
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
def _fit_predict(Xtr, ytr, Xte, w=None, ftr=None, k=0):
|
| 497 |
+
"""Ensemble: HGB + ExtraTrees + Logistic -> olasılık ortalaması (k>0 ise fold-içi özellik seçimi)."""
|
| 498 |
+
if k and ftr is not None:
|
| 499 |
+
sel = _select(Xtr, ftr, k)
|
| 500 |
+
Xtr, Xte = Xtr[sel], Xte[sel]
|
| 501 |
ps = []
|
| 502 |
for name, m in _models().items():
|
| 503 |
if name == "lr":
|
|
|
|
| 527 |
"hit": float((tr > 0).mean()), "pf": float(gp / gl) if gl > 0 else np.inf}
|
| 528 |
|
| 529 |
|
| 530 |
+
def _walk(X, Y, F, W, horizon, n_folds, k):
|
| 531 |
+
"""Purged expanding-window walk-forward; OOS olasılıkları ve satır indeksleri."""
|
| 532 |
+
n = len(X)
|
| 533 |
+
edges = np.linspace(int(n * 0.4), n, n_folds + 1).astype(int)
|
| 534 |
+
oos_p, oos_idx = [], []
|
| 535 |
+
for j in range(n_folds):
|
| 536 |
+
tr_end, te0, te1 = edges[j] - horizon, edges[j], edges[j + 1]
|
| 537 |
+
if tr_end < 80:
|
| 538 |
+
continue
|
| 539 |
+
oos_p.append(_fit_predict(X.iloc[:tr_end], Y.iloc[:tr_end], X.iloc[te0:te1],
|
| 540 |
+
W[:tr_end], F[:tr_end], k))
|
| 541 |
+
oos_idx.extend(range(te0, te1))
|
| 542 |
+
if not oos_p:
|
| 543 |
+
raise ValueError("Walk-forward için yeterli veri yok. Mum sayısını artır.")
|
| 544 |
+
return np.concatenate(oos_p), np.array(oos_idx)
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
def predict(df: pd.DataFrame, horizon: int, fee: float = 0.0006, n_folds: int = 5, ext=None,
|
| 548 |
+
max_feat: int = 40):
|
| 549 |
feats, atr = make_features(df)
|
| 550 |
n_ext = 0
|
| 551 |
if ext is not None:
|
|
|
|
| 580 |
# ve büyük hareketlere daha fazla ağırlık ver.
|
| 581 |
W = np.clip(np.abs(F) / (TM + 1e-12), 0.05, 3.0)
|
| 582 |
|
| 583 |
+
# --- Purged walk-forward (expanding window), fold-içi özellik seçimiyle ---
|
| 584 |
+
oos_p, oos_idx = _walk(X, Y, F, W, horizon, n_folds, max_feat)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
yo, fo = Y.values[oos_idx], F[oos_idx]
|
| 586 |
|
| 587 |
+
# Teşhis (ablation): yalnızca temel (fiyat/hacim) özelliklerle aynı test
|
| 588 |
+
base_cols = [c for c in cols if not c.startswith(("ts_", "dv_"))]
|
| 589 |
+
auc_base = None
|
| 590 |
+
if len(base_cols) < len(cols):
|
| 591 |
+
pb, ib = _walk(X[base_cols], Y, F, W, horizon, n_folds, max_feat)
|
| 592 |
+
try:
|
| 593 |
+
auc_base = float(roc_auc_score(Y.values[ib], pb))
|
| 594 |
+
except ValueError:
|
| 595 |
+
auc_base = 0.5
|
| 596 |
+
|
| 597 |
acc = float(((oos_p > 0.5).astype(int) == yo).mean())
|
| 598 |
base = float(max(yo.mean(), 1 - yo.mean()))
|
| 599 |
try:
|
|
|
|
| 609 |
best = {"thr": thr, **r}
|
| 610 |
|
| 611 |
# Son model: tüm veriyle, canlı tahmin
|
| 612 |
+
final_p = _fit_predict(X, Y, feats.iloc[[-1]][cols].ffill().fillna(0), W, F, max_feat)
|
| 613 |
p_up = float(final_p[0])
|
| 614 |
|
| 615 |
price, a = float(close.iloc[-1]), float(atr.iloc[-1])
|
|
|
|
| 619 |
"p_up": p_up, "acc": acc, "baseline": base, "auc": auc, "price": price, "atr": a,
|
| 620 |
"center": center, "low": center - move, "high": center + move,
|
| 621 |
"thr": best["thr"], "bt": best, "n_oos": len(oos_p), "fee": fee,
|
| 622 |
+
"n_ext": n_ext, "n_feat": len(cols), "auc_base": auc_base, "max_feat": max_feat,
|
| 623 |
}
|
| 624 |
|
| 625 |
|
|
|
|
| 664 |
return f"{type(e).__name__}: {e}"
|
| 665 |
|
| 666 |
|
| 667 |
+
def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True, max_feat=40):
|
| 668 |
try:
|
| 669 |
ohlcv, deep, ind, extra = asyncio.run(run_async(symbol, timeframe, exchange, horizon, limit))
|
| 670 |
df = to_df(ohlcv)
|
|
|
|
| 675 |
ext = indicator_features(ind, df)
|
| 676 |
if dfeat is not None:
|
| 677 |
ext = dfeat if ext is None else pd.concat([ext, dfeat], axis=1)
|
| 678 |
+
res = predict(df, int(horizon), ext=ext, max_feat=int(max_feat))
|
| 679 |
except BaseException as e: # noqa: BLE001
|
| 680 |
import traceback
|
| 681 |
traceback.print_exc()
|
|
|
|
| 695 |
| OOS AUC / doğruluk | {res['auc']:.3f} / {res['acc']:.1%} (baseline {res['baseline']:.1%}) |
|
| 696 |
| OOS işlem sayısı | {bt['n']} (hit {bt['hit']:.1%}, PF {bt['pf']:.2f}) |
|
| 697 |
| Ort. net getiri / işlem | {bt['mean']:.3%} (t = {bt['t']:.2f}, ücret {res['fee']:.2%}/işlem) |
|
| 698 |
+
| Özellik sayısı | {res['n_feat']} (Tickscope/türev: {res['n_ext']}) → fold başına en fazla {res['max_feat']} seçilir |
|
| 699 |
+
| AUC yalnızca temel özelliklerle | {('%.3f' % res['auc_base']) if res['auc_base'] is not None else '—'} (ek veri kaynakları yardım ediyor mu?) |
|
| 700 |
| Model edge | {edge} |
|
| 701 |
|
| 702 |
*Walk-forward (purged) out-of-sample sonuçlar. Eşik OOS'ta seçildiği için hafif iyimser olabilir. Eğitim/araştırma amaçlıdır; yatırım tavsiyesi değildir.*"""
|
|
|
|
| 729 |
horizon = gr.Slider(1, 24, value=4, step=1, label="Tahmin ufku (bar)")
|
| 730 |
limit = gr.Slider(300, 20000, value=5000, step=100, label="Mum sayısı (1000 üstü sayfalanır)")
|
| 731 |
use_deriv = gr.Checkbox(True, label="Türev verisi (LiquidVision MCP: funding/OI) — mumları geçmişin kapsadığı aralığa kırpabilir")
|
| 732 |
+
max_feat = gr.Slider(10, 150, value=40, step=5, label="Maks. özellik (fold içinde seçilir; az = daha az aşırı uyum)")
|
| 733 |
btn = gr.Button("Tahmin et", variant="primary")
|
| 734 |
out_md = gr.Markdown()
|
| 735 |
out_plot = gr.Plot()
|
| 736 |
with gr.Accordion("Tickscope ham çıktı (deep_analyze / indicators)", open=False):
|
| 737 |
out_raw = gr.Code(language="json")
|
| 738 |
+
btn.click(run, [symbol, timeframe, exchange, horizon, limit, use_deriv, max_feat], [out_md, out_plot, out_raw])
|
| 739 |
|
| 740 |
if __name__ == "__main__":
|
| 741 |
demo.launch()
|