GoshawkVortexAI commited on
Commit
ad817d4
Β·
verified Β·
1 Parent(s): 6c9cb7f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +347 -199
app.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import gradio as gr
3
  import pandas as pd
4
  import numpy as np
@@ -8,233 +7,382 @@ from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor, A
8
  from sklearn.linear_model import Ridge
9
  from sklearn.svm import SVR
10
  from sklearn.preprocessing import StandardScaler
 
 
 
 
11
  import warnings
12
  warnings.filterwarnings('ignore')
13
 
14
- def get_forex_data(symbol="EURUSD=X", period="60d"):
15
- """Forex verilerini indir"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  try:
17
  data = yf.download(symbol, period=period, interval="1d", progress=False)
 
 
18
  return data
19
- except Exception as e:
20
  return None
21
 
22
  def create_features(data):
23
- """Teknik gâstergeler ve âzellikler oluştur"""
24
  df = data.copy()
25
-
26
- # Temel ΓΆzellikler
27
- df['Returns'] = df['Close'].pct_change()
28
- df['High_Low'] = df['High'] - df['Low']
29
  df['Close_Open'] = df['Close'] - df['Open']
30
-
31
- # Hareketli ortalamalar
32
- df['SMA_5'] = df['Close'].rolling(window=5).mean()
33
- df['SMA_10'] = df['Close'].rolling(window=10).mean()
34
- df['SMA_20'] = df['Close'].rolling(window=20).mean()
35
-
36
- # RSI
37
  delta = df['Close'].diff()
38
- gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
39
- loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
40
- rs = gain / loss
41
- df['RSI'] = 100 - (100 / (1 + rs))
42
-
43
- # MACD
44
  exp1 = df['Close'].ewm(span=12, adjust=False).mean()
45
  exp2 = df['Close'].ewm(span=26, adjust=False).mean()
46
- df['MACD'] = exp1 - exp2
47
-
48
- # Bollinger Bands
49
- df['BB_middle'] = df['Close'].rolling(window=20).mean()
50
- df['BB_std'] = df['Close'].rolling(window=20).std()
51
- df['BB_upper'] = df['BB_middle'] + (df['BB_std'] * 2)
52
- df['BB_lower'] = df['BB_middle'] - (df['BB_std'] * 2)
53
-
54
- # Lag features
55
- for i in range(1, 6):
 
 
56
  df[f'Lag_{i}'] = df['Close'].shift(i)
57
-
58
- df = df.dropna()
59
- return df
60
-
61
- def train_ensemble_models(df):
62
- """5 farklı model eğit"""
63
- feature_cols = ['Returns', 'High_Low', 'Close_Open', 'SMA_5', 'SMA_10',
64
- 'SMA_20', 'RSI', 'MACD', 'BB_middle', 'BB_upper', 'BB_lower',
65
- 'Lag_1', 'Lag_2', 'Lag_3', 'Lag_4', 'Lag_5']
66
-
67
- X = df[feature_cols].values
 
 
 
 
 
 
 
 
 
 
 
 
68
  y = df['Close'].values
69
-
70
- # Train/test split (son 5 gοΏ½οΏ½n test iΓ§in)
71
- split_idx = -5
72
- X_train, X_test = X[:split_idx], X[split_idx:]
73
- y_train, y_test = y[:split_idx], y[split_idx:]
74
-
75
- # Normalizasyon
76
  scaler = StandardScaler()
77
- X_train_scaled = scaler.fit_transform(X_train)
78
- X_test_scaled = scaler.transform(X_test)
79
-
80
- # 5 Ensemble Model
81
- models = {
82
- 'Random Forest': RandomForestRegressor(n_estimators=100, random_state=42),
83
- 'Gradient Boosting': GradientBoostingRegressor(n_estimators=100, random_state=42),
84
- 'AdaBoost': AdaBoostRegressor(n_estimators=100, random_state=42),
85
- 'Ridge': Ridge(alpha=1.0),
86
- 'SVR': SVR(kernel='rbf', C=100, gamma=0.1)
87
- }
88
-
89
- predictions = {}
90
- for name, model in models.items():
91
- model.fit(X_train_scaled, y_train)
92
- pred = model.predict(X_test_scaled)
93
- predictions[name] = pred
94
-
95
- return models, scaler, predictions, y_test, feature_cols
96
-
97
- def predict_next_day(models, scaler, df, feature_cols):
98
- """Ertesi gΓΌn tahmini yap"""
99
- latest_features = df[feature_cols].iloc[-1].values.reshape(1, -1)
100
- latest_features_scaled = scaler.transform(latest_features)
101
-
102
- predictions = {}
103
- for name, model in models.items():
104
- pred = model.predict(latest_features_scaled)[0]
105
- predictions[name] = pred
106
-
107
- # Ensemble ortalamasΔ±
108
- ensemble_pred = np.mean(list(predictions.values()))
109
-
110
- return predictions, ensemble_pred
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  def predict_eurusd():
113
- """Ana tahmin fonksiyonu"""
114
  try:
115
- # Veri indir
116
  data = get_forex_data()
117
- if data is None or len(data) == 0:
118
- return "❌ Veri indirilemedi. Lütfen internet bağlantınızı kontrol edin.", None
119
-
120
- # Γ–zellikler oluştur
121
- df = create_features(data)
122
-
123
- # Modelleri eğit
124
- models, scaler, test_predictions, y_test, feature_cols = train_ensemble_models(df)
125
-
126
- # Ertesi gΓΌn tahmini
127
- next_day_preds, ensemble_pred = predict_next_day(models, scaler, df, feature_cols)
128
-
129
- # Mevcut fiyat
130
- current_price = float(df['Close'].iloc[-1])
131
-
132
- # SonuΓ§larΔ± hazΔ±rla
133
- result = f"""πŸ”„ GÜNCELLEME ZAMANI: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
134
-
135
- πŸ“Š MEVCUT EUR/USD FΔ°YATI: {current_price:.5f}
136
-
137
- 🎯 ERTESİ GÜN TAHMİNLERİ:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- """
140
-
141
- for model_name, pred in next_day_preds.items():
142
- pred_value = float(pred)
143
- change = ((pred_value - current_price) / current_price) * 100
144
- arrow = "πŸ“ˆ" if change > 0 else "πŸ“‰"
145
- result += f"{arrow} {model_name}: {pred_value:.5f} ({change:+.2f}%)\n"
146
-
147
- ensemble_pred_value = float(ensemble_pred)
148
- ensemble_change = ((ensemble_pred_value - current_price) / current_price) * 100
149
- ensemble_arrow = "πŸ“ˆ" if ensemble_change > 0 else "πŸ“‰"
150
-
151
- result += f"\n{ensemble_arrow} πŸ† ENSEMBLE ORTALAMA: {ensemble_pred_value:.5f} ({ensemble_change:+.2f}%)\n"
152
-
153
- # Model performanslarΔ±
154
- result += f"\n\nπŸ“ˆ MODEL PERFORMANSLARI (Son 5 GΓΌn):\n"
155
- for i, (model_name, preds) in enumerate(test_predictions.items()):
156
- mse = float(np.mean((preds - y_test) ** 2))
157
- mae = float(np.mean(np.abs(preds - y_test)))
158
- result += f"β€’ {model_name}: MAE={mae:.5f}, MSE={mse:.8f}\n"
159
-
160
- # Grafik iΓ§in veri hazΔ±rla
161
- import matplotlib.pyplot as plt
162
-
163
- fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
164
-
165
- # Üst grafik: Fiyat geçmişi
166
- last_30_days = df.tail(30)
167
- ax1.plot(range(len(last_30_days)), last_30_days['Close'].values, label='GerΓ§ek Fiyat', linewidth=2)
168
- ax1.axhline(y=current_price, color='gray', linestyle='--', alpha=0.5, label='Mevcut Fiyat')
169
- ax1.axhline(y=ensemble_pred_value, color='red', linestyle='--', alpha=0.7, label='Tahmin')
170
- ax1.set_title('EUR/USD Son 30 GΓΌn Fiyat Hareketi', fontsize=14, fontweight='bold')
171
- ax1.set_xlabel('GΓΌn')
172
- ax1.set_ylabel('Fiyat')
173
- ax1.legend()
174
- ax1.grid(True, alpha=0.3)
175
-
176
- # Alt grafik: Model tahminleri karşılaştırması
177
- model_names = list(next_day_preds.keys())
178
- model_values = [float(v) for v in next_day_preds.values()]
179
- colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8']
180
-
181
- bars = ax2.barh(model_names, model_values, color=colors)
182
- ax2.axvline(x=current_price, color='black', linestyle='--', linewidth=2, label='Mevcut Fiyat')
183
- ax2.set_title('Model Tahminleri Karşılaştırması', fontsize=14, fontweight='bold')
184
- ax2.set_xlabel('Tahmin Edilen Fiyat')
185
- ax2.legend()
186
- ax2.grid(True, alpha=0.3, axis='x')
187
-
188
- # Değerleri barların üzerine yaz
189
- for i, (bar, val) in enumerate(zip(bars, model_values)):
190
- change = ((val - current_price) / current_price) * 100
191
- ax2.text(val, i, f' {val:.5f} ({change:+.2f}%)',
192
- va='center', fontsize=9, fontweight='bold')
193
-
194
- plt.tight_layout()
195
-
196
- return result, fig
197
-
198
  except Exception as e:
199
- import traceback
200
- error_msg = f"❌ Hata oluştu: {str(e)}\n\n{traceback.format_exc()}"
201
- return error_msg, None
202
-
203
- # Gradio arayΓΌzΓΌ
204
- with gr.Blocks(title="EUR/USD Fiyat Tahmini", theme=gr.themes.Soft()) as app:
205
- gr.Markdown("""
206
- # πŸ’Ή EUR/USD Fiyat Tahmin Sistemi
207
- ## 5 Ensemble Model ile Otomatik Forex Tahmini
208
-
209
- Bu uygulama **Random Forest, Gradient Boosting, AdaBoost, Ridge Regression ve SVR** modellerini kullanarak EUR/USD paritesi iΓ§in ertesi gΓΌn fiyat tahmini yapar.
210
-
211
- πŸ”„ **Yenile** butonuna tΔ±klayarak gΓΌncel verileri Γ§ekip yeni tahmin alabilirsiniz.
212
- """)
213
-
214
- with gr.Row():
215
- predict_btn = gr.Button("πŸš€ Tahmin Yap / Yenile", variant="primary", scale=2)
216
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  with gr.Row():
218
- output_text = gr.Textbox(label="πŸ“Š Tahmin SonuΓ§larΔ±", lines=25)
219
-
220
  with gr.Row():
221
- output_plot = gr.Plot(label="πŸ“ˆ Grafikler")
222
-
 
 
 
 
 
 
 
 
223
  predict_btn.click(
224
  fn=predict_eurusd,
225
  inputs=[],
226
- outputs=[output_text, output_plot]
227
  )
228
-
229
- gr.Markdown("""
230
- ---
231
- ### πŸ“ Notlar:
232
- - Veriler Yahoo Finance'ten otomatik olarak Γ§ekilir
233
- - Tahminler son 60 gΓΌnlΓΌk veriye dayanΔ±r
234
- - 5 farklı makine âğrenmesi modeli kullanılır
235
- - Ensemble tahmini, tΓΌm modellerin ortalamasΔ±dΔ±r
236
- - **Finansal tavsiye değildir, sadece eğitim amaçlıdır**
237
- """)
238
 
239
  if __name__ == "__main__":
240
  app.launch()
 
 
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
 
7
  from sklearn.linear_model import Ridge
8
  from sklearn.svm import SVR
9
  from sklearn.preprocessing import StandardScaler
10
+ import matplotlib.pyplot as plt
11
+ import matplotlib.patches as mpatches
12
+ from matplotlib.gridspec import GridSpec
13
+ import matplotlib.ticker as mticker
14
  import warnings
15
  warnings.filterwarnings('ignore')
16
 
17
+ # ─── Stil sabitleri ───────────────────────────────────────────────────────────
18
+ BG = "#0A0E17"
19
+ PANEL = "#111827"
20
+ BORDER = "#1E293B"
21
+ ACCENT = "#3B82F6"
22
+ GREEN = "#10B981"
23
+ RED = "#EF4444"
24
+ AMBER = "#F59E0B"
25
+ MUTED = "#64748B"
26
+ TEXT = "#F1F5F9"
27
+ SUBTEXT = "#94A3B8"
28
+
29
+ MODEL_COLORS = ["#3B82F6", "#8B5CF6", "#10B981", "#F59E0B", "#EC4899"]
30
+
31
+ # ─── Veri & feature mΓΌhendisliği ─────────────────────────────────────────────
32
+ def get_forex_data(symbol="EURUSD=X", period="90d"):
33
  try:
34
  data = yf.download(symbol, period=period, interval="1d", progress=False)
35
+ if data is None or len(data) < 40:
36
+ return None
37
  return data
38
+ except Exception:
39
  return None
40
 
41
  def create_features(data):
 
42
  df = data.copy()
43
+ df['Returns'] = df['Close'].pct_change()
44
+ df['High_Low'] = df['High'] - df['Low']
 
 
45
  df['Close_Open'] = df['Close'] - df['Open']
46
+
47
+ for w in [5, 10, 20, 50]:
48
+ df[f'SMA_{w}'] = df['Close'].rolling(window=w).mean()
49
+
 
 
 
50
  delta = df['Close'].diff()
51
+ gain = (delta.where(delta > 0, 0)).rolling(14).mean()
52
+ loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
53
+ df['RSI'] = 100 - (100 / (1 + gain / loss))
54
+
 
 
55
  exp1 = df['Close'].ewm(span=12, adjust=False).mean()
56
  exp2 = df['Close'].ewm(span=26, adjust=False).mean()
57
+ df['MACD'] = exp1 - exp2
58
+ df['Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
59
+
60
+ df['BB_middle'] = df['Close'].rolling(20).mean()
61
+ df['BB_std'] = df['Close'].rolling(20).std()
62
+ df['BB_upper'] = df['BB_middle'] + df['BB_std'] * 2
63
+ df['BB_lower'] = df['BB_middle'] - df['BB_std'] * 2
64
+ df['BB_width'] = (df['BB_upper'] - df['BB_lower']) / df['BB_middle']
65
+
66
+ df['ATR'] = (df['High'] - df['Low']).rolling(14).mean()
67
+
68
+ for i in range(1, 8):
69
  df[f'Lag_{i}'] = df['Close'].shift(i)
70
+
71
+ return df.dropna()
72
+
73
+ FEATURE_COLS = [
74
+ 'Returns','High_Low','Close_Open',
75
+ 'SMA_5','SMA_10','SMA_20','SMA_50',
76
+ 'RSI','MACD','Signal',
77
+ 'BB_middle','BB_upper','BB_lower','BB_width',
78
+ 'ATR',
79
+ 'Lag_1','Lag_2','Lag_3','Lag_4','Lag_5','Lag_6','Lag_7'
80
+ ]
81
+
82
+ # ─── Model katmanΔ± ────────────────────────────────────────────────────────────
83
+ MODELS_DEF = {
84
+ 'Random Forest': RandomForestRegressor(n_estimators=200, random_state=42, n_jobs=-1),
85
+ 'Gradient Boosting': GradientBoostingRegressor(n_estimators=200, learning_rate=0.05, random_state=42),
86
+ 'AdaBoost': AdaBoostRegressor(n_estimators=150, random_state=42),
87
+ 'Ridge': Ridge(alpha=0.5),
88
+ 'SVR': SVR(kernel='rbf', C=200, gamma=0.05, epsilon=0.0001),
89
+ }
90
+
91
+ def train_and_evaluate(df):
92
+ X = df[FEATURE_COLS].values
93
  y = df['Close'].values
94
+ split = -10
95
+
96
+ X_train, X_test = X[:split], X[split:]
97
+ y_train, y_test = y[:split], y[split:]
98
+
 
 
99
  scaler = StandardScaler()
100
+ Xtr = scaler.fit_transform(X_train)
101
+ Xte = scaler.transform(X_test)
102
+
103
+ trained, test_preds = {}, {}
104
+ for name, m in MODELS_DEF.items():
105
+ m.fit(Xtr, y_train)
106
+ test_preds[name] = m.predict(Xte)
107
+ trained[name] = m
108
+
109
+ return trained, scaler, test_preds, y_test
110
+
111
+ def predict_next(trained, scaler, df):
112
+ x = df[FEATURE_COLS].iloc[-1].values.reshape(1, -1)
113
+ xs = scaler.transform(x)
114
+ preds = {n: float(m.predict(xs)[0]) for n, m in trained.items()}
115
+ ensemble = float(np.mean(list(preds.values())))
116
+ return preds, ensemble
117
+
118
+ # ─── Grafik ───────────────────────────────────────────────────────────────────
119
+ def build_figure(df, next_preds, ensemble_pred, test_preds, y_test):
120
+ plt.rcParams.update({
121
+ 'font.family': 'monospace',
122
+ 'text.color': TEXT,
123
+ 'axes.labelcolor': SUBTEXT,
124
+ 'xtick.color': SUBTEXT,
125
+ 'ytick.color': SUBTEXT,
126
+ 'figure.facecolor': BG,
127
+ 'axes.facecolor': PANEL,
128
+ 'axes.edgecolor': BORDER,
129
+ 'grid.color': BORDER,
130
+ 'grid.linewidth': 0.5,
131
+ })
132
+
133
+ fig = plt.figure(figsize=(14, 12))
134
+ gs = GridSpec(3, 2, figure=fig, hspace=0.45, wspace=0.35)
135
+
136
+ ax1 = fig.add_subplot(gs[0, :]) # Fiyat + BB
137
+ ax2 = fig.add_subplot(gs[1, 0]) # RSI
138
+ ax3 = fig.add_subplot(gs[1, 1]) # MACD
139
+ ax4 = fig.add_subplot(gs[2, :]) # Model tahminleri + hata
140
+
141
+ tail = df.tail(45)
142
+ idx = np.arange(len(tail))
143
+ cp = float(df['Close'].iloc[-1])
144
+
145
+ # ── Panel 1: Fiyat ──────────────────────────────────────────────
146
+ ax1.fill_between(idx, tail['BB_lower'].values, tail['BB_upper'].values,
147
+ alpha=0.08, color=ACCENT, label='Bollinger BandΔ±')
148
+ ax1.plot(idx, tail['BB_upper'].values, lw=0.5, color=ACCENT, alpha=0.4)
149
+ ax1.plot(idx, tail['BB_lower'].values, lw=0.5, color=ACCENT, alpha=0.4)
150
+ ax1.plot(idx, tail['Close'].values, lw=1.8, color=TEXT, label='Kapanış')
151
+ ax1.plot(idx, tail['SMA_20'].values, lw=1.1, color=AMBER, alpha=0.8, label='SMA 20', ls='--')
152
+ ax1.plot(idx, tail['SMA_50'].values, lw=1.1, color=MUTED, alpha=0.8, label='SMA 50', ls=':')
153
+
154
+ # Ensemble tahmini noktasΔ±
155
+ arrow_x = len(idx) + 0.5
156
+ ax1.annotate(
157
+ f'β–Ά {ensemble_pred:.5f}',
158
+ xy=(len(idx) - 1, cp),
159
+ xytext=(len(idx) + 1, ensemble_pred),
160
+ arrowprops=dict(arrowstyle='->', color=GREEN, lw=1.5),
161
+ color=GREEN, fontsize=9, fontweight='bold',
162
+ )
163
+ ax1.axhline(y=cp, color=MUTED, lw=0.8, ls='--', alpha=0.5)
164
+
165
+ ax1.set_title('EUR/USD Β· Son 45 GΓΌn + Sonraki GΓΌn Tahmini',
166
+ fontsize=11, color=TEXT, pad=10, loc='left', fontweight='bold')
167
+ ax1.legend(fontsize=7, framealpha=0.15, labelcolor=SUBTEXT, ncol=4)
168
+ ax1.set_ylabel('Fiyat', fontsize=8)
169
+ ax1.yaxis.set_major_formatter(mticker.FormatStrFormatter('%.4f'))
170
+
171
+ # ── Panel 2: RSI ─────────────────────────────────────────────────
172
+ rsi_tail = tail['RSI'].values
173
+ ax2.plot(idx, rsi_tail, lw=1.4, color=ACCENT)
174
+ ax2.fill_between(idx, rsi_tail, 50, where=(rsi_tail >= 50), alpha=0.15, color=GREEN)
175
+ ax2.fill_between(idx, rsi_tail, 50, where=(rsi_tail < 50), alpha=0.15, color=RED)
176
+ ax2.axhline(70, color=RED, lw=0.8, ls='--', alpha=0.6)
177
+ ax2.axhline(30, color=GREEN, lw=0.8, ls='--', alpha=0.6)
178
+ ax2.set_ylim(0, 100)
179
+ ax2.set_title('RSI (14)', fontsize=9, color=TEXT, pad=6, loc='left')
180
+ ax2.set_ylabel('RSI', fontsize=8)
181
+ for level, lbl in [(70, 'OB'), (30, 'OS')]:
182
+ ax2.text(len(idx), level, lbl, color=SUBTEXT, fontsize=7, va='center')
183
 
184
+ # ── Panel 3: MACD ────────────────────────────────────────────────
185
+ macd = tail['MACD'].values
186
+ signal = tail['Signal'].values
187
+ hist = macd - signal
188
+ colors_hist = [GREEN if v >= 0 else RED for v in hist]
189
+ ax3.bar(idx, hist, color=colors_hist, alpha=0.5, width=0.8, label='Histogram')
190
+ ax3.plot(idx, macd, lw=1.3, color=ACCENT, label='MACD')
191
+ ax3.plot(idx, signal, lw=1.1, color=AMBER, label='Signal', ls='--')
192
+ ax3.axhline(0, color=MUTED, lw=0.7)
193
+ ax3.set_title('MACD (12/26/9)', fontsize=9, color=TEXT, pad=6, loc='left')
194
+ ax3.legend(fontsize=7, framealpha=0.1, labelcolor=SUBTEXT)
195
+
196
+ # ── Panel 4: Model karşılaştΔ±rmasΔ± ───────────────────────────────
197
+ names = list(next_preds.keys())
198
+ values = [next_preds[n] for n in names]
199
+ maes = [float(np.mean(np.abs(test_preds[n] - y_test))) for n in names]
200
+
201
+ x_pos = np.arange(len(names))
202
+ bar_colors = [GREEN if v > cp else RED for v in values]
203
+
204
+ bars = ax4.bar(x_pos, values, color=bar_colors, alpha=0.75, width=0.55, zorder=3)
205
+ ax4.axhline(y=cp, color=TEXT, lw=1.2, ls='--', alpha=0.7, label=f'Mevcut {cp:.5f}', zorder=4)
206
+ ax4.axhline(y=ensemble_pred, color=ACCENT, lw=1.5, ls='-', alpha=0.9,
207
+ label=f'Ensemble {ensemble_pred:.5f}', zorder=4)
208
+
209
+ for bar, val, mae in zip(bars, values, maes):
210
+ pct = (val - cp) / cp * 100
211
+ sign = '+' if pct >= 0 else ''
212
+ ax4.text(bar.get_x() + bar.get_width()/2, val + 0.00003,
213
+ f'{sign}{pct:.3f}%\nMAE {mae:.5f}',
214
+ ha='center', va='bottom', fontsize=7.5, color=TEXT, fontweight='bold')
215
+
216
+ ax4.set_xticks(x_pos)
217
+ ax4.set_xticklabels(names, fontsize=9)
218
+ ax4.set_title('5 Model Tahmini vs Mevcut Fiyat', fontsize=9, color=TEXT, pad=6, loc='left')
219
+ ax4.legend(fontsize=8, framealpha=0.15, labelcolor=SUBTEXT)
220
+ ax4.yaxis.set_major_formatter(mticker.FormatStrFormatter('%.4f'))
221
+
222
+ fig.suptitle('EUR/USD Β· ENSEMBLE TAHMΔ°N SΔ°STEMΔ°',
223
+ fontsize=13, color=TEXT, fontweight='bold', y=0.98)
224
+
225
+ return fig
226
+
227
+ # ─── Ana tahmin fonksiyonu ───────────────────────────────────────────────────
228
  def predict_eurusd():
229
+ import traceback
230
  try:
 
231
  data = get_forex_data()
232
+ if data is None:
233
+ return "❌ Veri indirilemedi.", None
234
+
235
+ df = create_features(data)
236
+ trained, scaler, test_preds, y_test = train_and_evaluate(df)
237
+ next_preds, ensemble = predict_next(trained, scaler, df)
238
+
239
+ cp = float(df['Close'].iloc[-1])
240
+ ens_chg = (ensemble - cp) / cp * 100
241
+ direction = "YUKARI β–²" if ens_chg > 0 else "AŞAĞI β–Ό"
242
+ dir_color = "πŸ“ˆ" if ens_chg > 0 else "πŸ“‰"
243
+
244
+ rsi_val = float(df['RSI'].iloc[-1])
245
+ macd_val = float(df['MACD'].iloc[-1])
246
+ sig_val = float(df['Signal'].iloc[-1])
247
+ rsi_status = "Aşırı Alım" if rsi_val > 70 else ("Aşırı Satım" if rsi_val < 30 else "Nâtr")
248
+ macd_cross = "Boğa" if macd_val > sig_val else "Ayı"
249
+
250
+ lines = [
251
+ f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
252
+ f" EUR/USD Β· ENSEMBLE TAHMΔ°N RAPORU",
253
+ f" {datetime.now().strftime('%d %b %Y %H:%M:%S')}",
254
+ f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
255
+ f"",
256
+ f" MEVCUT FΔ°YAT : {cp:.5f}",
257
+ f" ENSEMBLE : {ensemble:.5f} ({ens_chg:+.3f}%) {dir_color} {direction}",
258
+ f"",
259
+ f" ─── TEKNΔ°K DURUM ───────────────────────────",
260
+ f" RSI (14) : {rsi_val:.1f} β†’ {rsi_status}",
261
+ f" MACD Sinyali : {macd_cross} kesişimi",
262
+ f" BB Pozisyonu : {float(df['BB_lower'].iloc[-1]):.5f} ↔ {float(df['BB_upper'].iloc[-1]):.5f}",
263
+ f"",
264
+ f" ─── MODEL TAHMΔ°NLERΔ° ───────────────────────",
265
+ ]
266
+
267
+ for name, val in next_preds.items():
268
+ chg = (val - cp) / cp * 100
269
+ mae = float(np.mean(np.abs(test_preds[name] - y_test)))
270
+ arrow = "β–²" if chg > 0 else "β–Ό"
271
+ lines.append(f" {name:<20s}: {val:.5f} ({chg:+.3f}%) {arrow} MAE={mae:.5f}")
272
+
273
+ lines += [
274
+ f"",
275
+ f" ─── BACKTEST (Son 10 GΓΌn) ──────────────────",
276
+ ]
277
+
278
+ ens_test = np.mean([test_preds[n] for n in test_preds], axis=0)
279
+ ens_mae = float(np.mean(np.abs(ens_test - y_test)))
280
+ ens_rmse = float(np.sqrt(np.mean((ens_test - y_test) ** 2)))
281
+ hit_rate = float(np.mean(np.sign(np.diff(np.append([y_test[0]], ens_test))) ==
282
+ np.sign(np.diff(y_test)))) * 100
283
+
284
+ lines += [
285
+ f" Ensemble MAE : {ens_mae:.6f}",
286
+ f" Ensemble RMSE : {ens_rmse:.6f}",
287
+ f" Yân Doğruluğu : {hit_rate:.1f}%",
288
+ f"",
289
+ f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
290
+ f" ⚠ Yalnızca eğitim amaçlıdır. Finansal tavsiye değildir.",
291
+ f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
292
+ ]
293
+
294
+ output_text = "\n".join(lines)
295
+ fig = build_figure(df, next_preds, ensemble, test_preds, y_test)
296
+
297
+ return output_text, fig
298
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  except Exception as e:
300
+ return f"❌ Hata:\n{traceback.format_exc()}", None
301
+
302
+ # ─── CSS ─────────────────────────────────────────────────────────────────────
303
+ CSS = """
304
+ body, .gradio-container {
305
+ background: #0A0E17 !important;
306
+ font-family: 'JetBrains Mono', 'Fira Code', monospace !important;
307
+ color: #F1F5F9 !important;
308
+ }
309
+ .gr-button-primary {
310
+ background: linear-gradient(135deg, #1D4ED8, #3B82F6) !important;
311
+ border: none !important;
312
+ color: #fff !important;
313
+ font-weight: 700 !important;
314
+ letter-spacing: 0.05em !important;
315
+ padding: 12px 28px !important;
316
+ border-radius: 6px !important;
317
+ transition: opacity .2s !important;
318
+ }
319
+ .gr-button-primary:hover { opacity: 0.85 !important; }
320
+ .gr-textbox textarea {
321
+ background: #111827 !important;
322
+ color: #94A3B8 !important;
323
+ border: 1px solid #1E293B !important;
324
+ font-family: 'JetBrains Mono', monospace !important;
325
+ font-size: 12.5px !important;
326
+ line-height: 1.7 !important;
327
+ }
328
+ .gr-box, .gr-panel {
329
+ background: #111827 !important;
330
+ border: 1px solid #1E293B !important;
331
+ }
332
+ footer { display: none !important; }
333
+ """
334
+
335
+ HEADER_HTML = """
336
+ <div style="
337
+ background: linear-gradient(135deg, #0A0E17 0%, #111827 100%);
338
+ border: 1px solid #1E293B;
339
+ border-radius: 10px;
340
+ padding: 28px 32px 20px;
341
+ margin-bottom: 8px;
342
+ ">
343
+ <div style="display:flex; align-items:center; gap:14px; margin-bottom:10px;">
344
+ <span style="font-size:2rem;">πŸ’Ή</span>
345
+ <div>
346
+ <h1 style="margin:0; font-size:1.5rem; font-weight:800; color:#F1F5F9; letter-spacing:.04em;">
347
+ EUR/USD Β· ENSEMBLE FORECAST ENGINE
348
+ </h1>
349
+ <p style="margin:4px 0 0; color:#64748B; font-size:.82rem; font-family:monospace;">
350
+ Random Forest Β· Gradient Boosting Β· AdaBoost Β· Ridge Β· SVR
351
+ </p>
352
+ </div>
353
+ </div>
354
+ <div style="display:flex; gap:20px; flex-wrap:wrap; margin-top:14px;">
355
+ <span style="background:#1E293B; border-radius:4px; padding:4px 12px; font-size:.78rem; color:#94A3B8;">πŸ“‘ Yahoo Finance β€” 90g</span>
356
+ <span style="background:#1E293B; border-radius:4px; padding:4px 12px; font-size:.78rem; color:#94A3B8;">πŸ“ 22 Teknik GΓΆsterge</span>
357
+ <span style="background:#1E293B; border-radius:4px; padding:4px 12px; font-size:.78rem; color:#94A3B8;">πŸ§ͺ 10-GΓΌn Backtest</span>
358
+ <span style="background:#1E293B; border-radius:4px; padding:4px 12px; font-size:.78rem; color:#94A3B8;">⚑ Tek Tıkla Güncelle</span>
359
+ </div>
360
+ </div>
361
+ """
362
+
363
+ # ─── Gradio arayΓΌzΓΌ ──────────────────────────────────────────────────────────
364
+ with gr.Blocks(css=CSS, title="EUR/USD Ensemble Forecast") as app:
365
+ gr.HTML(HEADER_HTML)
366
+
367
  with gr.Row():
368
+ predict_btn = gr.Button("⟳ TAHMİN YAP / YENİLE", variant="primary", scale=1)
369
+
370
  with gr.Row():
371
+ with gr.Column(scale=1):
372
+ output_text = gr.Textbox(
373
+ label="RAPOR",
374
+ lines=30,
375
+ interactive=False,
376
+ show_copy_button=True,
377
+ )
378
+ with gr.Column(scale=2):
379
+ output_plot = gr.Plot(label="GRAFΔ°KLER")
380
+
381
  predict_btn.click(
382
  fn=predict_eurusd,
383
  inputs=[],
384
+ outputs=[output_text, output_plot],
385
  )
 
 
 
 
 
 
 
 
 
 
386
 
387
  if __name__ == "__main__":
388
  app.launch()