GoshawkVortexAI commited on
Commit
f2f1569
·
verified ·
1 Parent(s): 35ff4fe

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -329
app.py DELETED
@@ -1,329 +0,0 @@
1
- # app.py
2
- # BTC/USD Multi-Model AI Radar: 7 ML models + Groq explanation
3
- # Gradio app for Hugging Face Spaces.
4
- # Required packages: gradio, pandas, numpy, scikit-learn, xgboost, groq
5
-
6
- import os
7
- import warnings
8
- import numpy as np
9
- import pandas as pd
10
- import gradio as gr
11
-
12
- from sklearn.preprocessing import StandardScaler
13
- from sklearn.pipeline import Pipeline
14
- from sklearn.impute import SimpleImputer
15
- from sklearn.neighbors import KNeighborsClassifier
16
- from sklearn.ensemble import (
17
- RandomForestClassifier,
18
- ExtraTreesClassifier,
19
- HistGradientBoostingClassifier,
20
- GradientBoostingClassifier,
21
- VotingClassifier,
22
- )
23
- from sklearn.linear_model import LogisticRegression
24
- from sklearn.svm import SVC
25
- from sklearn.metrics import accuracy_score, balanced_accuracy_score, log_loss
26
- from sklearn.model_selection import TimeSeriesSplit
27
- from xgboost import XGBClassifier
28
-
29
- try:
30
- from groq import Groq
31
- except Exception:
32
- Groq = None
33
-
34
- warnings.filterwarnings("ignore")
35
- CSV_PATH = os.environ.get("BTC_CSV_PATH", "BTCUSD_M30.csv")
36
- HORIZON = 1
37
- MIN_ROWS = 120
38
-
39
- api_key = os.environ.get("GROQ_API_KEY")
40
- client = Groq(api_key=api_key) if (api_key and Groq) else None
41
-
42
-
43
- def load_local_csv(path=CSV_PATH):
44
- """Reads headerless or headed OHLCV CSV/TSV."""
45
- if not os.path.exists(path):
46
- return None, f"CSV bulunamadı: {path}"
47
-
48
- try:
49
- # Try common separators and both header formats.
50
- raw = None
51
- for sep in ("\t", ",", ";"):
52
- for header in (None, 0):
53
- try:
54
- candidate = pd.read_csv(path, sep=sep, header=header)
55
- if candidate.shape[1] >= 6:
56
- raw = candidate
57
- break
58
- except Exception:
59
- pass
60
- if raw is not None:
61
- break
62
-
63
- if raw is None or raw.shape[1] < 6:
64
- return None, "CSV en az 6 sütun içermeli: date, open, high, low, close, volume."
65
-
66
- raw = raw.iloc[:, :6].copy()
67
- raw.columns = ["date", "open", "high", "low", "close", "volume"]
68
-
69
- # If the first row was actually a header, remove it after numeric conversion.
70
- for col in ["open", "high", "low", "close", "volume"]:
71
- raw[col] = pd.to_numeric(raw[col], errors="coerce")
72
-
73
- raw["date"] = raw["date"].astype(str)
74
- raw = raw.dropna(subset=["open", "high", "low", "close", "volume"])
75
- raw = raw[(raw["close"] > 0) & (raw["high"] > 0) & (raw["low"] > 0)]
76
- raw = raw.sort_values("date").drop_duplicates("date").reset_index(drop=True)
77
-
78
- if len(raw) < MIN_ROWS:
79
- return None, f"En az {MIN_ROWS} geçerli mum gerekli; bulunan: {len(raw)}."
80
- return raw, None
81
- except Exception as exc:
82
- return None, f"CSV okuma hatası: {exc}"
83
-
84
-
85
- def rsi(series, period=14):
86
- delta = series.diff()
87
- gain = delta.clip(lower=0).ewm(alpha=1 / period, adjust=False).mean()
88
- loss = (-delta.clip(upper=0)).ewm(alpha=1 / period, adjust=False).mean()
89
- rs = gain / loss.replace(0, np.nan)
90
- return 100 - (100 / (1 + rs))
91
-
92
-
93
- def make_features(raw):
94
- d = raw.copy()
95
- close = d["close"]
96
- d["ret_1"] = close.pct_change()
97
- d["ret_2"] = close.pct_change(2)
98
- d["ret_3"] = close.pct_change(3)
99
- d["ret_6"] = close.pct_change(6)
100
- d["ret_12"] = close.pct_change(12)
101
- d["range_pct"] = (d["high"] - d["low"]) / close
102
- d["body_pct"] = (d["close"] - d["open"]) / d["open"]
103
- d["upper_wick"] = (d["high"] - d[["open", "close"]].max(axis=1)) / close
104
- d["lower_wick"] = (d[["open", "close"]].min(axis=1) - d["low"]) / close
105
-
106
- for window in (5, 10, 20, 50):
107
- ma = close.rolling(window).mean()
108
- d[f"ma_gap_{window}"] = close / ma - 1
109
- d[f"volatility_{window}"] = d["ret_1"].rolling(window).std()
110
-
111
- d["rsi_14"] = rsi(close, 14)
112
- ema12 = close.ewm(span=12, adjust=False).mean()
113
- ema26 = close.ewm(span=26, adjust=False).mean()
114
- d["macd_pct"] = (ema12 - ema26) / close
115
- d["macd_signal_pct"] = (d["macd_pct"].ewm(span=9, adjust=False).mean())
116
- d["volume_change"] = d["volume"].pct_change()
117
- d["volume_z20"] = (
118
- (d["volume"] - d["volume"].rolling(20).mean()) /
119
- d["volume"].rolling(20).std()
120
- )
121
- d["target"] = (close.shift(-HORIZON) > close).astype(int)
122
- # Last row has no known future label; exclude it from training.
123
- d = d.iloc[:-HORIZON].replace([np.inf, -np.inf], np.nan)
124
- feature_cols = [
125
- c for c in d.columns
126
- if c not in ["date", "open", "high", "low", "close", "volume", "target"]
127
- ]
128
- d = d.dropna(subset=feature_cols + ["target"]).reset_index(drop=True)
129
- return d, feature_cols
130
-
131
-
132
- def build_models():
133
- # Seven independent classifiers. Scaling is included where distance/margin
134
- # methods need it; tree models use the original feature scale.
135
- return {
136
- "XGBoost": XGBClassifier(
137
- n_estimators=180, max_depth=3, learning_rate=0.035,
138
- subsample=0.85, colsample_bytree=0.85, reg_lambda=2,
139
- objective="binary:logistic", eval_metric="logloss",
140
- random_state=42, n_jobs=-1
141
- ),
142
- "KNN": Pipeline([
143
- ("imputer", SimpleImputer(strategy="median")),
144
- ("scaler", StandardScaler()),
145
- ("model", KNeighborsClassifier(n_neighbors=9, weights="distance"))
146
- ]),
147
- "Random Forest": RandomForestClassifier(
148
- n_estimators=220, max_depth=7, min_samples_leaf=4,
149
- class_weight="balanced_subsample", random_state=42, n_jobs=-1
150
- ),
151
- "Extra Trees": ExtraTreesClassifier(
152
- n_estimators=220, max_depth=8, min_samples_leaf=4,
153
- class_weight="balanced", random_state=43, n_jobs=-1
154
- ),
155
- "Hist Gradient Boosting": HistGradientBoostingClassifier(
156
- max_iter=160, learning_rate=0.05, max_leaf_nodes=15,
157
- l2_regularization=1.0, random_state=44
158
- ),
159
- "Logistic Regression": Pipeline([
160
- ("imputer", SimpleImputer(strategy="median")),
161
- ("scaler", StandardScaler()),
162
- ("model", LogisticRegression(C=0.5, max_iter=1500, class_weight="balanced"))
163
- ]),
164
- "SVM": Pipeline([
165
- ("imputer", SimpleImputer(strategy="median")),
166
- ("scaler", StandardScaler()),
167
- ("model", SVC(C=1.0, kernel="rbf", probability=True,
168
- class_weight="balanced", random_state=45))
169
- ]),
170
- }
171
-
172
-
173
- def fit_and_evaluate(data, features):
174
- X = data[features].astype(float)
175
- y = data["target"].astype(int)
176
-
177
- # Chronological holdout: no random shuffle and no future-to-past leakage.
178
- split = max(int(len(data) * 0.8), 50)
179
- split = min(split, len(data) - 20)
180
- X_train, X_test = X.iloc[:split], X.iloc[split:]
181
- y_train, y_test = y.iloc[:split], y.iloc[split:]
182
-
183
- models = build_models()
184
- trained = {}
185
- results = {}
186
- latest_x = X.iloc[[-1]]
187
-
188
- for name, model in models.items():
189
- try:
190
- model.fit(X_train, y_train)
191
- trained[name] = model
192
- prob = model.predict_proba(X_test)[:, 1]
193
- latest_prob = float(model.predict_proba(latest_x)[0, 1])
194
- pred = (prob >= 0.5).astype(int)
195
- results[name] = {
196
- "up": latest_prob,
197
- "accuracy": accuracy_score(y_test, pred) * 100,
198
- "balanced": balanced_accuracy_score(y_test, pred) * 100,
199
- "logloss": log_loss(y_test, np.clip(prob, 1e-6, 1 - 1e-6)),
200
- "test_n": len(y_test),
201
- }
202
- except Exception as exc:
203
- results[name] = {"error": str(exc)}
204
-
205
- valid = {k: v for k, v in results.items() if "up" in v}
206
- if len(valid) < 2:
207
- raise RuntimeError("Ensemble için en az iki model başarıyla eğitilemedi.")
208
-
209
- # Equal-weight ensemble keeps a weak/overfit holdout score from dominating.
210
- ensemble_up = float(np.mean([v["up"] for v in valid.values()]))
211
- return trained, results, ensemble_up, len(X_train), len(X_test)
212
-
213
-
214
- def groq_comment(prompt):
215
- if not client:
216
- return "Groq açıklaması için GROQ_API_KEY gerekli. Model tahminleri yine çalışır."
217
- try:
218
- response = client.chat.completions.create(
219
- model=os.environ.get("GROQ_MODEL", "openai/gpt-oss-120b"),
220
- messages=[
221
- {"role": "system", "content": "Türkçe konuşan, temkinli bir nicel piyasa analistisin. Kesinlik veya kâr garantisi verme. Verilmeyen veri uydurma."},
222
- {"role": "user", "content": prompt}
223
- ],
224
- temperature=0.25,
225
- max_tokens=1100
226
- )
227
- return response.choices[0].message.content
228
- except Exception as exc:
229
- return f"Groq API hatası: {exc}"
230
-
231
-
232
- def analyze():
233
- raw, error = load_local_csv()
234
- if error:
235
- return error, {}, pd.DataFrame()
236
-
237
- try:
238
- data, features = make_features(raw)
239
- if len(data) < MIN_ROWS - 35:
240
- return f"Göstergelerden sonra veri yetersiz: {len(data)} satır.", {}, pd.DataFrame()
241
-
242
- trained, results, ensemble_up, train_n, test_n = fit_and_evaluate(data, features)
243
- ensemble_down = 1 - ensemble_up
244
- direction = "YUKARI 📈" if ensemble_up >= 0.5 else "AŞAĞI 📉"
245
- confidence = max(ensemble_up, ensemble_down) * 100
246
-
247
- rows = []
248
- valid_probs = []
249
- for name, item in results.items():
250
- if "up" in item:
251
- valid_probs.append(item["up"])
252
- rows.append([
253
- name, f"{item['up'] * 100:.1f}%",
254
- f"{item['accuracy']:.1f}%",
255
- f"{item['balanced']:.1f}%",
256
- f"{item['logloss']:.4f}", item["test_n"]
257
- ])
258
- else:
259
- rows.append([name, "Hata", "-", "-", item["error"][:80], "-"])
260
-
261
- last = raw.iloc[-1]
262
- model_table = pd.DataFrame(
263
- rows,
264
- columns=["Model", "Yükseliş olasılığı", "Test doğruluğu",
265
- "Dengeli doğruluk", "Log-loss / hata", "Test mum"]
266
- )
267
-
268
- prompt = f"""
269
- BTC/USD M30 analizini Türkçe hazırla.
270
- Son kapanış: {float(last['close']):.2f} USD
271
- Son tarih: {last['date']}
272
- Ensemble yükseliş olasılığı: %{ensemble_up*100:.1f}
273
- Ensemble düşüş olasılığı: %{ensemble_down*100:.1f}
274
- Yön: {direction}; yön güveni (olasılık): %{confidence:.1f}
275
- Eğitim mum sayısı: {train_n}; kronolojik test mum sayısı: {test_n}.
276
- Model sonuçları:
277
- {model_table.to_string(index=False)}
278
- Başlıklar:
279
- 1. Çoklu model değerlendirmesi ve modeller arası uzlaşma/ayrışma
280
- 2. Teknik görünüm
281
- 3. Yukarı ve aşağı senaryolar
282
- 4. Risk yönetimi ve belirsizlik
283
- İşlem fiyatı, stop veya hedef seviyesi uydurma; OHLC verisinden hesaplanmayan seviyeleri açıkça belirt.
284
- Test doğruluğunun gelecekteki başarı garantisi olmadığını belirt. Yatırım tavsiyesi değildir.
285
- """
286
- ai_text = groq_comment(prompt)
287
- header = (
288
- f"## BTC/USD Çoklu Model Radarı\n\n"
289
- f"**Son kapanış:** ${float(last['close']):,.2f} | "
290
- f"**Tarih:** {last['date']}\n\n"
291
- f"**Ensemble:** {direction} | **Yükseliş:** %{ensemble_up*100:.1f} | "
292
- f"**Düşüş:** %{ensemble_down*100:.1f}\n\n"
293
- f"**Aktif model:** {len(valid_probs)}/7 | "
294
- f"**Eğitim:** {train_n} mum | **Test:** {test_n} mum\n\n"
295
- f"> Olasılık/güven skoru başarı oranı değildir. Test sonuçları geçmiş veriye aittir.\n\n"
296
- f"---\n\n{ai_text}"
297
- )
298
- labels = {
299
- name: float(item["up"])
300
- for name, item in results.items() if "up" in item
301
- }
302
- labels["Ensemble"] = ensemble_up
303
- return header, labels, model_table
304
- except Exception as exc:
305
- return f"Analiz hatası: {exc}", {}, pd.DataFrame()
306
-
307
-
308
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", secondary_hue="slate")) as demo:
309
- gr.Markdown(
310
- f"""# ₿ BTC/USD Multi-Model AI Radar
311
- **Veri:** `{CSV_PATH}` · **Zaman dilimi:** M30 · **Model sayısı:** 7 ML modeli + Groq AI açıklaması
312
-
313
- CSV biçimi: `date, open, high, low, close, volume` (başlıklı/başlıksız CSV veya TSV).
314
- """
315
- )
316
- with gr.Row():
317
- run_btn = gr.Button("📊 CSV'yi Oku ve 7 Modeli Eğit", variant="primary", size="lg")
318
- prediction = gr.Label(label="Model yükseliş olasılıkları")
319
- report = gr.Markdown("Analizi başlatmak için butona basın.")
320
- table = gr.Dataframe(label="Model karşılaştırması — kronolojik holdout testi",
321
- interactive=False, wrap=True)
322
- run_btn.click(fn=analyze, inputs=[], outputs=[report, prediction, table])
323
- gr.Markdown(
324
- "**Not:** Modeller aynı CSV üzerinde eğitilir. Test bölümü kronolojik olarak ayrılır; "
325
- "bu bir canlı emir/broker bağlantısı değildir. Geçmiş test performansı geleceği garanti etmez."
326
- )
327
-
328
- if __name__ == "__main__":
329
- demo.launch()