GoshawkVortexAI commited on
Commit
4f96d6d
·
verified ·
1 Parent(s): c09ca38

Delete app.py

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