GoshawkVortexAI commited on
Commit
828800b
·
verified ·
1 Parent(s): 0fe13e4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +201 -435
app.py CHANGED
@@ -1,474 +1,240 @@
1
-
2
  import os
3
- import time
4
- import tempfile
5
  import requests
 
6
  import pandas as pd
7
  import gradio as gr
8
-
9
- from datetime import datetime, timedelta, timezone
 
10
  from groq import Groq
11
 
12
  # ==========================================
13
- # CONFIGURATION
14
  # ==========================================
15
-
16
- OKX_URL = "https://www.okx.com/api/v5/market/history-candles"
17
-
18
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
19
-
20
- GROQ_MODEL = os.getenv(
21
- "GROQ_MODEL",
22
- "openai/gpt-oss-120b"
23
- )
24
 
25
  # ==========================================
26
- # OKX PUBLIC DATA
27
  # ==========================================
28
-
29
- def get_okx_data(days, timeframe, symbol, progress=gr.Progress()):
30
-
31
- days = int(days)
32
- symbol = symbol.strip().upper()
33
-
34
- if not symbol:
35
- raise ValueError("Trading pair gerekli.")
36
-
37
- end = datetime.now(timezone.utc)
38
- start = end - timedelta(days=days)
39
-
40
- start_ms = int(start.timestamp() * 1000)
41
- end_ms = int(end.timestamp() * 1000)
42
-
43
- all_data = []
44
- seen = set()
45
- after = None
46
-
47
- session = requests.Session()
48
-
49
- while True:
50
-
51
- params = {
52
- "instId": symbol,
53
- "bar": timeframe,
54
- "limit": "300"
55
- }
56
-
57
- if after is not None:
58
- params["after"] = str(after)
59
-
60
- response = session.get(
61
- OKX_URL,
62
- params=params,
63
- timeout=30
64
- )
65
-
66
- response.raise_for_status()
67
-
68
- result = response.json()
69
-
70
- if result.get("code") != "0":
71
- raise RuntimeError(
72
- result.get("msg", "OKX API error")
73
- )
74
-
75
- data = result.get("data", [])
76
-
77
- if not data:
78
- break
79
-
80
- for candle in data:
81
-
82
- ts = int(candle[0])
83
-
84
- if start_ms <= ts <= end_ms and ts not in seen:
85
- seen.add(ts)
86
- all_data.append(candle)
87
-
88
- oldest = min(int(x[0]) for x in data)
89
-
90
- progress(
91
- min(
92
- 0.99,
93
- max(
94
- 0.01,
95
- 1 - (oldest - start_ms) /
96
- max(1, end_ms - start_ms)
97
- )
98
- ),
99
- desc=f"OKX candles: {len(all_data)}"
100
- )
101
-
102
- if oldest <= start_ms:
103
- break
104
-
105
- after = oldest
106
-
107
- time.sleep(0.15)
108
-
109
- if not all_data:
110
- raise ValueError("OKX verisi bulunamadı.")
111
-
112
- columns = [
113
- "timestamp",
114
- "open",
115
- "high",
116
- "low",
117
- "close",
118
- "volume",
119
- "volume_currency",
120
- "volume_quote",
121
- "confirm"
122
- ]
123
-
124
- df = pd.DataFrame(
125
- all_data,
126
- columns=columns
127
- )
128
-
129
- df["timestamp"] = pd.to_datetime(
130
- df["timestamp"].astype("int64"),
131
- unit="ms",
132
- utc=True
133
- )
134
-
135
- numeric_columns = [
136
- "open",
137
- "high",
138
- "low",
139
- "close",
140
- "volume",
141
- "volume_currency",
142
- "volume_quote"
143
- ]
144
-
145
- for col in numeric_columns:
146
- df[col] = pd.to_numeric(df[col])
147
-
148
- df["confirm"] = pd.to_numeric(df["confirm"])
149
-
150
- df = (
151
- df.sort_values("timestamp")
152
- .drop_duplicates("timestamp")
153
- .reset_index(drop=True)
154
  )
155
-
156
- return df
157
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
  # ==========================================
160
- # TECHNICAL INDICATORS
161
  # ==========================================
 
 
 
162
 
163
- def add_indicators(df):
164
-
165
- df = df.copy()
166
 
167
- df["return_pct"] = (
168
- df["close"].pct_change() * 100
169
- )
170
-
171
- df["EMA20"] = df["close"].ewm(
172
- span=20,
173
- adjust=False
174
- ).mean()
175
-
176
- df["EMA50"] = df["close"].ewm(
177
- span=50,
178
- adjust=False
179
- ).mean()
180
 
181
- delta = df["close"].diff()
182
 
183
- gain = delta.clip(lower=0).ewm(
184
- alpha=1/14,
185
- adjust=False
186
- ).mean()
187
 
188
- loss = (-delta.clip(upper=0)).ewm(
189
- alpha=1/14,
190
- adjust=False
191
- ).mean()
192
 
193
- rs = gain / loss.replace(0, float("nan"))
 
 
194
 
195
- df["RSI"] = 100 - (100 / (1 + rs))
 
 
196
 
197
- previous_close = df["close"].shift(1)
 
198
 
199
- tr = pd.concat(
200
- [
201
- df["high"] - df["low"],
202
- (df["high"] - previous_close).abs(),
203
- (df["low"] - previous_close).abs()
204
- ],
205
- axis=1
206
- ).max(axis=1)
207
-
208
- df["ATR"] = tr.ewm(
209
- alpha=1/14,
210
- adjust=False
211
- ).mean()
212
-
213
- df["volume_sma"] = (
214
- df["volume"].rolling(20).mean()
215
- )
216
-
217
- return df
218
-
219
-
220
- # ==========================================
221
- # GROQ GPT-OSS AI
222
- # ==========================================
223
-
224
- def groq_analysis(df):
225
-
226
- if not GROQ_API_KEY:
227
- raise ValueError(
228
- "GROQ_API_KEY bulunamadı. "
229
- "Hugging Face Secrets bölümüne ekle."
230
- )
231
-
232
- client = Groq(
233
- api_key=GROQ_API_KEY
234
- )
235
-
236
- latest = df.tail(30).copy()
237
-
238
- market_data = latest[
239
- [
240
- "timestamp",
241
- "open",
242
- "high",
243
- "low",
244
- "close",
245
- "volume",
246
- "return_pct",
247
- "EMA20",
248
- "EMA50",
249
- "RSI",
250
- "ATR"
251
- ]
252
- ].round(4).to_string(index=False)
253
 
254
  prompt = f"""
255
- BTC-USDT teknik piyasa analizi yap.
256
-
257
- VERİLER:
258
- {market_data}
259
-
260
- ANALİZ GÖREVLERİ:
261
-
262
- 1. Genel piyasa trendi.
263
- 2. EMA20 ve EMA50 karşılaştırması.
264
- 3. RSI momentum analizi.
265
- 4. ATR volatilite analizi.
266
- 5. Hacim değerlendirmesi.
267
- 6. Destek ve direnç seviyeleri.
268
- 7. Bullish senaryosu.
269
- 8. Bearish senaryosu.
270
- 9. Neutral senaryosu.
271
- 10. Risk analizi.
272
-
273
- ÇIKTI FORMATI:
274
-
275
- YÖN: BULLISH / BEARISH / NEUTRAL
276
-
277
- MODEL GÜVENİ: 0-100
278
-
279
- TREND:
280
- MOMENTUM:
281
- VOLATİLİTE:
282
- DESTEK:
283
- DİRENÇ:
284
-
285
- YÜKSELİŞ SENARYOSU:
286
- DÜŞÜŞ SENARYOSU:
287
- YATAY SENARYO:
288
-
289
- RİSK:
290
- NİHAİ KARAR:
291
-
292
- Kurallar:
293
- - Sadece verilen verilere dayan.
294
- - Gelecekteki fiyatları uydurma.
295
- - Güven puanını kalibre edilmiş olasılık gibi sunma.
296
- - Kesin kazanç garantisi verme.
297
- - Türkçe yanıt ver.
298
- """
299
-
300
- response = client.chat.completions.create(
301
- model=GROQ_MODEL,
302
- messages=[
303
- {
304
- "role": "system",
305
- "content": (
306
- "You are a professional quantitative "
307
- "crypto market analyst. "
308
- "Analyze data carefully and respond in Turkish."
309
- )
310
- },
311
- {
312
- "role": "user",
313
- "content": prompt
314
- }
315
- ],
316
- temperature=0.2,
317
- max_completion_tokens=2000
318
  )
319
-
320
- return response.choices[0].message.content
321
-
322
-
323
- # ==========================================
324
- # MAIN ENGINE
325
- # ==========================================
326
-
327
- def run_system(days, timeframe, symbol):
328
-
329
- try:
330
-
331
- df = get_okx_data(
332
- days,
333
- timeframe,
334
- symbol
335
- )
336
-
337
- df = add_indicators(df)
338
-
339
- analysis = groq_analysis(df)
340
-
341
- temp = tempfile.NamedTemporaryFile(
342
- suffix=".csv",
343
- prefix="OKX_BTC_",
344
- delete=False
345
- )
346
-
347
- csv_path = temp.name
348
- temp.close()
349
-
350
- df.to_csv(
351
- csv_path,
352
- index=False
353
- )
354
-
355
- last = df.iloc[-1]
356
-
357
- info = f"""
358
- ## MARKET INFORMATION
359
-
360
- - Symbol: {symbol}
361
- - Timeframe: {timeframe}
362
- - Candles: {len(df)}
363
- - Last Close: {last['close']:.2f}
364
- - RSI: {last['RSI']:.2f}
365
- - EMA20: {last['EMA20']:.2f}
366
- - EMA50: {last['EMA50']:.2f}
367
- - ATR: {last['ATR']:.2f}
368
- - Volume: {last['volume']:.4f}
369
- """
370
-
371
- return (
372
- info,
373
- analysis,
374
- csv_path,
375
- df.tail(20)
376
- )
377
-
378
- except Exception as e:
379
-
380
- return (
381
- f"ERROR: {str(e)}",
382
- "",
383
- None,
384
- pd.DataFrame()
385
- )
386
-
387
 
388
  # ==========================================
389
- # GRADIO INTERFACE
390
  # ==========================================
391
-
392
- with gr.Blocks(
393
- title="OKX GPT-OSS BTC AI"
394
- ) as demo:
395
-
396
- gr.Markdown("""
397
- # BTC-USDT AI ANALYSIS ENGINE
398
-
399
- ### OKX Public API + Groq GPT-OSS 120B
400
-
401
- Historical Data | Technical Indicators | AI Analysis | CSV
402
- """)
403
-
404
- with gr.Row():
405
-
406
- symbol = gr.Textbox(
407
- label="Trading Pair",
408
- value="BTC-USDT"
409
- )
410
-
411
- timeframe = gr.Dropdown(
412
- choices=[
413
- "1m",
414
- "3m",
415
- "5m",
416
- "15m",
417
- "30m",
418
- "1H",
419
- "2H",
420
- "4H",
421
- "6H",
422
- "12H",
423
- "1D"
424
- ],
425
- value="1H",
426
- label="Timeframe"
427
- )
428
-
429
- days = gr.Slider(
430
- minimum=1,
431
- maximum=90,
432
- value=30,
433
- step=1,
434
- label="Historical Days"
435
- )
436
-
437
- run = gr.Button(
438
- "RUN GPT-OSS BTC AI",
439
- variant="primary"
440
  )
441
 
442
- market_info = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
- ai_output = gr.Markdown()
 
 
445
 
446
- csv_output = gr.File(
447
- label="Download CSV"
448
- )
449
 
450
- preview = gr.Dataframe(
451
- label="Last 20 Candles",
452
- interactive=False
 
453
  )
454
 
455
- run.click(
456
- fn=run_system,
457
- inputs=[
458
- days,
459
- timeframe,
460
- symbol
461
- ],
462
- outputs=[
463
- market_info,
464
- ai_output,
465
- csv_output,
466
- preview
467
- ]
468
  )
469
 
470
-
471
- demo.queue().launch(
472
- server_name="0.0.0.0",
473
- server_port=7860
474
- )
 
 
1
  import os
2
+ import io
 
3
  import requests
4
+ import numpy as np
5
  import pandas as pd
6
  import gradio as gr
7
+ import xgboost as xgb
8
+ from sklearn.neighbors import KNeighborsClassifier
9
+ from sklearn.preprocessing import StandardScaler
10
  from groq import Groq
11
 
12
  # ==========================================
13
+ # 🔌 1. GROQ CLIENT
14
  # ==========================================
15
+ api_key = os.environ.get("GROQ_API_KEY")
16
+ client = None
17
+ if api_key:
18
+ client = Groq(api_key=api_key)
 
 
 
 
 
19
 
20
  # ==========================================
21
+ # 📊 2. YARDIMCI FONKSİYONLAR
22
  # ==========================================
23
+ def compute_rsi(series, period=14):
24
+ delta = series.diff()
25
+ gain = delta.clip(lower=0).rolling(period).mean()
26
+ loss = -delta.clip(upper=0).rolling(period).mean()
27
+ rs = gain / loss
28
+ return 100 - (100 / (1 + rs))
29
+
30
+ def load_csv_from_url(url):
31
+ try:
32
+ headers = {"User-Agent": "Mozilla/5.0"}
33
+ r = requests.get(url, headers=headers, timeout=20)
34
+ r.raise_for_status()
35
+ df = pd.read_csv(io.StringIO(r.text))
36
+ return df, None
37
+ except Exception as e:
38
+ return None, f"CSV indirilemedi: {e}"
39
+
40
+ def process_dataframe(df):
41
+ df.columns = [c.strip().lower() for c in df.columns]
42
+ col_map = {
43
+ "date": "date", "datetime": "date", "timestamp": "date",
44
+ "open": "open", "high": "high", "low": "low",
45
+ "close": "close", "adj close": "close", "price": "close",
46
+ "volume": "volume", "vol": "volume"
47
+ }
48
+ df = df.rename(columns={c: col_map[c] for c in df.columns if c in col_map})
49
+
50
+ if "close" not in df.columns:
51
+ return None, None, None, f"'close' sütunu bulunamadı. Mevcut sütunlar: {list(df.columns)}"
52
+
53
+ for col in ["open", "high", "low", "close", "volume"]:
54
+ if col in df.columns:
55
+ df[col] = pd.to_numeric(df[col], errors="coerce")
56
+
57
+ df = df.dropna(subset=["close"]).reset_index(drop=True)
58
+ if len(df) < 50:
59
+ return None, None, None, "CSV'de en az 50 satır veri olmalı."
60
+
61
+ # Teknik göstergeler
62
+ df["ma_5"] = df["close"].rolling(5).mean()
63
+ df["ma_20"] = df["close"].rolling(20).mean()
64
+ df["rsi"] = compute_rsi(df["close"], 14)
65
+ df["macd"] = df["close"].ewm(span=12).mean() - df["close"].ewm(span=26).mean()
66
+ df["target"] = (df["close"].shift(-1) > df["close"]).astype(int)
67
+
68
+ df_model = df.dropna().reset_index(drop=True)
69
+ if len(df_model) < 20:
70
+ return None, None, None, "Göstergeler hesaplandıktan sonra yeterli veri kalmadı."
71
+
72
+ features = ["ma_5", "ma_20", "rsi", "macd"]
73
+ if "volume" in df_model.columns:
74
+ features.append("volume")
75
+
76
+ X = df_model[features].values
77
+ y = df_model["target"].values
78
+
79
+ # --- XGBoost ---
80
+ xgb_model = xgb.XGBClassifier(
81
+ n_estimators=50, max_depth=3, learning_rate=0.1,
82
+ objective="binary:logistic", eval_metric="logloss", random_state=42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  )
84
+ xgb_model.fit(X, y)
85
+
86
+ # --- KNN (ölçeklendirme gerekli) ---
87
+ scaler = StandardScaler()
88
+ X_scaled = scaler.fit_transform(X)
89
+ knn_model = KNeighborsClassifier(n_neighbors=5)
90
+ knn_model.fit(X_scaled, y)
91
+
92
+ latest = df_model.iloc[-1]
93
+ metrics = {
94
+ "last_close": float(latest["close"]),
95
+ "ma5": float(latest["ma_5"]),
96
+ "ma20": float(latest["ma_20"]),
97
+ "rsi": float(latest["rsi"]),
98
+ "macd": float(latest["macd"]),
99
+ "features": features,
100
+ }
101
+ if "volume" in df_model.columns:
102
+ metrics["volume"] = float(latest["volume"])
103
+
104
+ return (xgb_model, knn_model, scaler), metrics, features, None
105
 
106
  # ==========================================
107
+ # 🧠 3. ANALİZ MOTORU (Ensemble: XGB + KNN)
108
  # ==========================================
109
+ def predict_and_analyze(csv_url):
110
+ if not csv_url:
111
+ return "Lütfen bir CSV linki giriniz.", {}
112
 
113
+ df, err = load_csv_from_url(csv_url)
114
+ if err:
115
+ return err, {}
116
 
117
+ models, metrics, features, err = process_dataframe(df)
118
+ if err:
119
+ return err, {}
 
 
 
 
 
 
 
 
 
 
120
 
121
+ xgb_model, knn_model, scaler = models
122
 
123
+ feat_values = [metrics[k] for k in ["ma5", "ma20", "rsi", "macd"]]
124
+ if "volume" in metrics:
125
+ feat_values.append(metrics["volume"])
126
+ feat_arr = np.array([feat_values])
127
 
128
+ # XGBoost tahmini
129
+ xgb_prob_up = float(xgb_model.predict_proba(feat_arr)[0][1])
 
 
130
 
131
+ # KNN tahmini (ölçeklendirilmiş veriyle)
132
+ feat_scaled = scaler.transform(feat_arr)
133
+ knn_prob_up = float(knn_model.predict_proba(feat_scaled)[0][1])
134
 
135
+ # Ensemble (ortalama)
136
+ ensemble_prob_up = (xgb_prob_up + knn_prob_up) / 2
137
+ ensemble_prob_down = 1 - ensemble_prob_up
138
 
139
+ direction = "YUKARI 📈" if ensemble_prob_up > 0.5 else "AŞAĞI 📉"
140
+ confidence = max(ensemble_prob_up, ensemble_prob_down) * 100
141
 
142
+ prob_dict = {
143
+ "XGBoost Yükseliş": xgb_prob_up,
144
+ "KNN Yükseliş": knn_prob_up,
145
+ "Ensemble Yükseliş (Ortalama)": ensemble_prob_up,
146
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
  prompt = f"""
149
+ Sen uzman bir kripto para analisti ve teknik analiz uzmanısın.
150
+
151
+ 📊 BTC/USD Son Kapanış (CSV'den): ${metrics['last_close']:,.2f}
152
+ - RSI (14): {metrics['rsi']:.2f}
153
+ - MA5: ${metrics['ma5']:,.2f} | MA20: ${metrics['ma20']:,.2f}
154
+ - MACD: {metrics['macd']:.4f}
155
+ {f"- Hacim: {metrics['volume']:,.2f}" if 'volume' in metrics else ""}
156
+
157
+ 🤖 Model Sonuçları:
158
+ - XGBoost Yükseliş Olasılığı: %{xgb_prob_up*100:.1f}
159
+ - KNN Yükseliş Olasılığı: %{knn_prob_up*100:.1f}
160
+ - Ensemble (Ortalama) Yükseliş Olasılığı: %{ensemble_prob_up*100:.1f}
161
+ - Tahmin Yönü: {direction} (Güven: %{confidence:.1f})
162
+
163
+ Türkçe bir teknik analiz raporu hazırla. Şu başlıklar bulunsun:
164
+ 1. 🎯 Model Yorumu (XGBoost ve KNN'in karşılaştırmalı mantığı, ensemble neden kullanılır)
165
+ 2. 📈 Teknik Analiz (RSI, MA, MACD; destek/direnç)
166
+ 3. 💡 İşlem Stratejisi (Long/Short, stop-loss, take-profit, risk yönetimi)
167
+
168
+ Yatırım tavsiyesi olmadığını vurgula. Rasyonel ve profesyonel ol.
169
+ """
170
+
171
+ if client:
172
+ try:
173
+ response = client.chat.completions.create(
174
+ model="openai/gpt-oss-120b",
175
+ messages=[{"role": "user", "content": prompt}],
176
+ temperature=0.3,
177
+ max_tokens=1024
178
+ )
179
+ analysis_result = response.choices[0].message.content
180
+ except Exception as e:
181
+ analysis_result = f"Groq API Hatası: {e}"
182
+ else:
183
+ analysis_result = "GROQ_API_KEY bulunamadı! Hugging Face Secrets'a ekleyin."
184
+
185
+ header = (
186
+ f"**Son Kapanış: ${metrics['last_close']:,.2f}** | RSI: {metrics['rsi']:.1f} | "
187
+ f"MA5: ${metrics['ma5']:,.0f} | MA20: ${metrics['ma20']:,.0f}\n\n"
188
+ f"**XGB:** %{xgb_prob_up*100:.1f} | **KNN:** %{knn_prob_up*100:.1f} | "
189
+ f"**Ensemble:** %{ensemble_prob_up*100:.1f}\n\n---\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  )
191
+ return header + analysis_result, prob_dict
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
  # ==========================================
194
+ # 🎨 4. GRADIO ARAYÜZÜ
195
  # ==========================================
196
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="orange", secondary_hue="slate")) as demo:
197
+ gr.Markdown(
198
+ """
199
+ # ₿ BTC/USD XGBoost + KNN & Groq AI Tahmin Radarı (CSV Linki)
200
+ Bir CSV linki girin; kod dosyayı indirir, **XGBoost** ve **KNN** modellerini eğitir, ensemble tahmini üretir ve **Groq** analiz raporu hazırlar.
201
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  )
203
 
204
+ with gr.Row():
205
+ with gr.Column():
206
+ gr.Markdown("### 🔗 CSV Linki")
207
+ url_input = gr.Textbox(
208
+ label="CSV URL",
209
+ placeholder="https://.../btc_usd.csv",
210
+ value=""
211
+ )
212
+ gr.Markdown(
213
+ """
214
+ **İpucu**: `yfinance` ile indirdiğiniz CSV'yi GitHub'a yükleyip "Raw" linkini buraya yapıştırın.
215
+ """
216
+ )
217
+ submit_btn = gr.Button("CSV'yi İndir ve Analiz Et", variant="primary")
218
 
219
+ with gr.Column():
220
+ gr.Markdown("### 📈 Model Tahminleri (XGB + KNN)")
221
+ prob_output = gr.Label(label="Olasılık Dağılımı")
222
 
223
+ gr.Markdown("### 🧠 Groq Yapay Zeka Analiz Raporu")
224
+ analysis_output = gr.Markdown(value="CSV linkini girip butona basın.")
 
225
 
226
+ submit_btn.click(
227
+ fn=predict_and_analyze,
228
+ inputs=[url_input],
229
+ outputs=[analysis_output, prob_output]
230
  )
231
 
232
+ gr.Markdown(
233
+ """
234
+ ---
235
+ ⚠️ **Yatırım Tavsiyesi Değildir**: Bu uygulama eğitim amaçlıdır.
236
+ """
 
 
 
 
 
 
 
 
237
  )
238
 
239
+ if __name__ == "__main__":
240
+ demo.launch()