daafa999 commited on
Commit
2baa9f2
ยท
verified ยท
1 Parent(s): 953a727

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -437
app.py CHANGED
@@ -1,73 +1,48 @@
1
- import sys
2
  import os
 
3
  import logging
4
  import traceback
5
- import time
6
- import random
7
- from datetime import datetime, timedelta
8
 
9
- logging.basicConfig(
10
- stream=sys.stdout,
11
- level=logging.INFO,
12
- format="%(asctime)s [%(levelname)s] %(message)s"
13
- )
14
 
15
- logger = logging.getLogger("crypto_ml")
16
- logger.info("๐Ÿš€ Starting HF Space container...")
17
 
18
  # ============================================================
19
- # IMPORTS
20
  # ============================================================
21
 
22
- try:
23
- import gradio as gr
24
- import yfinance as yf
25
- import pandas as pd
26
- import numpy as np
27
-
28
- from sklearn.ensemble import RandomForestClassifier
29
- from sklearn.preprocessing import MinMaxScaler
30
-
31
- import joblib
32
-
33
- logger.info("โœ… All dependencies imported successfully")
34
-
35
- except Exception as e:
36
- logger.error(f"โŒ Import failed: {e}")
37
- sys.exit(1)
38
 
39
  # ============================================================
40
  # CONFIG
41
  # ============================================================
42
 
43
  SYMBOL = "ETH-USD"
44
- FEE = 0.0015
45
  WINDOW = 20
 
46
 
47
- MODEL_PATH = "/tmp/rf_model.pkl"
48
  SCALER_PATH = "/tmp/scaler.pkl"
49
 
50
  # ============================================================
51
- # CLEAN COLUMNS
52
  # ============================================================
53
 
54
- def _clean_columns(df):
55
-
56
- if df is None or df.empty:
57
- return df
58
 
59
  if isinstance(df.columns, pd.MultiIndex):
60
- try:
61
- df.columns = df.columns.get_level_values(0)
62
- except:
63
- df.columns = [
64
- c[0] if isinstance(c, tuple) else c
65
- for c in df.columns
66
- ]
67
 
68
  df.columns = [str(c).strip() for c in df.columns]
69
 
70
- rename_map = {
71
  "open": "Open",
72
  "high": "High",
73
  "low": "Low",
@@ -77,36 +52,23 @@ def _clean_columns(df):
77
  }
78
 
79
  df.columns = [
80
- rename_map.get(c.lower(), c)
81
  for c in df.columns
82
  ]
83
 
84
- df = df.loc[:, ~df.columns.duplicated()]
85
-
86
- for col in ["Open", "High", "Low", "Close", "Volume"]:
87
- if col in df.columns:
88
- df[col] = pd.to_numeric(df[col], errors="coerce")
89
-
90
- df = df.dropna()
91
-
92
- logger.info(f"โœ… Cleaned columns: {df.columns.tolist()}")
93
-
94
  return df
95
 
96
  # ============================================================
97
  # SYNTHETIC DATA
98
  # ============================================================
99
 
100
- def generate_synthetic_data(n_bars=500):
101
-
102
- logger.warning("โš ๏ธ Using synthetic data")
103
 
104
  np.random.seed(42)
105
 
106
- end = datetime.utcnow()
107
  dates = pd.date_range(
108
- end=end,
109
- periods=n_bars,
110
  freq="4h"
111
  )
112
 
@@ -118,22 +80,17 @@ def generate_synthetic_data(n_bars=500):
118
  closes = []
119
  volumes = []
120
 
121
- for _ in range(n_bars):
122
 
123
- ret = np.random.normal(0.0005, 0.015)
124
 
125
  open_p = price
126
  close_p = price * (1 + ret)
127
 
128
- high_p = max(open_p, close_p) * (
129
- 1 + abs(np.random.normal(0, 0.005))
130
- )
131
-
132
- low_p = min(open_p, close_p) * (
133
- 1 - abs(np.random.normal(0, 0.005))
134
- )
135
 
136
- vol = np.random.lognormal(15, 1)
137
 
138
  opens.append(open_p)
139
  highs.append(high_p)
@@ -157,56 +114,29 @@ def generate_synthetic_data(n_bars=500):
157
  # DOWNLOAD DATA
158
  # ============================================================
159
 
160
- def safe_download(
161
- symbol=SYMBOL,
162
- period="90d",
163
- interval="1h"
164
- ):
165
-
166
- methods = [
167
- ("Ticker.history", lambda:
168
- yf.Ticker(symbol).history(
169
- period=period,
170
- interval=interval,
171
- auto_adjust=True
172
- )
173
- ),
174
-
175
- ("yf.download", lambda:
176
- yf.download(
177
- tickers=symbol,
178
- period=period,
179
- interval=interval,
180
- progress=False,
181
- auto_adjust=True,
182
- threads=False,
183
- group_by="column"
184
- )
185
- )
186
- ]
187
-
188
- for name, method in methods:
189
-
190
- try:
191
 
192
- logger.info(f"๐Ÿ“ฅ Trying {name}")
193
-
194
- df = method()
195
 
196
- if df is not None and not df.empty:
 
 
 
 
 
 
 
197
 
198
- df = _clean_columns(df)
199
 
200
- if len(df) > 30:
201
- logger.info(f"โœ… {name} success")
202
- return df, True
203
 
204
- except Exception as e:
205
- logger.warning(f"โš ๏ธ {name} failed: {e}")
206
 
207
- time.sleep(1)
 
208
 
209
- return generate_synthetic_data(), False
210
 
211
  # ============================================================
212
  # FEATURE ENGINEERING
@@ -216,33 +146,20 @@ def fetch_and_prepare():
216
 
217
  df, is_real = safe_download()
218
 
219
- if df.empty:
220
- raise ValueError("Data kosong")
221
-
222
  if not isinstance(df.index, pd.DatetimeIndex):
223
  df.index = pd.to_datetime(df.index)
224
 
225
- try:
226
-
227
- df = df.resample("4h").agg({
228
- "Open": "first",
229
- "High": "max",
230
- "Low": "min",
231
- "Close": "last",
232
- "Volume": "sum"
233
- }).dropna()
234
-
235
- except Exception as e:
236
- logger.warning(f"Resample failed: {e}")
237
-
238
- if len(df) < 50:
239
- raise ValueError("Data terlalu sedikit")
240
 
241
  df.columns = [c.lower() for c in df.columns]
242
 
243
- # ========================================================
244
  # RSI
245
- # ========================================================
246
 
247
  delta = df["close"].diff()
248
 
@@ -255,63 +172,33 @@ def fetch_and_prepare():
255
  100 - (100 / (1 + rs))
256
  ).fillna(50)
257
 
258
- # ========================================================
259
- # BOLLINGER
260
- # ========================================================
261
-
262
- sma20 = df["close"].rolling(20).mean()
263
- std20 = df["close"].rolling(20).std()
264
-
265
- df["bb_upper"] = sma20 + 2 * std20
266
- df["bb_lower"] = sma20 - 2 * std20
267
-
268
- bb_width = (
269
- df["bb_upper"] - df["bb_lower"]
270
- ).replace(0, np.nan)
271
-
272
- df["bb_pct"] = (
273
- (df["close"] - df["bb_lower"]) / bb_width
274
- ).fillna(0.5)
275
-
276
- # ========================================================
277
  # MACD
278
- # ========================================================
279
 
280
  ema12 = df["close"].ewm(span=12).mean()
281
  ema26 = df["close"].ewm(span=26).mean()
282
 
283
  df["macd"] = ema12 - ema26
284
  df["macd_signal"] = df["macd"].ewm(span=9).mean()
285
- df["macd_hist"] = (
286
- df["macd"] - df["macd_signal"]
287
- )
288
 
289
- # ========================================================
290
- # EMA
291
- # ========================================================
292
 
293
- df["ema_9"] = df["close"].ewm(span=9).mean()
294
- df["ema_21"] = df["close"].ewm(span=21).mean()
295
 
296
- # ========================================================
297
- # RETURNS
298
- # ========================================================
299
 
300
- df["vol_change"] = (
301
- df["volume"].pct_change()
302
- )
303
 
304
- df["return_1"] = (
305
- df["close"].pct_change(1)
306
- )
307
 
308
- df["return_4"] = (
309
- df["close"].pct_change(4)
310
- )
 
311
 
312
- # ========================================================
313
- # LABEL
314
- # ========================================================
315
 
316
  df["next_close"] = df["close"].shift(-1)
317
 
@@ -320,10 +207,6 @@ def fetch_and_prepare():
320
  df["close"] * (1 + FEE)
321
  ).astype(int)
322
 
323
- # ========================================================
324
- # CLEANUP
325
- # ========================================================
326
-
327
  df = df.replace([np.inf, -np.inf], np.nan)
328
  df = df.dropna()
329
 
@@ -334,17 +217,14 @@ def fetch_and_prepare():
334
  "close",
335
  "volume",
336
  "rsi",
337
- "bb_upper",
338
- "bb_lower",
339
- "bb_pct",
340
  "macd",
341
  "macd_signal",
342
- "macd_hist",
343
- "ema_9",
344
- "ema_21",
345
- "vol_change",
346
- "return_1",
347
- "return_4"
348
  ]
349
 
350
  X_list = []
@@ -352,25 +232,23 @@ def fetch_and_prepare():
352
 
353
  for i in range(WINDOW, len(df)):
354
 
355
- window_data = df.iloc[
356
  i-WINDOW:i
357
  ][feat_cols].values
358
 
359
- if np.isnan(window_data).any():
360
  continue
361
 
362
- X_list.append(window_data.flatten())
363
  y_list.append(df.iloc[i]["label"])
364
 
365
- X = np.array(X_list, dtype=np.float32)
366
  y = np.array(y_list)
367
 
368
- X = np.nan_to_num(X)
369
-
370
  return X, y, df, feat_cols, is_real
371
 
372
  # ============================================================
373
- # TRAIN
374
  # ============================================================
375
 
376
  def run_train():
@@ -395,79 +273,52 @@ def run_train():
395
  n_estimators=100,
396
  max_depth=8,
397
  random_state=42,
398
- class_weight="balanced",
399
- n_jobs=1
400
  )
401
 
402
  model.fit(X_train, y_train)
403
 
404
  acc = model.score(X_test, y_test)
405
 
406
- # ====================================================
407
- # FIX FEATURE IMPORTANCE
408
- # ====================================================
409
-
410
- importances = model.feature_importances_
411
-
412
- imp_matrix = importances.reshape(
413
- WINDOW,
414
- len(feat_cols)
415
- )
416
-
417
- mean_importance = imp_matrix.mean(axis=0)
418
-
419
- top_idx = np.argsort(mean_importance)[-5:][::-1]
420
-
421
- top_features = [
422
- (
423
- feat_cols[i],
424
- float(mean_importance[i])
425
- )
426
- for i in top_idx
427
- ]
428
-
429
  joblib.dump(model, MODEL_PATH)
430
  joblib.dump(scaler, SCALER_PATH)
431
 
432
- txt = f"""
433
  โœ… MODEL TRAINED
434
 
435
- Accuracy: {acc:.4f}
 
436
 
437
- Top Features:
 
438
 
 
 
439
  """
440
 
441
- for name, score in top_features:
442
- txt += f"\n{name}: {score:.4f}"
443
-
444
- return txt
445
-
446
  except Exception as e:
447
 
448
  logger.error(traceback.format_exc())
449
 
450
- return f"โŒ ERROR:\n\n{e}"
451
 
452
  # ============================================================
453
- # ADD THIS AFTER run_predict()
454
  # ============================================================
455
 
456
- def calculate_trade_levels(df, signal):
457
-
458
- latest = df.iloc[-1]
459
 
460
- close_price = float(latest["close"])
461
 
462
  recent_high = float(df["high"].tail(20).max())
463
  recent_low = float(df["low"].tail(20).min())
464
 
465
  fib_range = recent_high - recent_low
466
 
467
- fib_236 = recent_high - fib_range * 0.236
468
- fib_382 = recent_high - fib_range * 0.382
469
- fib_500 = recent_high - fib_range * 0.5
470
- fib_618 = recent_high - fib_range * 0.618
471
 
472
  atr = (
473
  (df["high"] - df["low"])
@@ -478,39 +329,34 @@ def calculate_trade_levels(df, signal):
478
 
479
  if signal == "BUY":
480
 
481
- entry = close_price
482
-
483
- tp1 = fib_236
484
- tp2 = fib_382
485
  tp3 = recent_high
486
 
487
- sl = close_price - (atr * 1.5)
488
 
489
  else:
490
 
491
- entry = close_price
492
-
493
- tp1 = fib_618
494
- tp2 = fib_500
495
  tp3 = recent_low
496
 
497
- sl = close_price + (atr * 1.5)
498
 
499
  return {
500
- "entry": entry,
501
  "tp1": tp1,
502
  "tp2": tp2,
503
  "tp3": tp3,
504
  "sl": sl,
505
- "fib_236": fib_236,
506
- "fib_382": fib_382,
507
- "fib_500": fib_500,
508
- "fib_618": fib_618
509
  }
510
 
511
-
512
  # ============================================================
513
- # REPLACE run_predict()
514
  # ============================================================
515
 
516
  def run_predict():
@@ -531,8 +377,6 @@ def run_predict():
531
 
532
  latest = latest.reshape(1, -1)
533
 
534
- latest = np.nan_to_num(latest)
535
-
536
  latest_scaled = scaler.transform(latest)
537
 
538
  pred = model.predict(latest_scaled)[0]
@@ -545,198 +389,78 @@ def run_predict():
545
 
546
  signal = "BUY" if pred == 1 else "SELL"
547
 
548
- levels = calculate_trade_levels(df, signal)
549
-
550
- price = float(df.iloc[-1]["close"])
551
-
552
- rsi = float(df.iloc[-1]["rsi"])
553
- macd = float(df.iloc[-1]["macd"])
554
 
555
  return f"""
556
- โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
557
- AI SIGNAL
558
- โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
559
-
560
- ๐Ÿ”ฎ Signal : {signal}
561
- ๐Ÿ“Š Confidence : {confidence:.2f}%
562
-
563
- ๐Ÿ’ฐ Current Price : ${price:,.2f}
564
-
565
- โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
566
- ๐Ÿ“Œ ENTRY / TP / SL
567
- โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
568
-
569
- ๐ŸŽฏ Entry : ${levels['entry']:,.2f}
570
-
571
- โœ… TP1 : ${levels['tp1']:,.2f}
572
- โœ… TP2 : ${levels['tp2']:,.2f}
573
- โœ… TP3 : ${levels['tp3']:,.2f}
574
-
575
- ๐Ÿ›‘ Stop Loss : ${levels['sl']:,.2f}
576
-
577
- โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
578
- ๐Ÿ“ Fibonacci Levels
579
- โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
580
-
581
- Fib 0.236 : ${levels['fib_236']:,.2f}
582
- Fib 0.382 : ${levels['fib_382']:,.2f}
583
- Fib 0.500 : ${levels['fib_500']:,.2f}
584
- Fib 0.618 : ${levels['fib_618']:,.2f}
585
-
586
- โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
587
- ๐Ÿ“ˆ Indicators
588
- โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
589
-
590
- RSI : {rsi:.2f}
591
- MACD : {macd:.2f}
592
-
593
- ๐Ÿ“ก Source:
594
- {"LIVE DATA" if is_real else "SIMULATED"}
595
- """
596
-
597
- except Exception as e:
598
-
599
- logger.error(traceback.format_exc())
600
-
601
- return f"โŒ ERROR:\n\n{e}"
602
-
603
-
604
- # ============================================================
605
- # ADD THIS BEFORE UI
606
- # ============================================================
607
-
608
- TRADINGVIEW_HTML = """
609
- <div style="width:100%;height:600px;border-radius:12px;overflow:hidden;
610
- border:1px solid #2B3139;background:#181A20;">
611
-
612
- <iframe
613
- src="https://s.tradingview.com/widgetembed/?symbol=BINANCE:ETHUSDT&interval=240&theme=dark&style=1&toolbarbg=%23181A20&hide_top_toolbar=0&saveimage=1&studies=%5B%22RSI%40tv-basicstudies%22,%22MACD%40tv-basicstudies%22,%22BB%40tv-basicstudies%22%5D"
614
- style="width:100%;height:100%;border:none;"
615
- allowfullscreen>
616
- </iframe>
617
-
618
- </div>
619
- """
620
-
621
-
622
- # ============================================================
623
- # REPLACE UI SECTION
624
- # ============================================================
625
-
626
- with gr.Blocks(
627
- title="CryptoML PRO"
628
- ) as demo:
629
-
630
- gr.Markdown("""
631
- # ๐Ÿš€ CryptoML PRO Terminal
632
-
633
- ### ETH/USDT AI Trading System
634
- """)
635
-
636
- gr.HTML(TRADINGVIEW_HTML)
637
-
638
- market_box = gr.Textbox(
639
- label="Market Info",
640
- value=get_market_info(),
641
- lines=5
642
- )
643
-
644
- with gr.Row():
645
 
646
- train_btn = gr.Button(
647
- "โšก Train AI Model",
648
- variant="primary"
649
- )
650
 
651
- predict_btn = gr.Button(
652
- "๐Ÿ”ฎ Generate Signal",
653
- variant="secondary"
654
- )
655
-
656
- refresh_btn = gr.Button(
657
- "๐Ÿ”„ Refresh Market",
658
- variant="secondary"
659
- )
660
 
661
- train_output = gr.Textbox(
662
- label="AI Training Result",
663
- lines=15
664
- )
665
-
666
- predict_output = gr.Textbox(
667
- label="AI Trading Signal",
668
- lines=30
669
- )
670
 
671
- train_btn.click(
672
- fn=run_train,
673
- outputs=train_output
674
- )
675
-
676
- predict_btn.click(
677
- fn=run_predict,
678
- outputs=predict_output
679
- )
680
-
681
- refresh_btn.click(
682
- fn=get_market_info,
683
- outputs=market_box
684
- )
685
 
686
- # ============================================================
687
- # PREDICT
688
- # ============================================================
689
 
690
- def run_predict():
 
691
 
692
- try:
 
693
 
694
- if not os.path.exists(MODEL_PATH):
695
- return "โš ๏ธ Train model terlebih dahulu"
696
 
697
- X, y, df, feat_cols, is_real = fetch_and_prepare()
698
 
699
- scaler = joblib.load(SCALER_PATH)
700
- model = joblib.load(MODEL_PATH)
701
 
702
- latest = df.iloc[
703
- -WINDOW:
704
- ][feat_cols].values.flatten()
705
 
706
- latest = latest.reshape(1, -1)
707
 
708
- latest = np.nan_to_num(latest)
709
 
710
- latest_scaled = scaler.transform(latest)
 
711
 
712
- pred = model.predict(latest_scaled)[0]
 
713
 
714
- proba = model.predict_proba(
715
- latest_scaled
716
- )[0]
717
 
718
- confidence = float(np.max(proba)) * 100
 
719
 
720
- signal = "BUY" if pred == 1 else "SELL"
721
 
722
- price = float(df.iloc[-1]["close"])
723
 
724
- return f"""
725
- ๐Ÿ”ฎ SIGNAL: {signal}
726
 
727
- Confidence: {confidence:.2f}%
 
728
 
729
- Price: ${price:,.2f}
730
 
731
- RSI: {df.iloc[-1]['rsi']:.2f}
732
- MACD: {df.iloc[-1]['macd']:.2f}
733
  """
734
 
735
  except Exception as e:
736
 
737
  logger.error(traceback.format_exc())
738
 
739
- return f"โŒ ERROR:\n\n{e}"
740
 
741
  # ============================================================
742
  # MARKET INFO
@@ -746,38 +470,57 @@ def get_market_info():
746
 
747
  try:
748
 
749
- df, is_real = safe_download(
750
- SYMBOL,
751
- period="5d",
752
- interval="1h"
753
- )
754
 
755
  price = float(df["Close"].iloc[-1])
756
 
757
- return f"""
758
- ETH PRICE
759
 
 
 
760
  ${price:,.2f}
761
 
762
- Source:
 
 
 
 
 
 
763
  {"LIVE DATA" if is_real else "SIMULATED"}
764
  """
765
 
766
  except Exception as e:
767
  return str(e)
768
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
  # ============================================================
770
  # UI
771
  # ============================================================
772
 
773
- with gr.Blocks(title="CryptoML") as demo:
 
 
774
 
775
- gr.Markdown("# ๐Ÿš€ CryptoML ETH/USDT")
776
 
777
  market_box = gr.Textbox(
778
- label="Market",
779
  value=get_market_info(),
780
- lines=5
781
  )
782
 
783
  with gr.Row():
@@ -787,7 +530,7 @@ with gr.Blocks(title="CryptoML") as demo:
787
  )
788
 
789
  predict_btn = gr.Button(
790
- "๐Ÿ”ฎ Predict"
791
  )
792
 
793
  refresh_btn = gr.Button(
@@ -796,12 +539,12 @@ with gr.Blocks(title="CryptoML") as demo:
796
 
797
  train_output = gr.Textbox(
798
  label="Training Result",
799
- lines=15
800
  )
801
 
802
  predict_output = gr.Textbox(
803
  label="Prediction",
804
- lines=10
805
  )
806
 
807
  train_btn.click(
@@ -819,11 +562,10 @@ with gr.Blocks(title="CryptoML") as demo:
819
  outputs=market_box
820
  )
821
 
822
- demo.queue(default_concurrency_limit=1)
823
-
824
- logger.info("โœ… App ready")
825
 
826
  demo.launch(
827
  server_name="0.0.0.0",
828
  server_port=7860
829
  )
 
 
 
1
  import os
2
+ import time
3
  import logging
4
  import traceback
5
+ from datetime import datetime
 
 
6
 
7
+ import gradio as gr
8
+ 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",
 
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
 
 
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)
 
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
 
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
 
 
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
 
 
207
  df["close"] * (1 + FEE)
208
  ).astype(int)
209
 
 
 
 
 
210
  df = df.replace([np.inf, -np.inf], np.nan)
211
  df = df.dropna()
212
 
 
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 = []
 
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():
 
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"])
 
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():
 
377
 
378
  latest = latest.reshape(1, -1)
379
 
 
 
380
  latest_scaled = scaler.transform(latest)
381
 
382
  pred = model.predict(latest_scaled)[0]
 
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
 
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():
 
530
  )
531
 
532
  predict_btn = gr.Button(
533
+ "๐Ÿ”ฎ Predict Signal"
534
  )
535
 
536
  refresh_btn = gr.Button(
 
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(
 
562
  outputs=market_box
563
  )
564
 
565
+ demo.queue()
 
 
566
 
567
  demo.launch(
568
  server_name="0.0.0.0",
569
  server_port=7860
570
  )
571
+