GoshawkVortexAI commited on
Commit
2558acf
·
verified ·
1 Parent(s): 70ee01c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +243 -93
app.py CHANGED
@@ -95,27 +95,26 @@ os.makedirs(MODEL_DIR, exist_ok=True)
95
  MODEL_PATH = os.path.join(MODEL_DIR, "gos_hawk_model.pkl")
96
 
97
  MIN_TRAIN_SAMPLES = 150
98
- REQUEST_TIMEOUT = 60 # daha uzun timeout (saatlik veri için)
99
  MAX_RETRIES = 3
100
  RETRY_BACKOFF = 2
101
 
102
- # Saatlik veri (24 saatlik hedef için daha fazla nokta → güvenilir model)
103
  HISTORY_TIME_SCALE = 1
104
 
105
- # Hedef ölçeği: log getiri * 10000 = baz puan
106
  TARGET_SCALE = 10000.0
107
-
108
- # Tahmin clip (log getiri, ±%15)
109
  MAX_LOG_RETURN = 0.15
110
  MIN_LOG_RETURN = -0.15
111
-
112
- # Belirsizlik eşiği (log getiri)
113
  LOW_CONFIDENCE_THRESHOLD = 0.01
114
-
115
- # Zaman-tabanlı hedef toleransı (saat)
116
  TARGET_TOLERANCE_HOURS = 12
117
 
118
- # Hedef feature kolonları
 
 
 
 
 
 
 
119
  FEATURE_COLS = [
120
  "hour", "dayofweek", "is_weekend",
121
  "lag_1h", "lag_3h", "lag_6h", "lag_12h", "lag_24h", "lag_48h",
@@ -139,7 +138,7 @@ PRICE_LIKE_COLS = [
139
 
140
  def safe_request(url: str, params: Optional[dict] = None) -> Optional[list]:
141
  headers = {
142
- "User-Agent": "GOSHAWK-ALBION-MARKET-AI/1.4 (public-api-client)",
143
  "Accept": "application/json",
144
  }
145
  for attempt in range(MAX_RETRIES):
@@ -191,7 +190,7 @@ def fetch_current_prices(item_ids: List[str], cities: List[str], qualities: str
191
 
192
  def fetch_history(item_ids: List[str], cities: List[str], time_scale: int = 1, qualities: str = "1") -> pd.DataFrame:
193
  all_records = []
194
- chunk_size = 20 # saatlik veri daha ağır → daha küçük chunk
195
  for i in range(0, len(item_ids), chunk_size):
196
  chunk = item_ids[i:i+chunk_size]
197
  url = f"{API_BASE}/stats/history/{','.join(chunk)}.json"
@@ -276,15 +275,6 @@ def compute_rsi(series: pd.Series, period: int = 14) -> pd.Series:
276
 
277
 
278
  def build_features(df: pd.DataFrame) -> pd.DataFrame:
279
- """
280
- Saatlik veride feature'lar:
281
- - lag_1h..lag_48h: saatlik fiyat gecikmeleri
282
- - chg_1h..chg_24h: log değişimleri
283
- - ma_6h..ma_48h: hareketli ortalamalar
284
- - rsi_14, volatility_24h
285
- - volume_ratio, city_price_deviation
286
- - target_return_24h: zaman-tabanlı (tolerance ile) 24 saat ileri log getiri × 10000
287
- """
288
  if df.empty:
289
  return df
290
 
@@ -298,7 +288,6 @@ def build_features(df: pd.DataFrame) -> pd.DataFrame:
298
 
299
  group_cols = ["item_id", "city"]
300
 
301
- # Saatlik lag'ler
302
  for lag in [1, 3, 6, 12, 24, 48]:
303
  df[f"lag_{lag}h"] = df.groupby(group_cols)["avg_price"].shift(lag)
304
 
@@ -325,17 +314,22 @@ def build_features(df: pd.DataFrame) -> pd.DataFrame:
325
  city_avg = df.groupby(["item_id", "timestamp"])["avg_price"].transform("mean")
326
  df["city_price_deviation"] = (df["avg_price"] - city_avg) / city_avg.replace(0, np.nan)
327
 
328
- # ZAMAN-TABANLI HEDEF: her (item_id, city) grubu için 24 saat ilerideki log getiri
329
- df = _compute_forward_target(df)
 
 
 
 
 
330
 
 
 
 
 
331
  return df
332
 
333
 
334
  def _compute_forward_target(df: pd.DataFrame) -> pd.DataFrame:
335
- """
336
- Her (item_id, city) grubu için 24 saat ilerideki log fiyatını bulur.
337
- shift(-N) yerine merge_asof ile zaman-tabanlı eşleştirme yapar (düzensiz aralıklara dayanıklı).
338
- """
339
  if df.empty or "log_price" not in df.columns:
340
  df["target_return_24h"] = np.nan
341
  return df
@@ -394,12 +388,10 @@ def prepare_model_data(featured_df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.Seri
394
 
395
  df = df.dropna(subset=["target_return_24h", "lag_24h"]).copy()
396
 
397
- # Fiyat-benzeri feature'ları log ölçeğine çevir
398
  for col in PRICE_LIKE_COLS:
399
  if col in df.columns:
400
  df[col] = np.log1p(df[col].clip(lower=1.0))
401
 
402
- # Uzun pencereleri doldur
403
  if "lag_48h" in df.columns:
404
  df["lag_48h"] = df["lag_48h"].fillna(df["lag_24h"])
405
  if "ma_48h" in df.columns:
@@ -423,7 +415,6 @@ def prepare_model_data(featured_df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.Seri
423
 
424
  model_df = df.dropna(subset=FEATURE_COLS + ["target_return_24h"]).copy()
425
 
426
- # Hedef clip (bp cinsinden ±1500)
427
  target_min = MIN_LOG_RETURN * TARGET_SCALE
428
  target_max = MAX_LOG_RETURN * TARGET_SCALE
429
  model_df["target_return_24h"] = model_df["target_return_24h"].clip(target_min, target_max)
@@ -442,11 +433,10 @@ def train_global_model(X: pd.DataFrame, y: pd.Series) -> Optional[lgb.LGBMRegres
442
  return None
443
 
444
  y_log_std = (y / TARGET_SCALE).std()
445
- y_bp_std = y.std()
446
- print(f"[TANI] train_global_model: n={len(X)}, y_bp_std={y_bp_std:.4f}, y_log_std={y_log_std:.8f}")
447
 
448
  if y_log_std < 1e-6:
449
- print(f"⚠️ Hedef log std çok düşük: {y_log_std:.8f}. Model öğrenemez.")
450
  return None
451
 
452
  split_idx = int(len(X) * 0.8)
@@ -485,8 +475,7 @@ def train_global_model(X: pd.DataFrame, y: pd.Series) -> Optional[lgb.LGBMRegres
485
  )
486
 
487
  val_preds = model.predict(X_val)
488
- pred_std = float(np.std(val_preds))
489
- print(f"[TANI] val_pred_std={pred_std:.4f} bp")
490
 
491
  return model
492
 
@@ -523,15 +512,14 @@ def predict_24h_returns(model: lgb.LGBMRegressor, latest_features: pd.DataFrame)
523
  raw_preds_bp = model.predict(X_ready)
524
  raw_preds_log = raw_preds_bp / TARGET_SCALE
525
 
526
- pred_std = float(np.std(raw_preds_log))
527
- if pred_std < 1e-5:
528
- print(f"⚠️ UYARI: Tüm tahminler aynı (std={pred_std:.8f}).")
529
-
530
  clipped_log = np.clip(raw_preds_log, MIN_LOG_RETURN, MAX_LOG_RETURN)
531
  pred_return_pct = (np.exp(clipped_log) - 1) * 100
532
 
533
- result = X_pred.loc[X_ready.index, ["item_id", "city", "avg_price", "item_count",
534
- "volatility_24h", "rsi_14", "volume_ratio"]].copy()
 
 
 
535
  result["raw_log_pred"] = raw_preds_log
536
  result["clipped_log_pred"] = clipped_log
537
  result["predicted_log_return_24h"] = clipped_log
@@ -542,21 +530,117 @@ def predict_24h_returns(model: lgb.LGBMRegressor, latest_features: pd.DataFrame)
542
  return result
543
 
544
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  # ---------------------------------------------------------------------------
546
  # WALK-FORWARD BACKTEST
547
  # ---------------------------------------------------------------------------
548
 
549
 
550
- def walk_forward_backtest(featured_df: pd.DataFrame, n_splits: int = 3) -> Dict:
551
  if featured_df.empty:
552
  return {"error": "Veri yok."}
553
 
554
  df = featured_df.sort_values("timestamp").copy()
555
  unique_times = df["timestamp"].sort_values().unique()
556
- if len(unique_times) < 100:
557
- return {"error": "Yeterli zaman noktası yok."}
 
558
 
559
  test_size = len(unique_times) // (n_splits + 1)
 
 
 
560
  results = []
561
  all_predictions = []
562
 
@@ -565,7 +649,7 @@ def walk_forward_backtest(featured_df: pd.DataFrame, n_splits: int = 3) -> Dict:
565
  test_end_idx = test_start_idx + test_size
566
  if test_end_idx > len(unique_times):
567
  break
568
- train_end_idx = test_start_idx - 48
569
  if train_end_idx <= 0:
570
  continue
571
 
@@ -575,19 +659,21 @@ def walk_forward_backtest(featured_df: pd.DataFrame, n_splits: int = 3) -> Dict:
575
  train_df = df[df["timestamp"].isin(train_times)]
576
  test_df = df[df["timestamp"].isin(test_times)]
577
 
578
- if len(train_df) < MIN_TRAIN_SAMPLES or len(test_df) < 20:
 
579
  continue
580
 
581
  X_train, y_train, _ = prepare_model_data(train_df)
582
  X_test, y_test, meta_test = prepare_model_data(test_df)
583
 
584
- if len(X_train) < MIN_TRAIN_SAMPLES or len(X_test) < 5:
 
585
  continue
586
 
587
  model = lgb.LGBMRegressor(
588
  objective="regression", metric="mae",
589
- num_leaves=31, max_depth=8, min_child_samples=20,
590
- learning_rate=0.05, n_estimators=400,
591
  reg_lambda=0.1, verbose=-1, random_state=42, n_jobs=-1,
592
  )
593
  model.fit(X_train, y_train)
@@ -598,7 +684,7 @@ def walk_forward_backtest(featured_df: pd.DataFrame, n_splits: int = 3) -> Dict:
598
 
599
  mae = mean_absolute_error(y_test_log, preds)
600
  rmse = np.sqrt(mean_squared_error(y_test_log, preds))
601
- direction_correct = np.mean((y_test_log > 0) == (preds > 0))
602
 
603
  baseline_preds = np.zeros_like(y_test_log)
604
  baseline_mae = mean_absolute_error(y_test_log, baseline_preds)
@@ -606,8 +692,8 @@ def walk_forward_backtest(featured_df: pd.DataFrame, n_splits: int = 3) -> Dict:
606
 
607
  results.append({
608
  "split": i + 1,
609
- "train_samples": len(X_train),
610
- "test_samples": len(X_test),
611
  "mae": round(float(mae), 6),
612
  "rmse": round(float(rmse), 6),
613
  "direction_accuracy": round(float(direction_correct), 4),
@@ -620,9 +706,10 @@ def walk_forward_backtest(featured_df: pd.DataFrame, n_splits: int = 3) -> Dict:
620
  test_meta["predicted_return"] = preds
621
  test_meta["actual_return"] = y_test_log.values
622
  all_predictions.append(test_meta)
 
623
 
624
  if not results:
625
- return {"error": "Backtest tamamlanamadı."}
626
 
627
  if all_predictions:
628
  sim_df = pd.concat(all_predictions)
@@ -738,16 +825,13 @@ class DataPipeline:
738
  if n_with_target < MIN_TRAIN_SAMPLES:
739
  self.status_message = (
740
  f"Yetersiz hedefli örnek: {n_with_target} < {MIN_TRAIN_SAMPLES}. "
741
- f"Toplam: {diag.get('n_total', 0)}, grup: {diag.get('n_groups', 0)}. "
742
- f"24 saatlik ileri veri bulunamadı — API çok seyrek olabilir."
743
  )
744
  return False
745
 
746
  if std_log < 1e-6:
747
  self.status_message = (
748
- f"Hedef varyansı çok düşük (std_log={std_log:.8f}, "
749
- f"std_bp={diag.get('target_std_bp', 0):.4f}). Model öğrenemez. "
750
- f"API'den daha fazla veri çekmeyi deneyin."
751
  )
752
  return False
753
 
@@ -764,7 +848,7 @@ class DataPipeline:
764
  sample_preds = self.model.predict(X.head(500))
765
  pred_std = float(np.std(sample_preds))
766
  if pred_std < 1.0:
767
- self.status_message = f"⚠️ Model zayıf öğrendi (pred_std={pred_std:.2f} bp). Daha fazla veri gerekli."
768
  else:
769
  self.status_message = f"✓ Model eğitildi ({len(X)} örnek, pred_std={pred_std:.1f} bp)."
770
 
@@ -810,6 +894,11 @@ class DataPipeline:
810
  total_cost = SETUP_FEE_RATE + TRANSACTION_TAX_RATE
811
  preds["net_return_pct"] = preds["predicted_return_pct"] - (total_cost * 100)
812
 
 
 
 
 
 
813
  return preds
814
 
815
  def get_backtest(self):
@@ -849,6 +938,7 @@ def get_dashboard_metrics():
849
  "Ortalama Fiyat": f"{df['avg_price'].mean():.0f} silver",
850
  "Model Durumu": "Eğitildi" if pipeline.model else "Eğitilmedi",
851
  "Tahmin Sınırı": f"±%{MAX_LOG_RETURN * 100:.0f}",
 
852
  "Son Güncelleme": pipeline.last_update.strftime("%Y-%m-%d %H:%M") if pipeline.last_update else "—",
853
  }
854
  return pd.DataFrame(list(metrics.items()), columns=["Metrik", "Değer"])
@@ -874,10 +964,13 @@ def get_analysis_table():
874
  latest["Mevcut Fiyat"] = latest["avg_price"].round(0)
875
  latest["Hacim"] = latest["item_count"].fillna(0).astype(int)
876
  latest["Spread"] = (latest["sell_price_min"] - latest["buy_price_max"]).round(0)
 
 
 
877
  latest["Likidite"] = pd.cut(latest["item_count"], bins=[-1, 10, 50, 200, np.inf],
878
  labels=["Düşük", "Orta", "İyi", "Yüksek"]).astype(str)
879
 
880
- return latest[["Ürün", "Şehir", "Mevcut Fiyat", "Hacim", "Spread", "Likidite"]].sort_values("Hacim", ascending=False).head(100)
881
 
882
 
883
  def get_prediction_table():
@@ -892,18 +985,56 @@ def get_prediction_table():
892
  preds["Tahmini Fiyat (24s)"] = preds["predicted_price_24h"].round(0)
893
  preds["Tahmini Değişim %"] = preds["predicted_return_pct"].round(2)
894
  preds["Güvenilirlik"] = (preds["confidence"] * 100).round(0).astype(int).astype(str) + "%"
895
- preds["Spread"] = preds["spread"].round(0)
896
  preds["Net Getiri % (maliyet sonrası)"] = preds["net_return_pct"].round(2)
897
  preds["Likidite"] = pd.cut(preds["item_count"], bins=[-1, 10, 50, 200, np.inf],
898
  labels=["Düşük", "Orta", "İyi", "Yüksek"]).astype(str)
899
- preds["Not"] = np.where(preds["is_uncertain"], "⚠️ Belirsiz", "")
900
 
901
  cols = ["Ürün", "Şehir", "Mevcut Fiyat", "Tahmini Fiyat (24s)",
902
- "Tahmini Değişim %", "Güvenilirlik", "Spread", "Likidite",
903
- "Net Getiri % (maliyet sonrası)", "Not"]
904
  return preds[cols].sort_values("Tahmini Değişim %", ascending=False)
905
 
906
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
907
  def get_arbitrage_table():
908
  if pipeline.prices_df.empty:
909
  return pd.DataFrame({"Bilgi": ["Anlık fiyat yok."]})
@@ -1023,6 +1154,7 @@ def refresh_data_ui():
1023
  _safe(get_dashboard_metrics, pd.DataFrame()),
1024
  _safe(get_analysis_table, pd.DataFrame()),
1025
  _safe(get_prediction_table, pd.DataFrame()),
 
1026
  _safe(get_arbitrage_table, pd.DataFrame()),
1027
  _safe(get_model_performance_plot, go.Figure().update_layout(title="—", template="plotly_dark")),
1028
  bt_table, bt_fig, sim_table,
@@ -1039,7 +1171,7 @@ def initial_load():
1039
 
1040
  return (
1041
  _safe(get_dashboard_metrics, pd.DataFrame({"Metrik": ["Durum"], "Değer": ["Hazır"]})),
1042
- pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
1043
  go.Figure().update_layout(title="Yenile butonuna basın", template="plotly_dark"),
1044
  pd.DataFrame(),
1045
  go.Figure().update_layout(title="Veri gerekli", template="plotly_dark"),
@@ -1059,11 +1191,10 @@ with gr.Blocks(title="GOSHAWK ALBION MARKET AI", theme=gr.themes.Soft(primary_hu
1059
  """
1060
  # 🦅 GOSHAWK ALBION MARKET AI
1061
  **Profesyonel Market Intelligence Dashboard** | Avrupa Sunucusu
1062
- *LightGBM tabanlı 24 saatlik fiyat tahmini ve arbitraj analizi.*
1063
-
1064
- **Tahmin sınırı:** ±%15 · **Hedef ölçeği:** baz puan · **Veri:** saatlik · **Hedef:** zaman-tabanlı (24h, tolerans ±12h)
1065
 
1066
- **⚠️ Risk Uyarısı:** Bu uygulama yatırım tavsiyesi değildir. Model tahminleri belirsizlik içerir ve kesin kazanç garantisi sunmaz.
 
1067
  """
1068
  )
1069
 
@@ -1075,27 +1206,42 @@ with gr.Blocks(title="GOSHAWK ALBION MARKET AI", theme=gr.themes.Soft(primary_hu
1075
  with gr.TabItem("📊 Dashboard"):
1076
  dashboard_table = gr.Dataframe(label="Özet & Teşhis", interactive=False, wrap=True)
1077
  gr.Markdown(
1078
- "### Kalibrasyon\n"
1079
- "- Saatlik veri (`time-scale=1`) → 24h hedef için ~24 nokta\n"
1080
- "- Hedef **zaman-tabanlı** (merge_asof, tolerans ±12h) — düzensiz aralıklara dayanıklı\n"
1081
- "- Hedef baz puan (×10000) → model daha stabil öğrenir\n"
1082
- "- Fiyat feature'ları `log1p` ile normalize\n"
1083
- "- Tahmin ±%15 aralığına clip\n"
1084
- "- |tahmin| < 1% ise **belirsiz** işaretlenir\n\n"
1085
- "### Teşhis sütunları\n"
1086
- "- **Hedefli Satır:** `target_return_24h` NaN olmayan satır sayısı\n"
1087
- "- **Hedef std (log):** 0.005-0.05 arası ise model öğrenebilir\n"
1088
- "- **Model Durumu:** Eğitildi = hazır\n\n"
 
 
 
 
1089
  "### Riskler\n"
1090
- "- Tahminler olasılıksaldır; kesin kazanç garantisi yoktur"
 
1091
  )
1092
 
1093
- with gr.TabItem("📦 100 Ürün Analizi"):
1094
- analysis_table = gr.Dataframe(label="Ürün Analizi", interactive=False)
 
 
 
 
 
 
 
 
1095
 
1096
- with gr.TabItem("🔮 24 Saatlik Fiyat Tahmini"):
1097
- prediction_table = gr.Dataframe(label="Tahminler", interactive=False)
1098
- gr.Markdown("*⚠️ Belirsiz = model sinyali zayıf. Net getiri %2.5+%8 maliyet düşülmüş halidir.*")
 
 
1099
 
1100
  with gr.TabItem("🚚 Arbitraj"):
1101
  arbitrage_table = gr.Dataframe(label="Arbitraj Fırsatları", interactive=False)
@@ -1106,13 +1252,16 @@ with gr.Blocks(title="GOSHAWK ALBION MARKET AI", theme=gr.themes.Soft(primary_hu
1106
  with gr.TabItem("🧪 Backtest"):
1107
  backtest_table = gr.Dataframe(label="Split Sonuçları", interactive=False)
1108
  backtest_plot = gr.Plot(label="MAE Karşılaştırması")
1109
- simulation_table = gr.Dataframe(label="Simülasyon", interactive=False)
1110
 
1111
  with gr.TabItem("🗄️ Veri Yönetimi"):
1112
  gr.Markdown(
1113
  "**API:** `europe.albion-online-data.com/api/v2`\n"
1114
  "**Time-scale:** 1h (saatlik)\n"
1115
- "**Model:** `gos_hawk_data/models/gos_hawk_model.pkl`"
 
 
 
1116
  )
1117
  csv_files = gr.Dataframe(label="CSV Dosyaları", interactive=False,
1118
  headers=["Dosya", "Boyut (KB)", "Son Değişiklik"])
@@ -1121,15 +1270,16 @@ with gr.Blocks(title="GOSHAWK ALBION MARKET AI", theme=gr.themes.Soft(primary_hu
1121
  fn=refresh_data_ui,
1122
  inputs=[],
1123
  outputs=[status_text, dashboard_table, analysis_table, prediction_table,
1124
- arbitrage_table, performance_plot, backtest_table, backtest_plot,
1125
- simulation_table, csv_files],
1126
  )
1127
 
1128
  demo.load(
1129
  fn=initial_load,
1130
  inputs=[],
1131
- outputs=[dashboard_table, analysis_table, prediction_table, arbitrage_table,
1132
- performance_plot, backtest_table, backtest_plot, simulation_table, csv_files],
 
1133
  )
1134
 
1135
 
 
95
  MODEL_PATH = os.path.join(MODEL_DIR, "gos_hawk_model.pkl")
96
 
97
  MIN_TRAIN_SAMPLES = 150
98
+ REQUEST_TIMEOUT = 60
99
  MAX_RETRIES = 3
100
  RETRY_BACKOFF = 2
101
 
 
102
  HISTORY_TIME_SCALE = 1
103
 
 
104
  TARGET_SCALE = 10000.0
 
 
105
  MAX_LOG_RETURN = 0.15
106
  MIN_LOG_RETURN = -0.15
 
 
107
  LOW_CONFIDENCE_THRESHOLD = 0.01
 
 
108
  TARGET_TOLERANCE_HOURS = 12
109
 
110
+ # DİP ALIM SİNYALİ PARAMETRELERİ
111
+ DIP_RSI_MAX = 40.0 # RSI bu değerin altındaysa "aşırı satım"
112
+ DIP_PERCENTILE_MAX = 0.25 # 7 günlük aralıkta alt %25'te mi?
113
+ DIP_MIN_VOLUME_RATIO = 1.0 # Hacim ortalamanın üstünde mi?
114
+ DIP_MIN_PREDICTED_PCT = -1.0 # Model tahmini bu kadar negatiften fazla olmalı
115
+ DIP_MAX_SPREAD_PCT = 0.15 # Spread / fiyat > %15 ise likit değil
116
+ DIP_MIN_LIQUIDITY = "Orta" # Düşük likidite = atla
117
+
118
  FEATURE_COLS = [
119
  "hour", "dayofweek", "is_weekend",
120
  "lag_1h", "lag_3h", "lag_6h", "lag_12h", "lag_24h", "lag_48h",
 
138
 
139
  def safe_request(url: str, params: Optional[dict] = None) -> Optional[list]:
140
  headers = {
141
+ "User-Agent": "GOSHAWK-ALBION-MARKET-AI/1.6 (public-api-client)",
142
  "Accept": "application/json",
143
  }
144
  for attempt in range(MAX_RETRIES):
 
190
 
191
  def fetch_history(item_ids: List[str], cities: List[str], time_scale: int = 1, qualities: str = "1") -> pd.DataFrame:
192
  all_records = []
193
+ chunk_size = 20
194
  for i in range(0, len(item_ids), chunk_size):
195
  chunk = item_ids[i:i+chunk_size]
196
  url = f"{API_BASE}/stats/history/{','.join(chunk)}.json"
 
275
 
276
 
277
  def build_features(df: pd.DataFrame) -> pd.DataFrame:
 
 
 
 
 
 
 
 
 
278
  if df.empty:
279
  return df
280
 
 
288
 
289
  group_cols = ["item_id", "city"]
290
 
 
291
  for lag in [1, 3, 6, 12, 24, 48]:
292
  df[f"lag_{lag}h"] = df.groupby(group_cols)["avg_price"].shift(lag)
293
 
 
314
  city_avg = df.groupby(["item_id", "timestamp"])["avg_price"].transform("mean")
315
  df["city_price_deviation"] = (df["avg_price"] - city_avg) / city_avg.replace(0, np.nan)
316
 
317
+ # 7 günlük (168h) fiyat aralığındaki yüzdelik konum
318
+ df["price_pct_7d"] = df.groupby(group_cols)["avg_price"].transform(
319
+ lambda x: x.rolling(168, min_periods=24).apply(
320
+ lambda arr: (arr[-1] - np.min(arr)) / (np.max(arr) - np.min(arr) + 1e-9),
321
+ raw=True
322
+ )
323
+ )
324
 
325
+ # Fiyat düşüş trendi (12h ve 24h değişimlerin negatif olması)
326
+ df["is_downtrend"] = ((df["chg_12h"] < 0) & (df["chg_24h"] < 0)).astype(int)
327
+
328
+ df = _compute_forward_target(df)
329
  return df
330
 
331
 
332
  def _compute_forward_target(df: pd.DataFrame) -> pd.DataFrame:
 
 
 
 
333
  if df.empty or "log_price" not in df.columns:
334
  df["target_return_24h"] = np.nan
335
  return df
 
388
 
389
  df = df.dropna(subset=["target_return_24h", "lag_24h"]).copy()
390
 
 
391
  for col in PRICE_LIKE_COLS:
392
  if col in df.columns:
393
  df[col] = np.log1p(df[col].clip(lower=1.0))
394
 
 
395
  if "lag_48h" in df.columns:
396
  df["lag_48h"] = df["lag_48h"].fillna(df["lag_24h"])
397
  if "ma_48h" in df.columns:
 
415
 
416
  model_df = df.dropna(subset=FEATURE_COLS + ["target_return_24h"]).copy()
417
 
 
418
  target_min = MIN_LOG_RETURN * TARGET_SCALE
419
  target_max = MAX_LOG_RETURN * TARGET_SCALE
420
  model_df["target_return_24h"] = model_df["target_return_24h"].clip(target_min, target_max)
 
433
  return None
434
 
435
  y_log_std = (y / TARGET_SCALE).std()
436
+ print(f"[TANI] train: n={len(X)}, y_log_std={y_log_std:.8f}")
 
437
 
438
  if y_log_std < 1e-6:
439
+ print(f"⚠️ Hedef log std çok düşük: {y_log_std:.8f}")
440
  return None
441
 
442
  split_idx = int(len(X) * 0.8)
 
475
  )
476
 
477
  val_preds = model.predict(X_val)
478
+ print(f"[TANI] val_pred_std={float(np.std(val_preds)):.4f} bp")
 
479
 
480
  return model
481
 
 
512
  raw_preds_bp = model.predict(X_ready)
513
  raw_preds_log = raw_preds_bp / TARGET_SCALE
514
 
 
 
 
 
515
  clipped_log = np.clip(raw_preds_log, MIN_LOG_RETURN, MAX_LOG_RETURN)
516
  pred_return_pct = (np.exp(clipped_log) - 1) * 100
517
 
518
+ keep_cols = ["item_id", "city", "avg_price", "item_count",
519
+ "volatility_24h", "rsi_14", "volume_ratio",
520
+ "price_pct_7d", "is_downtrend", "chg_12h", "chg_24h"]
521
+ keep_cols = [c for c in keep_cols if c in X_pred.columns]
522
+ result = X_pred.loc[X_ready.index, keep_cols].copy()
523
  result["raw_log_pred"] = raw_preds_log
524
  result["clipped_log_pred"] = clipped_log
525
  result["predicted_log_return_24h"] = clipped_log
 
530
  return result
531
 
532
 
533
+ # ---------------------------------------------------------------------------
534
+ # DİP ALIM SİNYALİ
535
+ # ---------------------------------------------------------------------------
536
+
537
+
538
+ def compute_dip_signal(row: pd.Series) -> Tuple[str, str]:
539
+ """
540
+ Kural tabanlı dip alım filtresi.
541
+ Returns: (signal_text, reason_text)
542
+
543
+ Sinyaller:
544
+ - 🟢 GÜÇLÜ AL: tüm koşullar sağlanıyor
545
+ - 🟡 ZAYIF AL: çoğu koşul sağlanıyor
546
+ - 🔵 İZLE: dip bölgesinde ama teyit yok
547
+ - ⚪ BEKLE: dip değil
548
+ - 🔴 ALMA: düşüş trendi, model de negatif
549
+ """
550
+ reasons = []
551
+ passed = 0
552
+ total = 0
553
+
554
+ # 1. RSI aşırı satım
555
+ rsi = row.get("rsi_14", 50)
556
+ total += 1
557
+ if pd.notna(rsi) and rsi < DIP_RSI_MAX:
558
+ passed += 1
559
+ reasons.append(f"RSI={rsi:.0f}")
560
+ else:
561
+ reasons.append(f"RSI={rsi:.0f} (değil)")
562
+
563
+ # 2. 7 günlük fiyat percentile
564
+ pct = row.get("price_pct_7d", 0.5)
565
+ total += 1
566
+ if pd.notna(pct) and pct < DIP_PERCENTILE_MAX:
567
+ passed += 1
568
+ reasons.append(f"7g%={pct*100:.0f}")
569
+ else:
570
+ reasons.append(f"7g%={(pct*100 if pd.notna(pct) else 0):.0f} (yüksek)")
571
+
572
+ # 3. Hacim onayı (talep dönüyor)
573
+ vr = row.get("volume_ratio", 1.0)
574
+ total += 1
575
+ if pd.notna(vr) and vr >= DIP_MIN_VOLUME_RATIO:
576
+ passed += 1
577
+ reasons.append(f"Hacim×{vr:.1f}")
578
+ else:
579
+ reasons.append(f"Hacim×{(vr if pd.notna(vr) else 0):.1f} (zayıf)")
580
+
581
+ # 4. Model tahmini çok negatif değil
582
+ pred_pct = row.get("predicted_return_pct", 0)
583
+ total += 1
584
+ if pd.notna(pred_pct) and pred_pct >= DIP_MIN_PREDICTED_PCT:
585
+ passed += 1
586
+ reasons.append(f"Model={pred_pct:+.1f}%")
587
+ else:
588
+ reasons.append(f"Model={pred_pct:+.1f}% (düşüş)")
589
+
590
+ # 5. Likidite yeterli (spread kontrolü)
591
+ spread = row.get("spread", np.nan)
592
+ price = row.get("avg_price", 1)
593
+ total += 1
594
+ if pd.notna(spread) and price > 0 and abs(spread) / price < DIP_MAX_SPREAD_PCT:
595
+ passed += 1
596
+ reasons.append(f"Spread={abs(spread)/price*100:.1f}%")
597
+ else:
598
+ reasons.append(f"Spread geniş")
599
+
600
+ # 6. Aşırı düşüş trendi DEĞİL (bıçak tutma koruması)
601
+ is_down = row.get("is_downtrend", 0)
602
+ chg_24 = row.get("chg_24h", 0)
603
+ total += 1
604
+ if not is_down or (pd.notna(pred_pct) and pred_pct > 0.5):
605
+ passed += 1
606
+ reasons.append(f"Trend OK")
607
+ else:
608
+ reasons.append(f"↓ düşüş trendi")
609
+
610
+ # Sinyal kararı
611
+ if passed >= 5:
612
+ signal = "🟢 GÜÇLÜ AL"
613
+ elif passed == 4:
614
+ signal = "🟡 ZAYIF AL"
615
+ elif passed == 3 and pd.notna(rsi) and rsi < 45:
616
+ signal = "🔵 İZLE"
617
+ elif is_down and pd.notna(pred_pct) and pred_pct < -2:
618
+ signal = "🔴 ALMA"
619
+ else:
620
+ signal = "⚪ BEKLE"
621
+
622
+ return signal, " · ".join(reasons)
623
+
624
+
625
  # ---------------------------------------------------------------------------
626
  # WALK-FORWARD BACKTEST
627
  # ---------------------------------------------------------------------------
628
 
629
 
630
+ def walk_forward_backtest(featured_df: pd.DataFrame, n_splits: int = 2) -> Dict:
631
  if featured_df.empty:
632
  return {"error": "Veri yok."}
633
 
634
  df = featured_df.sort_values("timestamp").copy()
635
  unique_times = df["timestamp"].sort_values().unique()
636
+
637
+ if len(unique_times) < 40:
638
+ return {"error": f"Yeterli zaman noktası yok ({len(unique_times)} < 40)."}
639
 
640
  test_size = len(unique_times) // (n_splits + 1)
641
+ if test_size < 5:
642
+ return {"error": f"Test boyutu çok küçük ({test_size} < 5)."}
643
+
644
  results = []
645
  all_predictions = []
646
 
 
649
  test_end_idx = test_start_idx + test_size
650
  if test_end_idx > len(unique_times):
651
  break
652
+ train_end_idx = test_start_idx - 24
653
  if train_end_idx <= 0:
654
  continue
655
 
 
659
  train_df = df[df["timestamp"].isin(train_times)]
660
  test_df = df[df["timestamp"].isin(test_times)]
661
 
662
+ if len(train_df) < 50 or len(test_df) < 5:
663
+ print(f"[BACKTEST] split {i+1}: train={len(train_df)}, test={len(test_df)} → atlandı")
664
  continue
665
 
666
  X_train, y_train, _ = prepare_model_data(train_df)
667
  X_test, y_test, meta_test = prepare_model_data(test_df)
668
 
669
+ if len(X_train) < 30 or len(X_test) < 3:
670
+ print(f"[BACKTEST] split {i+1}: X_train={len(X_train)}, X_test={len(X_test)} → atlandı")
671
  continue
672
 
673
  model = lgb.LGBMRegressor(
674
  objective="regression", metric="mae",
675
+ num_leaves=15, max_depth=6, min_child_samples=15,
676
+ learning_rate=0.05, n_estimators=200,
677
  reg_lambda=0.1, verbose=-1, random_state=42, n_jobs=-1,
678
  )
679
  model.fit(X_train, y_train)
 
684
 
685
  mae = mean_absolute_error(y_test_log, preds)
686
  rmse = np.sqrt(mean_squared_error(y_test_log, preds))
687
+ direction_correct = float(np.mean((y_test_log > 0) == (preds > 0)))
688
 
689
  baseline_preds = np.zeros_like(y_test_log)
690
  baseline_mae = mean_absolute_error(y_test_log, baseline_preds)
 
692
 
693
  results.append({
694
  "split": i + 1,
695
+ "train_samples": int(len(X_train)),
696
+ "test_samples": int(len(X_test)),
697
  "mae": round(float(mae), 6),
698
  "rmse": round(float(rmse), 6),
699
  "direction_accuracy": round(float(direction_correct), 4),
 
706
  test_meta["predicted_return"] = preds
707
  test_meta["actual_return"] = y_test_log.values
708
  all_predictions.append(test_meta)
709
+ print(f"[BACKTEST] split {i+1}: MAE={mae:.6f}, baseline={baseline_mae:.6f}, dir_acc={direction_correct:.3f}")
710
 
711
  if not results:
712
+ return {"error": "Backtest tamamlanamadı. Yeterli eğitim/test örneği oluşmadı."}
713
 
714
  if all_predictions:
715
  sim_df = pd.concat(all_predictions)
 
825
  if n_with_target < MIN_TRAIN_SAMPLES:
826
  self.status_message = (
827
  f"Yetersiz hedefli örnek: {n_with_target} < {MIN_TRAIN_SAMPLES}. "
828
+ f"Toplam: {diag.get('n_total', 0)}, grup: {diag.get('n_groups', 0)}."
 
829
  )
830
  return False
831
 
832
  if std_log < 1e-6:
833
  self.status_message = (
834
+ f"Hedef varyansı çok düşük (std_log={std_log:.8f}). Model öğrenemez."
 
 
835
  )
836
  return False
837
 
 
848
  sample_preds = self.model.predict(X.head(500))
849
  pred_std = float(np.std(sample_preds))
850
  if pred_std < 1.0:
851
+ self.status_message = f"⚠️ Model zayıf öğrendi (pred_std={pred_std:.2f} bp)."
852
  else:
853
  self.status_message = f"✓ Model eğitildi ({len(X)} örnek, pred_std={pred_std:.1f} bp)."
854
 
 
894
  total_cost = SETUP_FEE_RATE + TRANSACTION_TAX_RATE
895
  preds["net_return_pct"] = preds["predicted_return_pct"] - (total_cost * 100)
896
 
897
+ # Dip sinyali hesapla (her satır için)
898
+ dip_signals = preds.apply(compute_dip_signal, axis=1, result_type="expand")
899
+ preds["Dip Sinyali"] = dip_signals[0]
900
+ preds["Sinyal Detayı"] = dip_signals[1]
901
+
902
  return preds
903
 
904
  def get_backtest(self):
 
938
  "Ortalama Fiyat": f"{df['avg_price'].mean():.0f} silver",
939
  "Model Durumu": "Eğitildi" if pipeline.model else "Eğitilmedi",
940
  "Tahmin Sınırı": f"±%{MAX_LOG_RETURN * 100:.0f}",
941
+ "Dip Alım Eşiği": f"RSI<{DIP_RSI_MAX:.0f}, 7g%<{DIP_PERCENTILE_MAX*100:.0f}%",
942
  "Son Güncelleme": pipeline.last_update.strftime("%Y-%m-%d %H:%M") if pipeline.last_update else "—",
943
  }
944
  return pd.DataFrame(list(metrics.items()), columns=["Metrik", "Değer"])
 
964
  latest["Mevcut Fiyat"] = latest["avg_price"].round(0)
965
  latest["Hacim"] = latest["item_count"].fillna(0).astype(int)
966
  latest["Spread"] = (latest["sell_price_min"] - latest["buy_price_max"]).round(0)
967
+ latest["RSI"] = latest["rsi_14"].round(0)
968
+ latest["7g Konum %"] = (latest["price_pct_7d"] * 100).round(0)
969
+ latest["Trend"] = np.where(latest["is_downtrend"] == 1, "↓ Düşüş", "→ Yatay/Yukarı")
970
  latest["Likidite"] = pd.cut(latest["item_count"], bins=[-1, 10, 50, 200, np.inf],
971
  labels=["Düşük", "Orta", "İyi", "Yüksek"]).astype(str)
972
 
973
+ return latest[["Ürün", "Şehir", "Mevcut Fiyat", "Hacim", "Spread", "RSI", "7g Konum %", "Trend", "Likidite"]].sort_values("Hacim", ascending=False).head(100)
974
 
975
 
976
  def get_prediction_table():
 
985
  preds["Tahmini Fiyat (24s)"] = preds["predicted_price_24h"].round(0)
986
  preds["Tahmini Değişim %"] = preds["predicted_return_pct"].round(2)
987
  preds["Güvenilirlik"] = (preds["confidence"] * 100).round(0).astype(int).astype(str) + "%"
 
988
  preds["Net Getiri % (maliyet sonrası)"] = preds["net_return_pct"].round(2)
989
  preds["Likidite"] = pd.cut(preds["item_count"], bins=[-1, 10, 50, 200, np.inf],
990
  labels=["Düşük", "Orta", "İyi", "Yüksek"]).astype(str)
 
991
 
992
  cols = ["Ürün", "Şehir", "Mevcut Fiyat", "Tahmini Fiyat (24s)",
993
+ "Tahmini Değişim %", "Güvenilirlik", "Likidite",
994
+ "Net Getiri % (maliyet sonrası)", "Dip Sinyali"]
995
  return preds[cols].sort_values("Tahmini Değişim %", ascending=False)
996
 
997
 
998
+ def get_dip_alim_table():
999
+ """Sadece DİP ALIM sinyali veren ürünler, sinyal gücüne göre sıralı."""
1000
+ preds = pipeline.get_latest_predictions()
1001
+ if preds.empty:
1002
+ return pd.DataFrame({"Bilgi": ["Tahmin üretilemedi."]})
1003
+
1004
+ # Sadece AL/İZLE sinyalleri
1005
+ dip_only = preds[preds["Dip Sinyali"].isin(
1006
+ ["🟢 GÜÇLÜ AL", "🟡 ZAYIF AL", "🔵 İZLE"]
1007
+ )].copy()
1008
+
1009
+ if dip_only.empty:
1010
+ return pd.DataFrame({"Bilgi": [
1011
+ "Şu anda dip alım sinyali yok. Daha fazla veri veya farklı eşikler gerekebilir."
1012
+ ]})
1013
+
1014
+ # Sıralama: sinyal gücü > güvenilirlik > tahmini değişim
1015
+ signal_order = {"🟢 GÜÇLÜ AL": 0, "🟡 ZAYIF AL": 1, "🔵 İZLE": 2}
1016
+ dip_only["_sig_rank"] = dip_only["Dip Sinyali"].map(signal_order)
1017
+
1018
+ dip_only["Ürün"] = dip_only["item_id"].apply(item_display_name)
1019
+ dip_only["Şehir"] = dip_only["city"]
1020
+ dip_only["Mevcut Fiyat"] = dip_only["avg_price"].round(0)
1021
+ dip_only["RSI"] = dip_only["rsi_14"].round(0)
1022
+ dip_only["7g Konum %"] = (dip_only["price_pct_7d"] * 100).round(0)
1023
+ dip_only["Trend"] = np.where(dip_only["is_downtrend"] == 1, "↓", "→")
1024
+ dip_only["Hacim×Ort"] = dip_only["volume_ratio"].round(1)
1025
+ dip_only["Tahmini Değişim %"] = dip_only["predicted_return_pct"].round(2)
1026
+ dip_only["Net Getiri %"] = dip_only["net_return_pct"].round(2)
1027
+ dip_only["Güvenilirlik"] = (dip_only["confidence"] * 100).round(0).astype(int).astype(str) + "%"
1028
+ dip_only["Spread"] = dip_only["spread"].round(0)
1029
+
1030
+ dip_only = dip_only.sort_values(["_sig_rank", "confidence"], ascending=[True, False])
1031
+
1032
+ cols = ["Dip Sinyali", "Ürün", "Şehir", "Mevcut Fiyat", "RSI", "7g Konum %",
1033
+ "Trend", "Hacim×Ort", "Tahmini Değişim %", "Net Getiri %",
1034
+ "Güvenilirlik", "Spread", "Sinyal Detayı"]
1035
+ return dip_only[cols].head(50)
1036
+
1037
+
1038
  def get_arbitrage_table():
1039
  if pipeline.prices_df.empty:
1040
  return pd.DataFrame({"Bilgi": ["Anlık fiyat yok."]})
 
1154
  _safe(get_dashboard_metrics, pd.DataFrame()),
1155
  _safe(get_analysis_table, pd.DataFrame()),
1156
  _safe(get_prediction_table, pd.DataFrame()),
1157
+ _safe(get_dip_alim_table, pd.DataFrame()),
1158
  _safe(get_arbitrage_table, pd.DataFrame()),
1159
  _safe(get_model_performance_plot, go.Figure().update_layout(title="—", template="plotly_dark")),
1160
  bt_table, bt_fig, sim_table,
 
1171
 
1172
  return (
1173
  _safe(get_dashboard_metrics, pd.DataFrame({"Metrik": ["Durum"], "Değer": ["Hazır"]})),
1174
+ pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
1175
  go.Figure().update_layout(title="Yenile butonuna basın", template="plotly_dark"),
1176
  pd.DataFrame(),
1177
  go.Figure().update_layout(title="Veri gerekli", template="plotly_dark"),
 
1191
  """
1192
  # 🦅 GOSHAWK ALBION MARKET AI
1193
  **Profesyonel Market Intelligence Dashboard** | Avrupa Sunucusu
1194
+ *LightGBM tabanlı 24 saatlik fiyat tahmini + kural-tabanlı dip alım sinyali.*
 
 
1195
 
1196
+ **⚠️ Risk Uyarısı:** Bu uygulama yatırım tavsiyesi değildir. Model tahminleri belirsizlik içerir.
1197
+ "Dip alım" sinyali **sadece** istatistiksel bir filtredir; fiyat daha da düşebilir.
1198
  """
1199
  )
1200
 
 
1206
  with gr.TabItem("📊 Dashboard"):
1207
  dashboard_table = gr.Dataframe(label="Özet & Teşhis", interactive=False, wrap=True)
1208
  gr.Markdown(
1209
+ "### Dip Alım Sinyali Mantığı\n"
1210
+ "**Fiyat düşerken almak** = riskli. Model bunu doğru şekilde **filtreler**:\n\n"
1211
+ "**6 koşuldan en az 4'ü sağlanırsa** AL sinyali:\n"
1212
+ "1. RSI < 40 (aşırı satım)\n"
1213
+ "2. 7 günlük aralıkta alt %25'te\n"
1214
+ "3. Hacim ortalamanın üstünde (talep dönüyor)\n"
1215
+ "4. Model tahmini çok negatif değil (> -1%)\n"
1216
+ "5. Spread dar (likit)\n"
1217
+ "6. Aşırı düşüş trendi DEĞİL (bıçak tutma koruması)\n\n"
1218
+ "### Sinyaller\n"
1219
+ "- 🟢 **GÜÇLÜ AL:** 5+ koşul\n"
1220
+ "- 🟡 **ZAYIF AL:** 4 koşul\n"
1221
+ "- 🔵 **İZLE:** 3 koşul + RSI<45\n"
1222
+ "- 🔴 **ALMA:** düşüş trendi + model negatif\n"
1223
+ "- ⚪ **BEKLE:** diğer\n\n"
1224
  "### Riskler\n"
1225
+ "- Model tahminleri olasılıksaldır\n"
1226
+ "- Dip alım her zaman işe yaramaz, stop-loss kullanın"
1227
  )
1228
 
1229
+ with gr.TabItem("🎯 DİP ALIM FIRSATLARI"):
1230
+ dip_alim_table = gr.Dataframe(label="Dip Alım Sinyalleri (Fiyat düşerken al)", interactive=False)
1231
+ gr.Markdown(
1232
+ "*Bu tablo **sadece** dip alım sinyali veren ürünleri gösterir. "
1233
+ "🟢 GÜÇLÜ AL en yüksek öncelikli. "
1234
+ "**Sinyal Detayı** sütunu hangi koşulların sağlandığını gösterir.*\n\n"
1235
+ "**⚠️ UYARI:** Fiyat düşerken almak, trend dönerse kârlıdır. "
1236
+ "Ama trend devam ederse zarar eder. "
1237
+ "Sadece **likit** ürünlerde ve **küçük pozisyonlarla** deneyin."
1238
+ )
1239
 
1240
+ with gr.TabItem("🔮 24s Tahmin"):
1241
+ prediction_table = gr.Dataframe(label="Tüm Tahminler + Dip Sinyali", interactive=False)
1242
+
1243
+ with gr.TabItem("📦 100 Ürün Analizi"):
1244
+ analysis_table = gr.Dataframe(label="Ürün Analizi (RSI, Trend, Konum)", interactive=False)
1245
 
1246
  with gr.TabItem("🚚 Arbitraj"):
1247
  arbitrage_table = gr.Dataframe(label="Arbitraj Fırsatları", interactive=False)
 
1252
  with gr.TabItem("🧪 Backtest"):
1253
  backtest_table = gr.Dataframe(label="Split Sonuçları", interactive=False)
1254
  backtest_plot = gr.Plot(label="MAE Karşılaştırması")
1255
+ simulation_table = gr.Dataframe(label="Simülasyon (maliyetler sonrası)", interactive=False)
1256
 
1257
  with gr.TabItem("🗄️ Veri Yönetimi"):
1258
  gr.Markdown(
1259
  "**API:** `europe.albion-online-data.com/api/v2`\n"
1260
  "**Time-scale:** 1h (saatlik)\n"
1261
+ "**Model:** `gos_hawk_data/models/gos_hawk_model.pkl`\n\n"
1262
+ "**Dip Alım Parametreleri:** "
1263
+ f"RSI<{DIP_RSI_MAX}, 7g%<{DIP_PERCENTILE_MAX*100:.0f}%, "
1264
+ f"Hacim≥{DIP_MIN_VOLUME_RATIO}, Spread<%{DIP_MAX_SPREAD_PCT*100:.0f}"
1265
  )
1266
  csv_files = gr.Dataframe(label="CSV Dosyaları", interactive=False,
1267
  headers=["Dosya", "Boyut (KB)", "Son Değişiklik"])
 
1270
  fn=refresh_data_ui,
1271
  inputs=[],
1272
  outputs=[status_text, dashboard_table, analysis_table, prediction_table,
1273
+ dip_alim_table, arbitrage_table, performance_plot, backtest_table,
1274
+ backtest_plot, simulation_table, csv_files],
1275
  )
1276
 
1277
  demo.load(
1278
  fn=initial_load,
1279
  inputs=[],
1280
+ outputs=[dashboard_table, analysis_table, prediction_table, dip_alim_table,
1281
+ arbitrage_table, performance_plot, backtest_table, backtest_plot,
1282
+ simulation_table, csv_files],
1283
  )
1284
 
1285