Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
try:
|
| 17 |
data = yf.download(symbol, period=period, interval="1d", progress=False)
|
|
|
|
|
|
|
| 18 |
return data
|
| 19 |
-
except Exception
|
| 20 |
return None
|
| 21 |
|
| 22 |
def create_features(data):
|
| 23 |
-
"""Teknik gΓΆstergeler ve ΓΆzellikler oluΕtur"""
|
| 24 |
df = data.copy()
|
| 25 |
-
|
| 26 |
-
|
| 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 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
df['SMA_20'] = df['Close'].rolling(window=20).mean()
|
| 35 |
-
|
| 36 |
-
# RSI
|
| 37 |
delta = df['Close'].diff()
|
| 38 |
-
gain
|
| 39 |
-
loss
|
| 40 |
-
|
| 41 |
-
|
| 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']
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
df['BB_middle'] = df['Close'].rolling(
|
| 50 |
-
df['BB_std']
|
| 51 |
-
df['BB_upper']
|
| 52 |
-
df['BB_lower']
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
| 56 |
df[f'Lag_{i}'] = df['Close'].shift(i)
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
y = df['Close'].values
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
# Normalizasyon
|
| 76 |
scaler = StandardScaler()
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
def predict_eurusd():
|
| 113 |
-
|
| 114 |
try:
|
| 115 |
-
# Veri indir
|
| 116 |
data = get_forex_data()
|
| 117 |
-
if data is None
|
| 118 |
-
return "β Veri indirilemedi.
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
#
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
with gr.Row():
|
| 218 |
-
|
| 219 |
-
|
| 220 |
with gr.Row():
|
| 221 |
-
|
| 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()
|