daafa999 commited on
Commit
a6e25df
Β·
verified Β·
1 Parent(s): 341e959

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -35
app.py CHANGED
@@ -3,12 +3,10 @@ import os
3
  import logging
4
  import time
5
 
6
- # πŸ”§ Force log output to HF container
7
  logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
8
  logger = logging.getLogger("crypto_ml")
9
  logger.info("πŸš€ Starting HF Space container...")
10
 
11
- # 1. IMPORTS (Wrapped for safe logging)
12
  try:
13
  import gradio as gr
14
  import yfinance as yf
@@ -17,48 +15,71 @@ try:
17
  from sklearn.ensemble import RandomForestClassifier
18
  from sklearn.preprocessing import MinMaxScaler
19
  import joblib
20
- logger.info("βœ… All dependencies imported successfully")
21
  except Exception as e:
22
  logger.error(f"❌ Import failed: {e}")
23
  sys.exit(1)
24
 
25
- # 2. CONFIG
26
  SYMBOL = "ETH-USD"
27
  FEE = 0.0015
28
  WINDOW = 60
29
  MODEL_PATH = "/tmp/rf_model.pkl"
30
  SCALER_PATH = "/tmp/scaler.pkl"
31
 
32
- # 3. DATA & MODEL FUNCTIONS
33
  def fetch_and_prepare():
34
  logger.info("πŸ“₯ Fetching ETH-USD from Yahoo Finance...")
35
  df = yf.download(SYMBOL, period="60d", interval="1h", progress=False)
36
- df = df.resample("4h").agg({"Open":"first","High":"max","Low":"min","Close":"last","Volume":"sum"}).dropna()
37
- df.columns = ["open","high","low","close","volume"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  df.index.name = "timestamp"
39
  df = df.reset_index()
40
-
41
- # Indicators (Manual)
42
  delta = df["close"].diff()
43
- gain = (delta.where(delta>0,0)).rolling(14).mean()
44
- loss = (-delta.where(delta<0,0)).rolling(14).mean()
45
- df["rsi"] = 100 - (100/(1+gain/loss))
46
-
47
  sma = df["close"].rolling(20).mean()
48
  std = df["close"].rolling(20).std()
49
- df["bb_upper"] = sma + (std*2)
50
- df["bb_lower"] = sma - (std*2)
51
-
 
52
  df["next_close"] = df["close"].shift(-1)
53
- df["label"] = (df["next_close"] > df["close"]*(1+FEE)).astype(int)
54
  df = df.dropna()
55
-
56
- feat_cols = ["open","high","low","close","volume","rsi","bb_upper","bb_lower"]
57
  X_list, y_list = [], []
58
  for i in range(WINDOW, len(df)):
59
  X_list.append(df.iloc[i-WINDOW:i][feat_cols].values.flatten())
60
  y_list.append(df.iloc[i]["label"])
61
-
62
  return np.array(X_list), np.array(y_list), df, feat_cols
63
 
64
  def run_train():
@@ -68,7 +89,7 @@ def run_train():
68
  scaler = MinMaxScaler()
69
  X_scaled = scaler.fit_transform(X)
70
 
71
- split = int(len(X)*0.95)
72
  model = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42, n_jobs=-1)
73
  model.fit(X_scaled[:split], y[:split])
74
 
@@ -77,10 +98,10 @@ def run_train():
77
  joblib.dump(scaler, SCALER_PATH)
78
 
79
  logger.info(f"βœ… Training complete. Accuracy: {acc:.4f}")
80
- return {"status": "βœ… Success", "accuracy": round(acc,4), "message": "Model saved to /tmp/"}
81
  except Exception as e:
82
  logger.error(f"❌ Train failed: {e}")
83
- return {"status": "❌ Error", "message": str(e)}
84
 
85
  def run_predict():
86
  try:
@@ -89,20 +110,24 @@ def run_predict():
89
 
90
  X, y, df_full, feat_cols = fetch_and_prepare()
91
  scaler = joblib.load(SCALER_PATH)
92
- latest = df_full.iloc[-WINDOW:][feat_cols].values.flatten().reshape(1,-1)
93
  latest_scaled = scaler.transform(latest)
94
 
95
  model = joblib.load(MODEL_PATH)
96
  pred = int(model.predict(latest_scaled)[0])
97
  prob = float(model.predict_proba(latest_scaled)[0][1])
98
 
99
- return {"status": "βœ… Prediction", "signal": "BUY" if pred==1 else "HOLD", "confidence": f"{prob*100:.1f}%"}
 
 
 
 
 
100
  except Exception as e:
101
  logger.error(f"❌ Predict failed: {e}")
102
- return {"status": "❌ Error", "message": str(e)}
103
 
104
- # 4. GRADIO UI
105
- logger.info("🎨 Building Gradio UI...")
106
  tradingview_html = """
107
  <div style="width:100%;height:400px;margin:10px 0;">
108
  <iframe src="https://s.tradingview.com/widgetembed/?symbol=BINANCE%3AETHUSDT&interval=240&hidesidetoolbar=1&symboledit=1&saveimage=1&toolbarbg=transparent&studies=%5B%5D&theme=dark&style=1&timezone=UTC&withdateranges=1&showpopupbutton=1&studies_overrides=%7B%7D&overrides=%7B%7D&enabled_features=%5B%5D&disabled_features=%5B%5D&showpopupbutton=1&locale=en"
@@ -124,12 +149,7 @@ with gr.Blocks(title="Crypto ML Strategy") as demo:
124
 
125
  train_btn.click(run_train, outputs=train_out)
126
  pred_btn.click(run_predict, outputs=pred_out)
127
- gr.Markdown("πŸ’‘ *Model disimpan di `/tmp/`. Retrain otomatis jika Space restart.*")
128
 
129
- # 5. QUEUE & LAUNCH
130
  demo.queue(default_concurrency_limit=1)
131
- logger.info("βœ… Gradio app built & queued. Ready for HF Spaces runner.")
132
-
133
- # Fallback untuk local testing, diabaikan di HF
134
- if __name__ == "__main__":
135
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
3
  import logging
4
  import time
5
 
 
6
  logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
7
  logger = logging.getLogger("crypto_ml")
8
  logger.info("πŸš€ Starting HF Space container...")
9
 
 
10
  try:
11
  import gradio as gr
12
  import yfinance as yf
 
15
  from sklearn.ensemble import RandomForestClassifier
16
  from sklearn.preprocessing import MinMaxScaler
17
  import joblib
18
+ logger.info("βœ… Dependencies loaded")
19
  except Exception as e:
20
  logger.error(f"❌ Import failed: {e}")
21
  sys.exit(1)
22
 
 
23
  SYMBOL = "ETH-USD"
24
  FEE = 0.0015
25
  WINDOW = 60
26
  MODEL_PATH = "/tmp/rf_model.pkl"
27
  SCALER_PATH = "/tmp/scaler.pkl"
28
 
 
29
  def fetch_and_prepare():
30
  logger.info("πŸ“₯ Fetching ETH-USD from Yahoo Finance...")
31
  df = yf.download(SYMBOL, period="60d", interval="1h", progress=False)
32
+ if df.empty:
33
+ raise ValueError("yfinance returned empty data")
34
+
35
+ # πŸ”§ FIX: Handle yfinance MultiIndex columns (v0.2.30+)
36
+ if isinstance(df.columns, pd.MultiIndex):
37
+ df.columns = df.columns.get_level_values(1)
38
+
39
+ # Map Adj Close -> Close if present
40
+ if 'Adj Close' in df.columns:
41
+ df.rename(columns={'Adj Close': 'Close'}, inplace=True)
42
+
43
+ # Standardize to lowercase to avoid case-sensitivity issues
44
+ df.columns = [col.lower() for col in df.columns]
45
+
46
+ # Verify required columns
47
+ required = ['open', 'high', 'low', 'close', 'volume']
48
+ missing = [c for c in required if c not in df.columns]
49
+ if missing:
50
+ raise ValueError(f"Missing columns: {missing}. Found: {list(df.columns)}")
51
+
52
+ # Resample to 4h candles
53
+ df = df.resample("4h").agg({
54
+ "open": "first", "high": "max", "low": "min",
55
+ "close": "last", "volume": "sum"
56
+ }).dropna()
57
+
58
  df.index.name = "timestamp"
59
  df = df.reset_index()
60
+
61
+ # πŸ“Š Technical Indicators
62
  delta = df["close"].diff()
63
+ gain = (delta.where(delta > 0, 0)).rolling(14).mean()
64
+ loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
65
+ df["rsi"] = 100 - (100 / (1 + gain / loss))
66
+
67
  sma = df["close"].rolling(20).mean()
68
  std = df["close"].rolling(20).std()
69
+ df["bb_upper"] = sma + (std * 2)
70
+ df["bb_lower"] = sma - (std * 2)
71
+
72
+ # Labels
73
  df["next_close"] = df["close"].shift(-1)
74
+ df["label"] = (df["next_close"] > df["close"] * (1 + FEE)).astype(int)
75
  df = df.dropna()
76
+
77
+ feat_cols = ["open", "high", "low", "close", "volume", "rsi", "bb_upper", "bb_lower"]
78
  X_list, y_list = [], []
79
  for i in range(WINDOW, len(df)):
80
  X_list.append(df.iloc[i-WINDOW:i][feat_cols].values.flatten())
81
  y_list.append(df.iloc[i]["label"])
82
+
83
  return np.array(X_list), np.array(y_list), df, feat_cols
84
 
85
  def run_train():
 
89
  scaler = MinMaxScaler()
90
  X_scaled = scaler.fit_transform(X)
91
 
92
+ split = int(len(X) * 0.95)
93
  model = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=42, n_jobs=-1)
94
  model.fit(X_scaled[:split], y[:split])
95
 
 
98
  joblib.dump(scaler, SCALER_PATH)
99
 
100
  logger.info(f"βœ… Training complete. Accuracy: {acc:.4f}")
101
+ return {"status": "βœ… Success", "accuracy": round(acc, 4), "message": "Model saved."}
102
  except Exception as e:
103
  logger.error(f"❌ Train failed: {e}")
104
+ return {"status": "❌ Error", "message": str(e)[:150]}
105
 
106
  def run_predict():
107
  try:
 
110
 
111
  X, y, df_full, feat_cols = fetch_and_prepare()
112
  scaler = joblib.load(SCALER_PATH)
113
+ latest = df_full.iloc[-WINDOW:][feat_cols].values.flatten().reshape(1, -1)
114
  latest_scaled = scaler.transform(latest)
115
 
116
  model = joblib.load(MODEL_PATH)
117
  pred = int(model.predict(latest_scaled)[0])
118
  prob = float(model.predict_proba(latest_scaled)[0][1])
119
 
120
+ return {
121
+ "status": "βœ… Prediction",
122
+ "signal": "BUY (Hold 4h)" if pred == 1 else "HOLD/SELL",
123
+ "confidence_pct": f"{prob*100:.1f}%",
124
+ "logic": "Predicts >0.15% move next 4h"
125
+ }
126
  except Exception as e:
127
  logger.error(f"❌ Predict failed: {e}")
128
+ return {"status": "❌ Error", "message": str(e)[:150]}
129
 
130
+ # 🎨 UI
 
131
  tradingview_html = """
132
  <div style="width:100%;height:400px;margin:10px 0;">
133
  <iframe src="https://s.tradingview.com/widgetembed/?symbol=BINANCE%3AETHUSDT&interval=240&hidesidetoolbar=1&symboledit=1&saveimage=1&toolbarbg=transparent&studies=%5B%5D&theme=dark&style=1&timezone=UTC&withdateranges=1&showpopupbutton=1&studies_overrides=%7B%7D&overrides=%7B%7D&enabled_features=%5B%5D&disabled_features=%5B%5D&showpopupbutton=1&locale=en"
 
149
 
150
  train_btn.click(run_train, outputs=train_out)
151
  pred_btn.click(run_predict, outputs=pred_out)
152
+ gr.Markdown("πŸ’‘ *Model saved in `/tmp/`. Retrain if Space restarts.*")
153
 
 
154
  demo.queue(default_concurrency_limit=1)
155
+ logger.info("βœ… App ready. HF Spaces runner will take over.")