GoshawkVortexAI commited on
Commit
50f7daa
·
verified ·
1 Parent(s): 95474fa

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -711
app.py DELETED
@@ -1,711 +0,0 @@
1
- # ╔══════════════════════════════════════════════════════════╗
2
- # ║ CRYPTO SIGNAL PRO — OKX Edition ║
3
- # ║ OKX REST API · XGBoost AI · Canlı Otomatik Analiz ║
4
- # ╚══════════════════════════════════════════════════════════╝
5
-
6
- import subprocess, sys, warnings
7
- warnings.filterwarnings("ignore")
8
-
9
- def _pip(pkg):
10
- subprocess.check_call([sys.executable,"-m","pip","install",pkg,"-q"],
11
- stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
12
-
13
- for _p in ["requests","pandas","numpy","plotly","xgboost","lightgbm","scikit-learn"]:
14
- try: _pip(_p)
15
- except: pass
16
-
17
- import gradio as gr
18
- import requests, time, traceback
19
- import pandas as pd
20
- import numpy as np
21
- import plotly.graph_objects as go
22
- from plotly.subplots import make_subplots
23
- from datetime import datetime, timezone
24
-
25
- try:
26
- from xgboost import XGBClassifier
27
- XGB_OK = True
28
- except ImportError:
29
- XGB_OK = False
30
-
31
- try:
32
- from lightgbm import LGBMClassifier
33
- LGB_OK = True
34
- except ImportError:
35
- LGB_OK = False
36
-
37
- from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
38
- from sklearn.preprocessing import StandardScaler
39
-
40
- # ──────────────────────────────────────────────────────────
41
- # OKX REST API
42
- # ──────────────────────────────────────────────────────────
43
- OKX_BASE = "https://www.okx.com"
44
-
45
- # OKX instId formatı: BTC-USDT (tire ile)
46
- # Kullanıcıya gösterilecek etiket: BTCUSDT (tirensiz)
47
- SYMBOLS_LABEL = [
48
- "BTCUSDT","ETHUSDT","SOLUSDT","BNBUSDT","XRPUSDT",
49
- "ADAUSDT","DOGEUSDT","AVAXUSDT","DOTUSDT","LINKUSDT",
50
- "LTCUSDT","UNIUSDT","ATOMUSDT","NEARUSDT","OPUSDT",
51
- "ARBUSDT","INJUSDT","TIAUSDT","SUIUSDT","APTUSDT",
52
- ]
53
- def _to_instid(label: str) -> str:
54
- """BTCUSDT → BTC-USDT"""
55
- base = label.replace("USDT","")
56
- return f"{base}-USDT"
57
-
58
- # OKX bar formatları
59
- INTERVALS = ["5m","15m","1H","4H","1D"]
60
- _OKX_BAR = {"5m":"5m","15m":"15m","1H":"1H","4H":"4H","1D":"1D"}
61
-
62
- def fetch_okx(label: str, interval: str, limit: int = 300) -> pd.DataFrame:
63
- """OKX /api/v5/market/candles → OHLCV DataFrame"""
64
- inst = _to_instid(label)
65
- bar = _OKX_BAR.get(interval, interval)
66
- url = f"{OKX_BASE}/api/v5/market/candles"
67
- params = {"instId": inst, "bar": bar, "limit": min(limit, 300)}
68
- r = requests.get(url, params=params, timeout=12)
69
- r.raise_for_status()
70
- data = r.json()
71
- if data.get("code") != "0":
72
- raise RuntimeError(f"OKX hata: {data.get('msg','bilinmeyen')}")
73
- raw = data["data"] # liste: [[ts,o,h,l,c,vol,volCcy,volCcyQuote,confirm],...]
74
- if not raw:
75
- raise RuntimeError("OKX boş veri döndürdü.")
76
- df = pd.DataFrame(raw, columns=["ts","open","high","low","close","vol","volCcy","volCcyQ","confirm"])
77
- for c in ["open","high","low","close","vol"]:
78
- df[c] = df[c].astype(float)
79
- df["ts"] = pd.to_datetime(df["ts"].astype(np.int64), unit="ms", utc=True)
80
- df = df.sort_values("ts").set_index("ts")
81
- df.index.name = "open_time"
82
- return df[["open","high","low","close"]].rename(columns={"vol":"volume"}).assign(
83
- volume=df["vol"])
84
-
85
- def fetch_ticker(label: str) -> dict:
86
- """Anlık ticker: bid/ask/last/24h değişim"""
87
- inst = _to_instid(label)
88
- r = requests.get(f"{OKX_BASE}/api/v5/market/ticker",
89
- params={"instId": inst}, timeout=8)
90
- r.raise_for_status()
91
- d = r.json()
92
- if d.get("code") != "0" or not d["data"]:
93
- return {}
94
- t = d["data"][0]
95
- return {
96
- "last": float(t.get("last",0)),
97
- "bid": float(t.get("bidPx",0)),
98
- "ask": float(t.get("askPx",0)),
99
- "chg24": float(t.get("chgUtc8",0) or t.get("sodUtc8",0) or 0),
100
- "vol24": float(t.get("vol24h",0)),
101
- "high24": float(t.get("high24h",0)),
102
- "low24": float(t.get("low24h",0)),
103
- }
104
-
105
- # ──────────────────────────────────────────────────────────
106
- # TEKNİK GÖSTERGELer
107
- # ──────────────────────────────────────────────────────────
108
- def ema(s,n): return s.ewm(span=n,adjust=False).mean()
109
- def rsi(s,n=14):
110
- d=s.diff(); g=d.clip(lower=0).rolling(n).mean()
111
- l=(-d.clip(upper=0)).rolling(n).mean()
112
- return 100-(100/(1+g/l.replace(0,np.nan)))
113
- def macd(s):
114
- ml=s.ewm(span=12,adjust=False).mean()-s.ewm(span=26,adjust=False).mean()
115
- sig=ml.ewm(span=9,adjust=False).mean(); return ml,sig,ml-sig
116
- def atr(df,n=14):
117
- tr=pd.concat([df.high-df.low,
118
- (df.high-df.close.shift()).abs(),
119
- (df.low -df.close.shift()).abs()],axis=1).max(axis=1)
120
- return tr.rolling(n).mean()
121
- def vwap(df):
122
- tp=(df.high+df.low+df.close)/3
123
- return (tp*df.volume).cumsum()/df.volume.cumsum()
124
-
125
- def add_indicators(df):
126
- df=df.copy()
127
- df["ema20"] =ema(df.close,20)
128
- df["ema50"] =ema(df.close,50)
129
- df["ema200"]=ema(df.close,200)
130
- df["rsi"] =rsi(df.close)
131
- df["macd"],df["macd_sig"],df["macd_hist"]=macd(df.close)
132
- df["atr"] =atr(df)
133
- df["vwap"] =vwap(df)
134
- df["vol_ma"]=df.volume.rolling(20).mean()
135
- df["vol_ratio"]=df.volume/df.vol_ma.replace(0,np.nan)
136
- return df.dropna()
137
-
138
- # ──────────────────────────────────────────────────────────
139
- # SİNYAL MANTIĞI
140
- # ──────────────────────────────────────────────────────────
141
- def generate_signal(df):
142
- l,p=df.iloc[-1],df.iloc[-2]
143
- bc=[l.ema20>l.ema50, l.ema50>l.ema200, l.rsi>50,
144
- bool((l.macd>l.macd_sig) and (p.macd<=p.macd_sig)),
145
- l.close>l.vwap]
146
- sc=[l.ema20<l.ema50, l.ema50<l.ema200, l.rsi<50,
147
- bool((l.macd<l.macd_sig) and (p.macd>=p.macd_sig)),
148
- l.close<l.vwap]
149
- bs,ss=sum(bc),sum(sc)
150
- px,at=l.close,l.atr
151
- if bs>=3:
152
- sig="AL 📈"; conf=int(bs/5*100)
153
- sl=round(px-1.5*at,6); tp=round(px+3.0*at,6)
154
- elif ss>=3:
155
- sig="SAT 📉"; conf=int(ss/5*100)
156
- sl=round(px+1.5*at,6); tp=round(px-3.0*at,6)
157
- else:
158
- sig="BEKLE ⏳"; conf=30
159
- sl=round(px-1.5*at,6); tp=round(px+1.5*at,6)
160
- rr=round(abs(tp-px)/abs(sl-px),2) if abs(sl-px)>0 else 0
161
- cond_txt=(
162
- f"EMA20>50:{'✅' if bc[0] else '❌'} "
163
- f"EMA50>200:{'✅' if bc[1] else '❌'} "
164
- f"RSI>50:{'✅' if bc[2] else '❌'} "
165
- f"MACD✗:{'✅' if bc[3] else '❌'} "
166
- f"Fiyat>VWAP:{'✅' if bc[4] else '❌'}"
167
- )
168
- return sig,conf,sl,tp,rr,cond_txt
169
-
170
- # ──────────────────────────────────────────────────────────
171
- # AI — PREMIUM ENSEMBLE (XGBoost + LightGBM + RF + GBM)
172
- # ──────────────────────────────────────────────────────────
173
- FCOLS = ["r1","r3","r5","r10",
174
- "e2050","e50200",
175
- "rsi_n","rsi_slope",
176
- "macd_n","mhist_n","macd_slope",
177
- "vwap_d","atr_n","volr",
178
- "hl_ratio","body_ratio"]
179
-
180
- def featurize(df):
181
- d = df.copy()
182
- # Fiyat değişimleri
183
- d["r1"] = d.close.pct_change(1)
184
- d["r3"] = d.close.pct_change(3)
185
- d["r5"] = d.close.pct_change(5)
186
- d["r10"] = d.close.pct_change(10)
187
- # EMA mesafeleri
188
- d["e2050"] = (d.ema20 - d.ema50) / d.close
189
- d["e50200"] = (d.ema50 - d.ema200) / d.close
190
- # RSI
191
- d["rsi_n"] = d.rsi / 100
192
- d["rsi_slope"]= d.rsi.diff(3) / 100
193
- # MACD
194
- d["macd_n"] = d.macd / d.close
195
- d["mhist_n"] = d.macd_hist / d.close
196
- d["macd_slope"]= d.macd.diff(3) / d.close
197
- # Fiyat konumu
198
- d["vwap_d"] = (d.close - d.vwap) / d.close
199
- d["atr_n"] = d.atr / d.close
200
- d["volr"] = d.vol_ratio.clip(0, 5)
201
- # Mum şekli
202
- d["hl_ratio"] = (d.high - d.low) / d.close
203
- d["body_ratio"]= (d.close - d.open).abs() / (d.high - d.low + 1e-9)
204
- return d.dropna()
205
-
206
- # Her modelin bilgisi: isim, ağırlık, renk
207
- ENSEMBLE_META = [
208
- {"name": "XGBoost", "w": 0.25, "color": "#64b5f6"},
209
- {"name": "LightGBM", "w": 0.25, "color": "#a5d6a7"},
210
- {"name": "RndForest", "w": 0.25, "color": "#ffcc02"},
211
- {"name": "GradBoost", "w": 0.25, "color": "#ce93d8"},
212
- ]
213
-
214
- def _build_models():
215
- """Tüm ensemble modellerini döndür; eksik kütüphane varsa atlar."""
216
- models = []
217
- if XGB_OK:
218
- models.append((
219
- ENSEMBLE_META[0],
220
- XGBClassifier(n_estimators=80, max_depth=3, learning_rate=0.08,
221
- subsample=0.8, colsample_bytree=0.8,
222
- eval_metric="logloss", verbosity=0, n_jobs=1)
223
- ))
224
- if LGB_OK:
225
- models.append((
226
- ENSEMBLE_META[1],
227
- LGBMClassifier(n_estimators=80, max_depth=4, learning_rate=0.08,
228
- subsample=0.8, colsample_bytree=0.8,
229
- verbose=-1, n_jobs=1)
230
- ))
231
- # scikit-learn modelleri her zaman mevcut
232
- models.append((
233
- ENSEMBLE_META[2],
234
- RandomForestClassifier(n_estimators=120, max_depth=5,
235
- min_samples_leaf=3, n_jobs=1, random_state=42)
236
- ))
237
- models.append((
238
- ENSEMBLE_META[3],
239
- GradientBoostingClassifier(n_estimators=80, max_depth=3,
240
- learning_rate=0.08, subsample=0.8,
241
- random_state=42)
242
- ))
243
- return models
244
-
245
- def train_ensemble(df):
246
- """
247
- Premium Ensemble eğitimi.
248
- Returns:
249
- ai_conf : int 0-100 (ağırlıklı ortalama prob)
250
- model_probs: list[dict] her modelin detayı
251
- """
252
- fd = featurize(df)
253
- if len(fd) < 80:
254
- return 50, []
255
-
256
- fd = fd.copy()
257
- fd["tgt"] = (fd.close.shift(-3) > fd.close).astype(int)
258
- fd = fd.dropna()
259
- if len(fd) < 50:
260
- return 50, []
261
-
262
- X = fd[FCOLS].values
263
- y = fd["tgt"].values
264
- sc = StandardScaler()
265
- Xs = sc.fit_transform(X)
266
- sp = int(len(Xs) * 0.80)
267
-
268
- last_feat = featurize(df.tail(80)).tail(1)[FCOLS].values
269
- last_scaled = sc.transform(last_feat)
270
-
271
- total_w = 0.0
272
- weighted = 0.0
273
- model_probs = []
274
-
275
- for meta, clf in _build_models():
276
- try:
277
- clf.fit(Xs[:sp], y[:sp])
278
- prob = clf.predict_proba(last_scaled)[0][1]
279
- model_probs.append({
280
- "name": meta["name"],
281
- "prob": prob,
282
- "color": meta["color"],
283
- "w": meta["w"],
284
- })
285
- weighted += meta["w"] * prob
286
- total_w += meta["w"]
287
- except Exception:
288
- pass
289
-
290
- if total_w == 0:
291
- return 50, []
292
-
293
- ai_conf = int((weighted / total_w) * 100)
294
- return ai_conf, model_probs
295
-
296
- # Geriye dönük uyumluluk — eski çağrı noktası
297
- def train_ai(df):
298
- conf, _ = train_ensemble(df)
299
- return conf
300
-
301
- # ──────────────────────────────────────────────────────────
302
- # GRAFİK
303
- # ──────────────────────────────────────────────────────────
304
- def build_chart(df, label, interval, signal, sl, tp, ticker):
305
- p=df.tail(150).copy()
306
- fig=make_subplots(
307
- rows=4,cols=1,shared_xaxes=True,
308
- row_heights=[0.52,0.16,0.16,0.16],
309
- vertical_spacing=0.02,
310
- subplot_titles=["","RSI","MACD","Volume"],
311
- )
312
-
313
- # ── Mum grafiği
314
- fig.add_trace(go.Candlestick(
315
- x=p.index,open=p.open,high=p.high,low=p.low,close=p.close,
316
- name="Fiyat",
317
- increasing_line_color="#00e676", decreasing_line_color="#ff5252",
318
- increasing_fillcolor="rgba(0,200,83,0.53)",decreasing_fillcolor="rgba(255,82,82,0.53)",
319
- line_width=1,
320
- ),row=1,col=1)
321
-
322
- # ── EMAs
323
- for n,c,w,dash in [
324
- (20,"#64b5f6",1.5,"solid"),
325
- (50,"#ffb74d",1.5,"solid"),
326
- (200,"#ce93d8",2.0,"dot"),
327
- ]:
328
- fig.add_trace(go.Scatter(x=p.index,y=p[f"ema{n}"],name=f"EMA{n}",
329
- line=dict(color=c,width=w,dash=dash),opacity=0.85),row=1,col=1)
330
-
331
- # ── VWAP
332
- fig.add_trace(go.Scatter(x=p.index,y=p.vwap,name="VWAP",
333
- line=dict(color="#f06292",width=1.4,dash="dashdot"),opacity=0.8),row=1,col=1)
334
-
335
- # ── SL / TP çizgileri
336
- fig.add_hline(y=sl,line_dash="dash",line_color="#ff525299",line_width=1.2,
337
- annotation_text=f"SL {sl:,.4f}",annotation_font_color="#ff5252",
338
- annotation_position="right",row=1,col=1)
339
- fig.add_hline(y=tp,line_dash="dash",line_color="#00e67699",line_width=1.2,
340
- annotation_text=f"TP {tp:,.4f}",annotation_font_color="#00e676",
341
- annotation_position="right",row=1,col=1)
342
-
343
- # ── Sinyal oku (son barda)
344
- li,lc=p.index[-1],p.close.iloc[-1]
345
- if "AL" in signal:
346
- fig.add_trace(go.Scatter(x=[li],y=[lc*0.992],mode="markers+text",
347
- marker=dict(symbol="triangle-up",size=18,color="#00e676"),
348
- text=["AL"],textposition="bottom center",
349
- textfont=dict(color="#00e676",size=11),showlegend=False),row=1,col=1)
350
- elif "SAT" in signal:
351
- fig.add_trace(go.Scatter(x=[li],y=[lc*1.008],mode="markers+text",
352
- marker=dict(symbol="triangle-down",size=18,color="#ff5252"),
353
- text=["SAT"],textposition="top center",
354
- textfont=dict(color="#ff5252",size=11),showlegend=False),row=1,col=1)
355
-
356
- # ── RSI
357
- fig.add_trace(go.Scatter(x=p.index,y=p.rsi,name="RSI",
358
- line=dict(color="#80cbc4",width=1.4),fill="tozeroy",
359
- fillcolor="rgba(128,203,196,0.06)"),row=2,col=1)
360
- for yv,c in [(70,"#ff525266"),(50,"#55555566"),(30,"#00e67666")]:
361
- fig.add_hline(y=yv,line_dash="dot",line_color=c,line_width=0.8,row=2,col=1)
362
-
363
- # ── MACD histogram + çizgiler
364
- clrs=["#00c853aa" if v>=0 else "#ff1744aa" for v in p.macd_hist]
365
- fig.add_trace(go.Bar(x=p.index,y=p.macd_hist,name="Hist",
366
- marker_color=clrs,showlegend=False),row=3,col=1)
367
- fig.add_trace(go.Scatter(x=p.index,y=p.macd,name="MACD",
368
- line=dict(color="#64b5f6",width=1.2)),row=3,col=1)
369
- fig.add_trace(go.Scatter(x=p.index,y=p.macd_sig,name="Signal",
370
- line=dict(color="#ffb74d",width=1.2)),row=3,col=1)
371
-
372
- # ── Volume
373
- vcols=["#00c85366" if c>=o else "#ff525266"
374
- for c,o in zip(p.close,p.open)]
375
- fig.add_trace(go.Bar(x=p.index,y=p.volume,name="Vol",
376
- marker_color=vcols,showlegend=False),row=4,col=1)
377
- fig.add_trace(go.Scatter(x=p.index,y=p.vol_ma,name="Vol MA",
378
- line=dict(color="#ffcc0299",width=1.2)),row=4,col=1)
379
-
380
- # ── Son fiyat annotation
381
- price_str=f"${lc:,.4f}"
382
- fig.add_annotation(x=1,y=lc,xref="paper",yref="y",
383
- text=f" {price_str} ",showarrow=False,
384
- font=dict(color="#fff",size=11,family="Courier New"),
385
- bgcolor="#1565c0",bordercolor="#42a5f5",borderwidth=1,
386
- xanchor="left",yanchor="middle")
387
-
388
- # ── Başlık
389
- chg = ticker.get("chg24",0)
390
- chg_col="#00e676" if chg>=0 else "#ff5252"
391
- chg_sym="▲" if chg>=0 else "▼"
392
- title_txt=(f"<b>{label}</b> <span style='font-size:13px;color:{chg_col}'>"
393
- f"{chg_sym} {abs(chg):.2f}%</span> "
394
- f"<span style='font-size:11px;color:#666'>{interval} · OKX</span>")
395
-
396
- fig.update_layout(
397
- title=dict(text=title_txt,font=dict(color="#e0e0e0",size=15,
398
- family="'Courier New',monospace"),x=0.01,xanchor="left"),
399
- paper_bgcolor="#0b0b10",plot_bgcolor="#0f0f16",
400
- font=dict(color="#9e9e9e",family="'Courier New',monospace",size=10),
401
- xaxis_rangeslider_visible=False,
402
- height=800,
403
- legend=dict(bgcolor="rgba(15,15,22,0.85)",bordercolor="#222",
404
- borderwidth=1,font=dict(size=9),x=0.01,y=0.99,
405
- xanchor="left",yanchor="top"),
406
- margin=dict(l=55,r=80,t=55,b=15),
407
- hoverlabel=dict(bgcolor="#1a1a2e",bordercolor="#333",
408
- font_color="#e0e0e0",font_family="Courier New"),
409
- )
410
- for i in range(1,5):
411
- fig.update_xaxes(gridcolor="#191924",zeroline=False,
412
- showspikes=True,spikecolor="#444",
413
- spikethickness=1,row=i,col=1)
414
- fig.update_yaxes(gridcolor="#191924",zeroline=False,
415
- showspikes=True,spikecolor="#444",row=i,col=1)
416
- return fig
417
-
418
- # ──────────────────────────────────────────────────────────
419
- # PANEL HTML
420
- # ──────────────────────────────────────────────────────────
421
- def build_panel(label,interval,signal,tech_conf,ai_conf,sl,tp,rr,
422
- cond_txt,ticker,df,model_probs=None):
423
- final=int(0.5*tech_conf+0.5*ai_conf)
424
- price=df.close.iloc[-1]
425
- rsi_v=df.rsi.iloc[-1]
426
- atr_v=df.atr.iloc[-1]
427
- ema20=df.ema20.iloc[-1]; ema50=df.ema50.iloc[-1]; ema200=df.ema200.iloc[-1]
428
- vwap_v=df.vwap.iloc[-1]
429
- now=datetime.now(timezone.utc).strftime("%H:%M:%S UTC")
430
-
431
- if "AL" in signal: dir_txt,dir_col,bg="🟢 YUKARI","#00e676","rgba(0,200,83,0.08)"
432
- elif "SAT" in signal: dir_txt,dir_col,bg="🔴 AŞAĞI","#ff5252","rgba(255,82,82,0.08)"
433
- else: dir_txt,dir_col,bg="🟡 YATAY","#ffb74d","rgba(255,183,77,0.08)"
434
-
435
- bar_col=("#238636" if final>=65 else "#f0883e" if final>=40 else "#da3633")
436
- chg=ticker.get("chg24",0); chg_col="#00e676" if chg>=0 else "#ff5252"
437
- h24=ticker.get("high24",0); l24=ticker.get("low24",0)
438
- vol24=ticker.get("vol24",0)
439
-
440
- def row(label,val,extra="",bg_tr=""):
441
- bg_style=f"background:{bg_tr};" if bg_tr else ""
442
- return (f'<tr style="{bg_style}">'
443
- f'<td style="padding:7px 10px;color:#6e7681;font-size:.85em">{label}</td>'
444
- f'<td style="padding:7px 10px;font-size:.9em">{val}</td>'
445
- f'{"<td style=padding:7px_10px;font-size:.8em;color:#555>"+extra+"</td>" if extra else ""}'
446
- f'</tr>')
447
-
448
- # ── Premium Ensemble Breakdown HTML
449
- if model_probs:
450
- rows_html = ""
451
- for mp in model_probs:
452
- p_pct = int(mp["prob"] * 100)
453
- w_pct = int(mp["w"] * 100)
454
- col = mp["color"]
455
- bull = "AL ▲" if mp["prob"] >= 0.5 else "SAT ▼"
456
- b_col = "#00e676" if mp["prob"] >= 0.5 else "#ff5252"
457
- rows_html += f"""
458
- <tr>
459
- <td style="padding:5px 8px;color:#8b949e;font-size:.75em;white-space:nowrap">
460
- <span style="display:inline-block;width:8px;height:8px;border-radius:50%;
461
- background:{col};margin-right:5px;box-shadow:0 0 5px {col}88"></span>
462
- {mp["name"]}
463
- </td>
464
- <td style="padding:5px 8px;font-size:.75em">
465
- <div style="background:#0d1117;border-radius:4px;overflow:hidden;height:7px;width:80px;display:inline-block;vertical-align:middle;margin-right:6px">
466
- <div style="width:{p_pct}%;height:100%;background:linear-gradient(90deg,{col}99,{col})"></div>
467
- </div>
468
- <span style="color:{col}">{p_pct}%</span>
469
- </td>
470
- <td style="padding:5px 8px;font-size:.72em;color:{b_col}">{bull}</td>
471
- <td style="padding:5px 8px;font-size:.68em;color:#484f58">ağırlık {w_pct}%</td>
472
- </tr>"""
473
- ensemble_html = f"""
474
- <!-- Ensemble Breakdown -->
475
- <div style="margin-bottom:14px;padding:10px;background:#0a0f1a;
476
- border:1px solid #1c2a3a;border-radius:10px;">
477
- <div style="font-size:.68em;color:#3d5afe;letter-spacing:2px;
478
- margin-bottom:8px;display:flex;align-items:center;gap:6px">
479
- <span style="background:linear-gradient(90deg,#3d5afe,#00e5ff);
480
- -webkit-background-clip:text;-webkit-text-fill-color:transparent;
481
- font-weight:bold">⚡ PREMIUM AI ENSEMBLE</span>
482
- <span style="color:#1c2a4a">4 Model · Eşit Ağırlıklı Oylama</span>
483
- </div>
484
- <table style="width:100%;border-collapse:collapse">{rows_html}</table>
485
- </div>"""
486
- else:
487
- ensemble_html = ""
488
-
489
- html=f"""
490
- <div style="background:linear-gradient(160deg,#0d1117 0%,#161b22 100%);
491
- border:1px solid #21262d;border-radius:14px;padding:18px 16px;
492
- font-family:'Courier New',monospace;color:#e6edf3;min-width:260px;">
493
-
494
- <!-- Header -->
495
- <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;">
496
- <div>
497
- <span style="font-size:1.1em;font-weight:bold;color:#58a6ff">{label}</span>
498
- <span style="margin-left:8px;font-size:.75em;color:#444;letter-spacing:1px">{interval} · OKX</span>
499
- </div>
500
- <div style="font-size:.7em;color:#30363d">{now}</div>
501
- </div>
502
-
503
- <!-- Büyük sinyal kutusu -->
504
- <div style="background:{bg};border:1px solid {dir_col}33;border-radius:10px;
505
- padding:14px;text-align:center;margin-bottom:14px;">
506
- <div style="font-size:1.6em;font-weight:bold;letter-spacing:2px">{signal}</div>
507
- <div style="font-size:1.1em;color:{dir_col};margin-top:4px">{dir_txt}</div>
508
- </div>
509
-
510
- <!-- Güven çubuğu -->
511
- <div style="margin-bottom:14px">
512
- <div style="display:flex;justify-content:space-between;font-size:.75em;
513
- color:#8b949e;margin-bottom:4px">
514
- <span>Güven Skoru</span><span style="color:#e6edf3;font-weight:bold">{final}%</span>
515
- </div>
516
- <div style="background:#21262d;border-radius:6px;overflow:hidden;height:10px">
517
- <div style="background:linear-gradient(90deg,{bar_col},{bar_col}bb);
518
- width:{final}%;height:100%;border-radius:6px;
519
- box-shadow:0 0 8px {bar_col}66"></div>
520
- </div>
521
- <div style="display:flex;justify-content:space-between;font-size:.7em;
522
- color:#484f58;margin-top:3px">
523
- <span>🔬 Teknik: {tech_conf}%</span><span>🤖 AI Ensemble: {ai_conf}%</span>
524
- </div>
525
- </div>
526
-
527
- {ensemble_html}
528
-
529
- <!-- Tablo -->
530
- <table style="width:100%;border-collapse:collapse;font-size:.88em">
531
- {row("💰 Güncel Fiyat", f'<b>${price:,.6g}</b>')}
532
- {row("24h Değişim", f'<span style="color:{chg_col}">{"▲" if chg>=0 else "▼"} {abs(chg):.2f}%</span>',
533
- bg_tr="rgba(255,255,255,.025)")}
534
- {row("24h Yüksek / Düşük", f'<span style="color:#00e676">{h24:,.6g}</span> / <span style="color:#ff5252">{l24:,.6g}</span>')}
535
- {row("🛑 Stop Loss", f'<b style="color:#ff5252">${sl:,.6g}</b>',
536
- bg_tr="rgba(255,82,82,.04)")}
537
- {row("✅ Take Profit", f'<b style="color:#00e676">${tp:,.6g}</b>',
538
- bg_tr="rgba(0,200,83,.04)")}
539
- {row("⚖️ Risk/Ödül", f'<b>{rr}x</b>')}
540
- {row("📉 RSI (14)", f'{rsi_v:.1f}', bg_tr="rgba(255,255,255,.025)")}
541
- {row("📏 ATR (14)", f'${atr_v:,.6g}')}
542
- {row("EMA 20/50/200", f'{ema20:,.6g} / {ema50:,.6g} / {ema200:,.6g}',
543
- bg_tr="rgba(255,255,255,.025)")}
544
- {row("VWAP", f'${vwap_v:,.6g}')}
545
- {row("24h Hacim", f'{vol24:,.0f}')}
546
- </table>
547
-
548
- <!-- Koşullar -->
549
- <div style="margin-top:12px;padding:10px;background:#0d1117;border-radius:8px;
550
- border:1px solid #1c2128;font-size:.72em;color:#8b949e;line-height:1.8">
551
- <div style="color:#444;margin-bottom:4px;letter-spacing:1px">SİNYAL KOŞULLARI</div>
552
- {cond_txt.replace(" ","&nbsp;&nbsp;")}
553
- </div>
554
-
555
- <p style="margin:12px 0 0;font-size:.65em;color:#30363d;text-align:center;letter-spacing:.5px">
556
- ⚠️ Eğitim amaçlıdır — finansal tavsiye değildir. DYOR.
557
- </p>
558
- </div>"""
559
- return html
560
-
561
- # ──────────────────────────────────────────────────────────
562
- # ANA ANALİZ FONKSİYONU
563
- # ──────────────────────────────────────────────────────────
564
- def analyze(label, interval):
565
- # 1. Veri çek
566
- try:
567
- df_raw=fetch_okx(label, interval, limit=300)
568
- except Exception as e:
569
- err=f"<div style='color:#ff5252;font-family:monospace;padding:20px'>❌ OKX veri hatası:<br>{e}</div>"
570
- return None, err
571
-
572
- # 2. Göstergeler
573
- df=add_indicators(df_raw)
574
- if len(df)<50:
575
- return None,"<div style='color:#ff5252;font-family:monospace;padding:20px'>❌ Yeterli veri yok.</div>"
576
-
577
- # 3. Sinyal
578
- sig,tech_conf,sl,tp,rr,cond_txt=generate_signal(df)
579
-
580
- # 4. AI — Premium Ensemble
581
- ai_conf, model_probs = train_ensemble(df)
582
-
583
- # 5. Ticker
584
- try:
585
- ticker=fetch_ticker(label)
586
- except:
587
- ticker={}
588
-
589
- # 6. Grafik & Panel
590
- fig=build_chart(df,label,interval,sig,sl,tp,ticker)
591
- panel=build_panel(label,interval,sig,tech_conf,ai_conf,sl,tp,rr,cond_txt,ticker,df,model_probs)
592
- return fig, panel
593
-
594
- # ──────────────────────────────────────────────────────────
595
- # GRADIO UI
596
- # ──────────────────────────────────────────────────────────
597
- CSS="""
598
- body,.gradio-container{background:#07070d !important;}
599
- footer{display:none !important;}
600
- .gr-button.primary{
601
- background:linear-gradient(135deg,#1565c0,#4a148c) !important;
602
- border:1px solid #42a5f566 !important;color:#fff !important;
603
- font-family:'Courier New',monospace !important;letter-spacing:1px !important;
604
- transition:all .2s !important;
605
- }
606
- .gr-button.primary:hover{
607
- background:linear-gradient(135deg,#1976d2,#6a1b9a) !important;
608
- box-shadow:0 0 16px #42a5f544 !important;
609
- }
610
- .gr-button{
611
- background:#0f0f1a !important;border:1px solid #1e1e3f !important;
612
- color:#8b949e !important;font-family:'Courier New',monospace !important;
613
- }
614
- select,input{background:#0d1117 !important;border:1px solid #21262d !important;
615
- color:#e6edf3 !important;font-family:'Courier New',monospace !important;}
616
- """
617
-
618
- HEADER="""
619
- <div style="background:linear-gradient(135deg,#07070d,#0d0d1a,#070712);
620
- border:1px solid #1c1c3a;border-radius:16px;padding:24px 28px;
621
- text-align:center;font-family:'Courier New',monospace;
622
- margin-bottom:6px;position:relative;overflow:hidden;">
623
- <div style="position:absolute;top:0;left:0;right:0;height:2px;
624
- background:linear-gradient(90deg,transparent,#3d5afe,#00e5ff,#7c4dff,transparent)"></div>
625
- <div style="position:absolute;bottom:0;left:0;right:0;height:1px;
626
- background:linear-gradient(90deg,transparent,#1c2a4a,transparent)"></div>
627
- <h1 style="margin:0 0 4px;font-size:1.75em;letter-spacing:5px;
628
- background:linear-gradient(90deg,#3d5afe,#00e5ff,#7c4dff);
629
- -webkit-background-clip:text;-webkit-text-fill-color:transparent;
630
- background-clip:text;text-transform:uppercase;font-weight:900">
631
- ⚡ CRYPTO SIGNAL PRO
632
- </h1>
633
- <p style="margin:0;color:#2a3550;letter-spacing:3px;font-size:.68em;text-transform:uppercase">
634
- OKX REST API · XGBoost AI · Canlı Otomatik Analiz
635
- </p>
636
- <div style="display:flex;justify-content:center;gap:18px;margin-top:10px;
637
- font-size:.65em;color:#1c2a4a;letter-spacing:2px">
638
- <span>EMA 20/50/200</span><span>·</span><span>RSI · MACD</span>
639
- <span>·</span><span>VWAP · ATR</span><span>·</span><span>Volume Profile</span>
640
- </div>
641
- </div>"""
642
-
643
- STATUS_TMPL="""
644
- <div style="display:flex;align-items:center;gap:10px;padding:8px 14px;
645
- background:#0d1117;border:1px solid #21262d;border-radius:8px;
646
- font-family:'Courier New',monospace;font-size:.78em;color:#8b949e;">
647
- <span style="width:8px;height:8px;border-radius:50%;background:{col};
648
- box-shadow:0 0 6px {col};display:inline-block"></span>
649
- {msg}
650
- </div>"""
651
-
652
- def make_status(msg,ok=True):
653
- return STATUS_TMPL.format(col="#00e676" if ok else "#ff5252",msg=msg)
654
-
655
- # Refresh count (hafıza için state)
656
- _refresh_count = {"n": 0}
657
-
658
- def run_and_status(label, interval):
659
- _refresh_count["n"] += 1
660
- n = _refresh_count["n"]
661
- fig, panel = analyze(label, interval)
662
- now = datetime.now(timezone.utc).strftime("%H:%M:%S UTC")
663
- if fig is None:
664
- st = make_status(f"#{n} · {now} ❌ Hata",ok=False)
665
- else:
666
- st = make_status(f"#{n} · {now} ✅ {label} / {interval} güncellendi")
667
- return fig, panel, st
668
-
669
- with gr.Blocks(css=CSS, title="⚡ Crypto Signal Pro · OKX") as demo:
670
- gr.HTML(HEADER)
671
-
672
- with gr.Row(equal_height=True):
673
- sym = gr.Dropdown(choices=SYMBOLS_LABEL, value="BTCUSDT",
674
- label="📊 Parite", scale=3, interactive=True)
675
- ivl = gr.Dropdown(choices=INTERVALS, value="1H",
676
- label="⏱ Zaman Dilimi", scale=2, interactive=True)
677
- auto_cb = gr.Checkbox(value=True, label="🔄 Otomatik Yenile", scale=1)
678
- btn = gr.Button("🔍 ANALİZ ET", variant="primary", scale=2)
679
-
680
- status_box = gr.HTML(make_status("Hazır — Analiz Et butonuna bas veya otomatik yenilemeyi bekle."))
681
-
682
- with gr.Row():
683
- chart_out = gr.Plot(label="Canlı Grafik", scale=3)
684
- panel_out = gr.HTML(label="Sinyal Paneli", scale=1)
685
-
686
- # Manuel buton
687
- btn.click(fn=run_and_status,
688
- inputs=[sym, ivl],
689
- outputs=[chart_out, panel_out, status_box])
690
-
691
- # Otomatik yenileme — gr.Timer (Gradio 4.x+)
692
- # Her 30 saniyede bir tetiklenir; checkbox kapalıysa atlar
693
- def auto_refresh(label, interval, enabled):
694
- if not enabled:
695
- return gr.update(), gr.update(), gr.update()
696
- return run_and_status(label, interval)
697
-
698
- timer = gr.Timer(value=30) # 30 sn
699
- timer.tick(fn=auto_refresh,
700
- inputs=[sym, ivl, auto_cb],
701
- outputs=[chart_out, panel_out, status_box])
702
-
703
- gr.HTML("""
704
- <div style="text-align:center;padding:10px;color:#1c2a4a;
705
- font-size:.62em;font-family:'Courier New',monospace;letter-spacing:1px">
706
- CRYPTO SIGNAL PRO · OKX REST API · XGBoost AI · Plotly · Gradio<br>
707
- ⚠️ Yatırım tavsiyesi değildir — DYOR
708
- </div>""")
709
-
710
- if __name__ == "__main__":
711
- demo.launch(server_name="0.0.0.0", server_port=7860)