daafa999 commited on
Commit
e817e4e
·
verified ·
1 Parent(s): a41a64e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +559 -352
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
  import time
3
- import logging
4
  import traceback
 
5
  from datetime import datetime
6
 
7
  import gradio as gr
@@ -9,558 +9,765 @@ import yfinance as yf
9
  import pandas as pd
10
  import numpy as np
11
  import joblib
 
12
 
13
- from sklearn.ensemble import RandomForestClassifier
14
  from sklearn.preprocessing import MinMaxScaler
 
15
 
16
- # ============================================================
17
- # LOGGING
18
- # ============================================================
19
 
20
- logging.basicConfig(level=logging.INFO)
21
- logger = logging.getLogger("crypto_ml")
22
 
23
  # ============================================================
 
24
  # CONFIG
 
25
  # ============================================================
26
 
27
- SYMBOL = "ETH-USD"
 
 
 
 
 
 
 
 
 
 
28
  WINDOW = 20
29
- FEE = 0.0015
30
 
31
- MODEL_PATH = "/tmp/model.pkl"
32
- SCALER_PATH = "/tmp/scaler.pkl"
 
 
 
 
 
 
33
 
34
  # ============================================================
35
- # CLEAN DATA
 
 
36
  # ============================================================
37
 
38
- def clean_columns(df):
39
 
40
- if isinstance(df.columns, pd.MultiIndex):
41
- df.columns = df.columns.get_level_values(0)
42
 
43
- df.columns = [str(c).strip() for c in df.columns]
 
 
44
 
45
- rename = {
46
- "open": "Open",
47
- "high": "High",
48
- "low": "Low",
49
- "close": "Close",
50
- "adj close": "Close",
51
- "volume": "Volume"
52
- }
53
 
54
- df.columns = [
55
- rename.get(c.lower(), c)
56
- for c in df.columns
57
- ]
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- return df
60
 
61
  # ============================================================
62
- # SYNTHETIC DATA
 
 
63
  # ============================================================
64
 
65
- def synthetic_data(n=500):
 
66
 
67
- np.random.seed(42)
68
 
69
- dates = pd.date_range(
70
- end=datetime.utcnow(),
71
- periods=n,
72
- freq="4h"
 
 
73
  )
74
 
75
- price = 3000
 
 
 
 
 
 
 
76
 
77
- opens = []
78
- highs = []
79
- lows = []
80
- closes = []
81
- volumes = []
82
 
83
- for _ in range(n):
84
 
85
- ret = np.random.normal(0, 0.015)
86
 
87
- open_p = price
88
- close_p = price * (1 + ret)
89
 
90
- high_p = max(open_p, close_p) * 1.01
91
- low_p = min(open_p, close_p) * 0.99
92
 
93
- vol = np.random.randint(1000, 5000)
94
 
95
- opens.append(open_p)
96
- highs.append(high_p)
97
- lows.append(low_p)
98
- closes.append(close_p)
99
- volumes.append(vol)
100
 
101
- price = close_p
 
102
 
103
- df = pd.DataFrame({
104
- "Open": opens,
105
- "High": highs,
106
- "Low": lows,
107
- "Close": closes,
108
- "Volume": volumes
109
- }, index=dates)
110
 
111
- return df
112
 
113
- # ============================================================
114
- # DOWNLOAD DATA
115
- # ============================================================
116
 
117
- def safe_download():
118
 
119
- try:
120
 
121
- df = yf.download(
122
- SYMBOL,
123
- period="90d",
124
- interval="1h",
125
- progress=False,
126
- auto_adjust=True,
127
- threads=False
128
- )
129
 
130
- if df is not None and not df.empty:
 
 
 
 
 
 
 
131
 
132
- df = clean_columns(df)
133
 
134
- return df, True
135
 
136
- except Exception as e:
137
- logger.warning(e)
 
 
 
 
 
138
 
139
- return synthetic_data(), False
140
 
141
  # ============================================================
 
142
  # FEATURE ENGINEERING
 
143
  # ============================================================
144
 
145
- def fetch_and_prepare():
 
 
 
 
 
 
146
 
147
- df, is_real = safe_download()
 
 
 
 
 
 
148
 
149
- if not isinstance(df.index, pd.DatetimeIndex):
150
- df.index = pd.to_datetime(df.index)
151
 
152
- df = df.resample("4h").agg({
153
- "Open": "first",
154
- "High": "max",
155
- "Low": "min",
156
- "Close": "last",
157
- "Volume": "sum"
158
- }).dropna()
159
 
160
- df.columns = [c.lower() for c in df.columns]
 
 
 
161
 
162
- # RSI
 
 
 
163
 
164
- delta = df["close"].diff()
165
 
166
- gain = delta.where(delta > 0, 0).rolling(14).mean()
167
- loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
 
168
 
169
- rs = gain / loss.replace(0, np.nan)
170
 
171
- df["rsi"] = (
172
- 100 - (100 / (1 + rs))
173
- ).fillna(50)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
- # MACD
 
 
 
 
176
 
177
- ema12 = df["close"].ewm(span=12).mean()
178
- ema26 = df["close"].ewm(span=26).mean()
 
 
 
179
 
180
- df["macd"] = ema12 - ema26
181
- df["macd_signal"] = df["macd"].ewm(span=9).mean()
 
 
 
182
 
183
- # Bollinger
184
 
185
- sma20 = df["close"].rolling(20).mean()
186
- std20 = df["close"].rolling(20).std()
 
 
 
187
 
188
- df["bb_upper"] = sma20 + (2 * std20)
189
- df["bb_lower"] = sma20 - (2 * std20)
 
 
 
190
 
191
- # EMA
 
192
 
193
- df["ema9"] = df["close"].ewm(span=9).mean()
194
- df["ema21"] = df["close"].ewm(span=21).mean()
195
 
196
- # Return
 
 
 
 
197
 
198
- df["return1"] = df["close"].pct_change()
199
- df["return4"] = df["close"].pct_change(4)
200
 
201
- # Label
 
 
 
 
202
 
203
- df["next_close"] = df["close"].shift(-1)
 
 
 
 
204
 
205
- df["label"] = (
206
- df["next_close"] >
207
- df["close"] * (1 + FEE)
208
- ).astype(int)
209
 
210
- df = df.replace([np.inf, -np.inf], np.nan)
211
- df = df.dropna()
 
 
 
212
 
213
- feat_cols = [
214
- "open",
215
- "high",
216
- "low",
217
- "close",
218
- "volume",
219
- "rsi",
220
- "macd",
221
- "macd_signal",
222
- "bb_upper",
223
- "bb_lower",
224
- "ema9",
225
- "ema21",
226
- "return1",
227
- "return4"
228
- ]
229
 
230
- X_list = []
231
- y_list = []
232
 
233
- for i in range(WINDOW, len(df)):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
235
- window = df.iloc[
236
- i-WINDOW:i
237
- ][feat_cols].values
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
- if np.isnan(window).any():
240
- continue
 
241
 
242
- X_list.append(window.flatten())
243
- y_list.append(df.iloc[i]["label"])
244
 
245
- X = np.array(X_list)
246
- y = np.array(y_list)
247
 
248
- return X, y, df, feat_cols, is_real
249
 
250
  # ============================================================
251
- # TRAIN MODEL
 
 
252
  # ============================================================
253
 
254
- def run_train():
255
 
256
- try:
257
 
258
- X, y, df, feat_cols, is_real = fetch_and_prepare()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
- scaler = MinMaxScaler()
261
 
262
- X_scaled = scaler.fit_transform(X)
 
263
 
264
- split = int(len(X_scaled) * 0.88)
 
 
 
265
 
266
- X_train = X_scaled[:split]
267
- X_test = X_scaled[split:]
 
 
268
 
269
- y_train = y[:split]
270
- y_test = y[split:]
 
 
271
 
272
- model = RandomForestClassifier(
273
- n_estimators=100,
274
- max_depth=8,
275
- random_state=42,
276
- class_weight="balanced"
277
- )
278
 
279
- model.fit(X_train, y_train)
280
 
281
- acc = model.score(X_test, y_test)
282
 
283
- joblib.dump(model, MODEL_PATH)
284
- joblib.dump(scaler, SCALER_PATH)
285
 
286
- return f"""
287
- ✅ MODEL TRAINED
288
 
289
- Accuracy:
290
- {acc:.4f}
291
 
292
- Samples:
293
- {len(X)}
294
 
295
- Source:
296
- {"LIVE DATA" if is_real else "SIMULATED"}
297
- """
 
 
298
 
299
  except Exception as e:
300
 
301
- logger.error(traceback.format_exc())
302
 
303
- return str(e)
304
 
305
  # ============================================================
306
- # TP SL FIBONACCI
 
 
307
  # ============================================================
308
 
309
  def trade_levels(df, signal):
310
 
311
- close_price = float(df.iloc[-1]["close"])
312
 
313
- recent_high = float(df["high"].tail(20).max())
314
- recent_low = float(df["low"].tail(20).min())
315
 
316
- fib_range = recent_high - recent_low
317
 
318
- fib236 = recent_high - fib_range * 0.236
319
- fib382 = recent_high - fib_range * 0.382
320
- fib500 = recent_high - fib_range * 0.5
321
- fib618 = recent_high - fib_range * 0.618
322
 
323
- atr = (
324
- (df["high"] - df["low"])
325
- .rolling(14)
326
- .mean()
327
- .iloc[-1]
328
- )
329
 
330
- if signal == "BUY":
331
 
332
- tp1 = fib236
333
- tp2 = fib382
334
- tp3 = recent_high
335
 
336
- sl = close_price - atr * 1.5
 
 
337
 
338
- else:
339
 
340
- tp1 = fib618
341
- tp2 = fib500
342
- tp3 = recent_low
343
 
344
- sl = close_price + atr * 1.5
 
 
 
 
 
 
 
 
345
 
346
- return {
347
- "entry": close_price,
348
- "tp1": tp1,
349
- "tp2": tp2,
350
- "tp3": tp3,
351
- "sl": sl,
352
- "fib236": fib236,
353
- "fib382": fib382,
354
- "fib500": fib500,
355
- "fib618": fib618
356
- }
357
 
358
  # ============================================================
 
359
  # PREDICT
 
360
  # ============================================================
361
 
362
- def run_predict():
363
 
364
- try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
- if not os.path.exists(MODEL_PATH):
367
- return "⚠️ Train model terlebih dahulu"
 
368
 
369
- X, y, df, feat_cols, is_real = fetch_and_prepare()
 
 
370
 
371
- scaler = joblib.load(SCALER_PATH)
372
- model = joblib.load(MODEL_PATH)
 
373
 
374
- latest = df.iloc[
375
- -WINDOW:
376
- ][feat_cols].values.flatten()
377
 
378
- latest = latest.reshape(1, -1)
379
 
380
- latest_scaled = scaler.transform(latest)
 
 
 
 
381
 
382
- pred = model.predict(latest_scaled)[0]
 
 
 
 
383
 
384
- proba = model.predict_proba(
385
- latest_scaled
386
- )[0]
 
 
387
 
388
- confidence = float(np.max(proba)) * 100
 
 
389
 
390
- signal = "BUY" if pred == 1 else "SELL"
391
 
392
- levels = trade_levels(df, signal)
393
 
394
- return f"""
395
- ═══════════════════════
396
- 🤖 AI TRADING SIGNAL
397
- ═══════════════════════
398
 
399
- 🔮 Signal:
400
- {signal}
401
 
402
- 📊 Confidence:
403
- {confidence:.2f}%
404
 
405
- 💰 Entry:
406
- ${levels['entry']:,.2f}
407
 
408
- ━━━━━━━━━━━━━━━━━━━
 
409
 
410
- 🎯 TAKE PROFIT
411
 
412
- TP1:
413
- ${levels['tp1']:,.2f}
414
 
415
- TP2:
416
- ${levels['tp2']:,.2f}
417
 
418
- TP3:
419
- ${levels['tp3']:,.2f}
420
 
421
- ━━━━━━━━━━━━━━━━━━━
422
 
423
- 🛑 STOP LOSS
424
 
425
- ${levels['sl']:,.2f}
426
 
427
- ━━━━━━━━━━━━━━━━━━━
428
 
429
- 📐 FIBONACCI
 
 
 
430
 
431
- 0.236:
432
- ${levels['fib236']:,.2f}
433
 
434
- 0.382:
435
- ${levels['fib382']:,.2f}
436
 
437
- 0.500:
438
- ${levels['fib500']:,.2f}
439
 
440
- 0.618:
441
- ${levels['fib618']:,.2f}
442
 
443
- ━━━━━━━━━━━━━━━━━━━
 
444
 
445
- 📈 INDICATORS
446
 
447
- RSI:
448
- {df.iloc[-1]['rsi']:.2f}
449
 
450
- MACD:
451
- {df.iloc[-1]['macd']:.2f}
452
 
453
- ━━━━━━━━━━━━━━━━━━━
454
 
455
- 📡 SOURCE:
456
- {"LIVE DATA" if is_real else "SIMULATED"}
457
  """
 
458
 
459
- except Exception as e:
460
 
461
- logger.error(traceback.format_exc())
 
 
 
 
 
 
 
 
 
 
 
 
 
462
 
463
- return str(e)
464
 
465
  # ============================================================
466
- # MARKET INFO
 
 
467
  # ============================================================
468
 
469
- def get_market_info():
470
 
471
- try:
472
 
473
- df, is_real = safe_download()
474
 
475
- price = float(df["Close"].iloc[-1])
476
 
477
- high = float(df["High"].tail(24).max())
478
- low = float(df["Low"].tail(24).min())
479
 
480
- return f"""
481
- 💰 ETH PRICE:
482
- ${price:,.2f}
483
 
484
- 📈 24H HIGH:
485
- ${high:,.2f}
486
 
487
- 📉 24H LOW:
488
- ${low:,.2f}
 
489
 
490
- 📡 SOURCE:
491
- {"LIVE DATA" if is_real else "SIMULATED"}
492
- """
 
 
 
 
 
 
 
 
 
 
493
 
494
  except Exception as e:
495
- return str(e)
496
 
497
- # ============================================================
498
- # TRADINGVIEW
499
- # ============================================================
 
 
 
 
 
 
 
 
 
500
 
501
- TRADINGVIEW_HTML = """
502
- <div style="width:100%;height:600px;">
503
- <iframe
504
- src="https://s.tradingview.com/widgetembed/?symbol=BINANCE:ETHUSDT&interval=240&theme=dark&style=1"
505
- style="width:100%;height:100%;border:none;">
506
- </iframe>
507
- </div>
508
- """
509
 
510
  # ============================================================
 
511
  # UI
 
512
  # ============================================================
513
 
514
- with gr.Blocks(title="CryptoML PRO") as demo:
 
 
515
 
516
- gr.Markdown("# 🚀 CryptoML PRO")
517
 
518
- gr.HTML(TRADINGVIEW_HTML)
 
 
519
 
520
- market_box = gr.Textbox(
521
- label="Market Info",
522
- value=get_market_info(),
523
- lines=8
524
- )
525
 
526
- with gr.Row():
527
 
528
- train_btn = gr.Button(
529
- "⚡ Train Model"
530
- )
 
531
 
532
- predict_btn = gr.Button(
533
- "🔮 Predict Signal"
534
- )
535
 
536
- refresh_btn = gr.Button(
537
- "🔄 Refresh"
538
- )
539
 
540
- train_output = gr.Textbox(
541
- label="Training Result",
542
- lines=10
543
- )
544
 
545
- predict_output = gr.Textbox(
546
- label="Prediction",
547
- lines=30
548
- )
549
 
550
- train_btn.click(
551
- fn=run_train,
552
- outputs=train_output
553
- )
554
 
555
- predict_btn.click(
556
- fn=run_predict,
557
- outputs=predict_output
558
- )
559
 
560
- refresh_btn.click(
561
- fn=get_market_info,
562
- outputs=market_box
563
- )
564
 
565
  demo.queue()
566
 
 
1
  import os
2
  import time
 
3
  import traceback
4
+ import logging
5
  from datetime import datetime
6
 
7
  import gradio as gr
 
9
  import pandas as pd
10
  import numpy as np
11
  import joblib
12
+ import requests
13
 
 
14
  from sklearn.preprocessing import MinMaxScaler
15
+ from sklearn.model_selection import TimeSeriesSplit
16
 
17
+ from xgboost import XGBClassifier
 
 
18
 
19
+ from ta.trend import ADXIndicator
 
20
 
21
  # ============================================================
22
+
23
  # CONFIG
24
+
25
  # ============================================================
26
 
27
+ SYMBOLS = [
28
+ "BTC-USD",
29
+ "ETH-USD",
30
+ "SOL-USD",
31
+ "BNB-USD",
32
+ "XRP-USD",
33
+ "DOGE-USD",
34
+ "AVAX-USD",
35
+ "LINK-USD"
36
+ ]
37
+
38
  WINDOW = 20
 
39
 
40
+ MODEL_DIR = "models"
41
+
42
+ os.makedirs(MODEL_DIR, exist_ok=True)
43
+
44
+ # TELEGRAM
45
+
46
+ BOT_TOKEN = "YOUR_BOT_TOKEN"
47
+ CHAT_ID = "YOUR_CHAT_ID"
48
 
49
  # ============================================================
50
+
51
+ # LOGGING
52
+
53
  # ============================================================
54
 
55
+ logging.basicConfig(level=logging.INFO)
56
 
57
+ logger = logging.getLogger("crypto_ai")
 
58
 
59
+ # ============================================================
60
+
61
+ # PATH
62
 
63
+ # ============================================================
 
 
 
 
 
 
 
64
 
65
+ def model_path(symbol):
66
+
67
+
68
+ return os.path.join(
69
+ MODEL_DIR,
70
+ f"{symbol}_model.pkl"
71
+ )
72
+
73
+
74
+ def scaler_path(symbol):
75
+
76
+
77
+ return os.path.join(
78
+ MODEL_DIR,
79
+ f"{symbol}_scaler.pkl"
80
+ )
81
 
 
82
 
83
  # ============================================================
84
+
85
+ # TELEGRAM
86
+
87
  # ============================================================
88
 
89
+ def send_telegram(message):
90
+
91
 
92
+ try:
93
 
94
+ if "YOUR_BOT_TOKEN" in BOT_TOKEN:
95
+ return
96
+
97
+ url = (
98
+ f"https://api.telegram.org/"
99
+ f"bot{BOT_TOKEN}/sendMessage"
100
  )
101
 
102
+ requests.post(
103
+ url,
104
+ data={
105
+ "chat_id": CHAT_ID,
106
+ "text": message
107
+ },
108
+ timeout=10
109
+ )
110
 
111
+ except Exception as e:
112
+ logger.warning(e)
 
 
 
113
 
 
114
 
115
+ # ============================================================
116
 
117
+ # DOWNLOAD
 
118
 
119
+ # ============================================================
 
120
 
121
+ def clean_columns(df):
122
 
 
 
 
 
 
123
 
124
+ if isinstance(df.columns, pd.MultiIndex):
125
+ df.columns = df.columns.get_level_values(0)
126
 
127
+ df.columns = [str(c).lower() for c in df.columns]
 
 
 
 
 
 
128
 
129
+ return df
130
 
 
 
 
131
 
132
+ def safe_download(symbol):
133
 
 
134
 
135
+ try:
 
 
 
 
 
 
 
136
 
137
+ df = yf.download(
138
+ symbol,
139
+ period="90d",
140
+ interval="1h",
141
+ progress=False,
142
+ auto_adjust=True,
143
+ threads=False
144
+ )
145
 
146
+ if df is not None and not df.empty:
147
 
148
+ df = clean_columns(df)
149
 
150
+ return df, True
151
+
152
+ except Exception as e:
153
+
154
+ logger.warning(e)
155
+
156
+ return None, False
157
 
 
158
 
159
  # ============================================================
160
+
161
  # FEATURE ENGINEERING
162
+
163
  # ============================================================
164
 
165
+ def fetch_and_prepare(symbol):
166
+
167
+
168
+ df, is_real = safe_download(symbol)
169
+
170
+ if df is None:
171
+ return None, None, None, False
172
 
173
+ df = df.resample("4h").agg({
174
+ "open": "first",
175
+ "high": "max",
176
+ "low": "min",
177
+ "close": "last",
178
+ "volume": "sum"
179
+ }).dropna()
180
 
181
+ # RSI
 
182
 
183
+ delta = df["close"].diff()
 
 
 
 
 
 
184
 
185
+ gain = delta.where(
186
+ delta > 0,
187
+ 0
188
+ ).rolling(14).mean()
189
 
190
+ loss = (-delta.where(
191
+ delta < 0,
192
+ 0
193
+ )).rolling(14).mean()
194
 
195
+ rs = gain / loss.replace(0, np.nan)
196
 
197
+ df["rsi"] = (
198
+ 100 - (100 / (1 + rs))
199
+ ).fillna(50)
200
 
201
+ # MACD
202
 
203
+ ema12 = df["close"].ewm(span=12).mean()
204
+ ema26 = df["close"].ewm(span=26).mean()
205
+
206
+ df["macd"] = ema12 - ema26
207
+ df["macd_signal"] = (
208
+ df["macd"]
209
+ .ewm(span=9)
210
+ .mean()
211
+ )
212
+
213
+ # EMA
214
+
215
+ df["ema9"] = (
216
+ df["close"]
217
+ .ewm(span=9)
218
+ .mean()
219
+ )
220
 
221
+ df["ema21"] = (
222
+ df["close"]
223
+ .ewm(span=21)
224
+ .mean()
225
+ )
226
 
227
+ df["ema50"] = (
228
+ df["close"]
229
+ .ewm(span=50)
230
+ .mean()
231
+ )
232
 
233
+ df["ema200"] = (
234
+ df["close"]
235
+ .ewm(span=200)
236
+ .mean()
237
+ )
238
 
239
+ # BOLLINGER
240
 
241
+ sma20 = (
242
+ df["close"]
243
+ .rolling(20)
244
+ .mean()
245
+ )
246
 
247
+ std20 = (
248
+ df["close"]
249
+ .rolling(20)
250
+ .std()
251
+ )
252
 
253
+ df["bb_upper"] = sma20 + (2 * std20)
254
+ df["bb_lower"] = sma20 - (2 * std20)
255
 
256
+ # ATR
 
257
 
258
+ df["atr"] = (
259
+ (df["high"] - df["low"])
260
+ .rolling(14)
261
+ .mean()
262
+ )
263
 
264
+ # VOLUME
 
265
 
266
+ df["vol_sma"] = (
267
+ df["volume"]
268
+ .rolling(20)
269
+ .mean()
270
+ )
271
 
272
+ df["volume_spike"] = (
273
+ df["volume"]
274
+ >
275
+ df["vol_sma"] * 2
276
+ )
277
 
278
+ # ADX
 
 
 
279
 
280
+ adx = ADXIndicator(
281
+ high=df["high"],
282
+ low=df["low"],
283
+ close=df["close"]
284
+ )
285
 
286
+ df["adx"] = adx.adx()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
+ # TREND STRENGTH
 
289
 
290
+ df["trend_strength"] = (
291
+ abs(
292
+ df["ema50"]
293
+ -
294
+ df["ema200"]
295
+ )
296
+ /
297
+ df["close"]
298
+ )
299
+
300
+ # VOLATILITY
301
+
302
+ df["volatility"] = (
303
+ df["close"]
304
+ .pct_change()
305
+ .rolling(20)
306
+ .std()
307
+ )
308
+
309
+ # MARKET REGIME
310
+
311
+ def detect_regime(row):
312
+
313
+ if row["volatility"] < 0.01:
314
+ return "SIDEWAYS"
315
+
316
+ if row["ema50"] > row["ema200"]:
317
+ return "BULL"
318
+
319
+ return "BEAR"
320
+
321
+ df["regime"] = df.apply(
322
+ detect_regime,
323
+ axis=1
324
+ )
325
+
326
+ # RETURNS
327
+
328
+ df["return1"] = (
329
+ df["close"]
330
+ .pct_change()
331
+ )
332
+
333
+ df["return4"] = (
334
+ df["close"]
335
+ .pct_change(4)
336
+ )
337
+
338
+ # LABEL
339
+
340
+ df["next_close"] = (
341
+ df["close"]
342
+ .shift(-1)
343
+ )
344
+
345
+ df["label"] = (
346
+ df["next_close"]
347
+ >
348
+ df["close"]
349
+ ).astype(int)
350
+
351
+ df = df.replace(
352
+ [np.inf, -np.inf],
353
+ np.nan
354
+ )
355
 
356
+ df = df.dropna()
357
+
358
+ feat_cols = [
359
+ "open",
360
+ "high",
361
+ "low",
362
+ "close",
363
+ "volume",
364
+ "rsi",
365
+ "macd",
366
+ "macd_signal",
367
+ "ema9",
368
+ "ema21",
369
+ "ema50",
370
+ "ema200",
371
+ "bb_upper",
372
+ "bb_lower",
373
+ "atr",
374
+ "adx",
375
+ "trend_strength",
376
+ "return1",
377
+ "return4"
378
+ ]
379
+
380
+ X_list = []
381
+ y_list = []
382
+
383
+ for i in range(WINDOW, len(df)):
384
+
385
+ window = df.iloc[
386
+ i-WINDOW:i
387
+ ][feat_cols].values
388
+
389
+ X_list.append(
390
+ window.flatten()
391
+ )
392
 
393
+ y_list.append(
394
+ df.iloc[i]["label"]
395
+ )
396
 
397
+ X = np.array(X_list)
398
+ y = np.array(y_list)
399
 
400
+ return X, y, df, feat_cols, is_real
 
401
 
 
402
 
403
  # ============================================================
404
+
405
+ # TRAIN
406
+
407
  # ============================================================
408
 
409
+ def run_train(symbol):
410
 
 
411
 
412
+ X, y, df, feat_cols, is_real = (
413
+ fetch_and_prepare(symbol)
414
+ )
415
+
416
+ if X is None:
417
+ return
418
+
419
+ scaler = MinMaxScaler()
420
+
421
+ X_scaled = scaler.fit_transform(X)
422
+
423
+ model = XGBClassifier(
424
+ n_estimators=300,
425
+ max_depth=6,
426
+ learning_rate=0.03,
427
+ subsample=0.8,
428
+ colsample_bytree=0.8,
429
+ eval_metric="logloss",
430
+ random_state=42
431
+ )
432
+
433
+ tscv = TimeSeriesSplit(
434
+ n_splits=5
435
+ )
436
 
437
+ for train_idx, test_idx in tscv.split(X_scaled):
438
 
439
+ X_train = X_scaled[train_idx]
440
+ y_train = y[train_idx]
441
 
442
+ model.fit(
443
+ X_train,
444
+ y_train
445
+ )
446
 
447
+ joblib.dump(
448
+ model,
449
+ model_path(symbol)
450
+ )
451
 
452
+ joblib.dump(
453
+ scaler,
454
+ scaler_path(symbol)
455
+ )
456
 
457
+ logger.info(
458
+ f"TRAINED: {symbol}"
459
+ )
 
 
 
460
 
 
461
 
462
+ # ============================================================
463
 
464
+ # TRAIN ALL
 
465
 
466
+ # ============================================================
 
467
 
468
+ def train_all():
 
469
 
 
 
470
 
471
+ for symbol in SYMBOLS:
472
+
473
+ try:
474
+
475
+ run_train(symbol)
476
 
477
  except Exception as e:
478
 
479
+ logger.error(e)
480
 
 
481
 
482
  # ============================================================
483
+
484
+ # TRADE LEVELS
485
+
486
  # ============================================================
487
 
488
  def trade_levels(df, signal):
489
 
 
490
 
491
+ last = df.iloc[-1]
 
492
 
493
+ close_price = float(last["close"])
494
 
495
+ atr = float(last["atr"])
 
 
 
496
 
497
+ if signal == "BUY":
498
+
499
+ tp1 = close_price + atr
500
+ tp2 = close_price + atr * 2
501
+ tp3 = close_price + atr * 3
 
502
 
503
+ sl = close_price - atr * 1.5
504
 
505
+ elif signal == "SELL":
 
 
506
 
507
+ tp1 = close_price - atr
508
+ tp2 = close_price - atr * 2
509
+ tp3 = close_price - atr * 3
510
 
511
+ sl = close_price + atr * 1.5
512
 
513
+ else:
 
 
514
 
515
+ tp1 = tp2 = tp3 = sl = close_price
516
+
517
+ return {
518
+ "entry": close_price,
519
+ "tp1": tp1,
520
+ "tp2": tp2,
521
+ "tp3": tp3,
522
+ "sl": sl
523
+ }
524
 
 
 
 
 
 
 
 
 
 
 
 
525
 
526
  # ============================================================
527
+
528
  # PREDICT
529
+
530
  # ============================================================
531
 
532
+ LAST_SIGNALS = {}
533
 
534
+ def run_predict(symbol):
535
+
536
+
537
+ if not os.path.exists(
538
+ model_path(symbol)
539
+ ):
540
+
541
+ run_train(symbol)
542
+
543
+ X, y, df, feat_cols, is_real = (
544
+ fetch_and_prepare(symbol)
545
+ )
546
+
547
+ scaler = joblib.load(
548
+ scaler_path(symbol)
549
+ )
550
+
551
+ model = joblib.load(
552
+ model_path(symbol)
553
+ )
554
+
555
+ latest = (
556
+ df.iloc[-WINDOW:][feat_cols]
557
+ .values
558
+ .flatten()
559
+ .reshape(1, -1)
560
+ )
561
 
562
+ latest_scaled = scaler.transform(
563
+ latest
564
+ )
565
 
566
+ pred = model.predict(
567
+ latest_scaled
568
+ )[0]
569
 
570
+ proba = model.predict_proba(
571
+ latest_scaled
572
+ )[0]
573
 
574
+ confidence = float(
575
+ np.max(proba)
576
+ ) * 100
577
 
578
+ last = df.iloc[-1]
579
 
580
+ bull_trend = (
581
+ last["ema50"]
582
+ >
583
+ last["ema200"]
584
+ )
585
 
586
+ bear_trend = (
587
+ last["ema50"]
588
+ <
589
+ last["ema200"]
590
+ )
591
 
592
+ strong_volume = (
593
+ last["volume"]
594
+ >
595
+ last["vol_sma"]
596
+ )
597
 
598
+ strong_adx = (
599
+ last["adx"] > 25
600
+ )
601
 
602
+ regime = last["regime"]
603
 
604
+ score = 0
605
 
606
+ if bull_trend:
607
+ score += 20
 
 
608
 
609
+ if strong_volume:
610
+ score += 20
611
 
612
+ if strong_adx:
613
+ score += 20
614
 
615
+ if last["rsi"] > 55:
616
+ score += 20
617
 
618
+ if last["macd"] > last["macd_signal"]:
619
+ score += 20
620
 
621
+ signal = "NO TRADE"
622
 
623
+ if regime == "SIDEWAYS":
 
624
 
625
+ signal = "NO TRADE"
 
626
 
627
+ else:
 
628
 
629
+ if pred == 1 and score >= 60:
630
 
631
+ signal = "BUY"
632
 
633
+ elif pred == 0 and score >= 60:
634
 
635
+ signal = "SELL"
636
 
637
+ levels = trade_levels(
638
+ df,
639
+ signal
640
+ )
641
 
642
+ # TELEGRAM
 
643
 
644
+ previous = LAST_SIGNALS.get(symbol)
 
645
 
646
+ if previous != signal:
 
647
 
648
+ LAST_SIGNALS[symbol] = signal
 
649
 
650
+ send_telegram(
651
+ f"""
652
 
 
653
 
654
+ 🚨 NEW SIGNAL
 
655
 
656
+ PAIR: {symbol}
 
657
 
658
+ SIGNAL: {signal}
659
 
660
+ CONFIDENCE: {confidence:.2f}%
 
661
  """
662
+ )
663
 
 
664
 
665
+ return {
666
+ "Pair": symbol,
667
+ "Signal": signal,
668
+ "Confidence": round(confidence, 2),
669
+ "Score": score,
670
+ "Entry": round(levels["entry"], 2),
671
+ "TP1": round(levels["tp1"], 2),
672
+ "TP2": round(levels["tp2"], 2),
673
+ "TP3": round(levels["tp3"], 2),
674
+ "SL": round(levels["sl"], 2),
675
+ "RSI": round(last["rsi"], 2),
676
+ "ADX": round(last["adx"], 2),
677
+ "Regime": regime
678
+ }
679
 
 
680
 
681
  # ============================================================
682
+
683
+ # SCAN ALL
684
+
685
  # ============================================================
686
 
687
+ LAST_RETRAIN = 0
688
 
689
+ def realtime_scan():
690
 
 
691
 
692
+ global LAST_RETRAIN
693
 
694
+ now = time.time()
 
695
 
696
+ # AUTO RETRAIN
 
 
697
 
698
+ if now - LAST_RETRAIN > 21600:
 
699
 
700
+ logger.info(
701
+ "AUTO RETRAIN..."
702
+ )
703
 
704
+ train_all()
705
+
706
+ LAST_RETRAIN = now
707
+
708
+ rows = []
709
+
710
+ for symbol in SYMBOLS:
711
+
712
+ try:
713
+
714
+ result = run_predict(symbol)
715
+
716
+ rows.append(result)
717
 
718
  except Exception as e:
 
719
 
720
+ logger.error(traceback.format_exc())
721
+
722
+ df = pd.DataFrame(rows)
723
+
724
+ if len(df) > 0:
725
+
726
+ df = df.sort_values(
727
+ by="Score",
728
+ ascending=False
729
+ )
730
+
731
+ return df
732
 
 
 
 
 
 
 
 
 
733
 
734
  # ============================================================
735
+
736
  # UI
737
+
738
  # ============================================================
739
 
740
+ with gr.Blocks(
741
+ title="Crypto AI Scanner"
742
+ ) as demo:
743
 
 
744
 
745
+ gr.Markdown(
746
+ "# 🤖 Crypto AI Scanner PRO"
747
+ )
748
 
749
+ table = gr.Dataframe(
750
+ interactive=False
751
+ )
 
 
752
 
753
+ timer = gr.Timer(20)
754
 
755
+ timer.tick(
756
+ fn=realtime_scan,
757
+ outputs=table
758
+ )
759
 
 
 
 
760
 
761
+ # ============================================================
 
 
762
 
763
+ # START
 
 
 
764
 
765
+ # ============================================================
 
 
 
766
 
767
+ if **name** == "**main**":
 
 
 
768
 
 
 
 
 
769
 
770
+ train_all()
 
 
 
771
 
772
  demo.queue()
773