angxt commited on
Commit
164e1e3
·
verified ·
1 Parent(s): 19da22f

Upload 6 files

Browse files
Files changed (6) hide show
  1. app.py +398 -0
  2. creds.json +13 -0
  3. histgb_pca_model_clean.pkl +3 -0
  4. pca.pkl +3 -0
  5. requirements.txt +13 -0
  6. scaler.pkl +3 -0
app.py ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app_final.py (final debugged version)
2
+ import streamlit as st
3
+ import requests
4
+ import yfinance as yf
5
+ import pandas as pd
6
+ import numpy as np
7
+ import os
8
+ from datetime import datetime, timedelta
9
+ import joblib
10
+ import re
11
+ import time
12
+
13
+ # ---------------------------- CONFIG ----------------------------
14
+ HF_API_TOKEN = st.secrets["HF_API_TOKEN"]
15
+ CRYPTO_NEWS_API_KEY = "of9jvyylshwcddtw0qsv16zswpi8k39lbr67qm97"
16
+ FRED_API_KEY = "4c3fd5be0b1f052f5d1d0080261277b1"
17
+
18
+ FINBERT_API = "https://api-inference.huggingface.co/models/ProsusAI/finbert"
19
+ HEADERS = {"Authorization": f"Bearer {HF_API_TOKEN}"}
20
+
21
+ TICKERS = {
22
+ "bitcoin": "BTC-USD",
23
+ "gold": "GC=F",
24
+ "sp500": "^GSPC",
25
+ "dxy": "DX-Y.NYB"
26
+ }
27
+
28
+ FRED_CODES = {
29
+ "interest_rate": "FEDFUNDS",
30
+ "inflation": "CPIAUCSL"
31
+ }
32
+
33
+ model = joblib.load("histgb_pca_model_clean.pkl")
34
+ pca = joblib.load("pca.pkl")
35
+ scaler = joblib.load("scaler.pkl")
36
+
37
+ # ---------------------------- FUNCTIONS ----------------------------
38
+ def fetch_news(source):
39
+ url = f"https://cryptonews-api.com/api/v1/category"
40
+ params = {
41
+ "section": "general",
42
+ "items": 10,
43
+ "page": 1,
44
+ "source": source,
45
+ "token": CRYPTO_NEWS_API_KEY
46
+ }
47
+ r = requests.get(url, params=params)
48
+ articles = r.json().get("data", [])
49
+ texts = []
50
+ for art in articles:
51
+ summary = art.get("text") or art.get("content", "").split(".")[0]
52
+ texts.append(summary)
53
+ return texts
54
+
55
+ def call_finbert(news_list):
56
+ results_df = []
57
+ news_list = news_list[:5]
58
+ for idx, news in enumerate(news_list):
59
+ if not isinstance(news, str) or not news.strip():
60
+ results_df.append({"positive": 0.0, "neutral": 0.0, "negative": 0.0})
61
+ continue
62
+ payload = {"inputs": news}
63
+ for attempt in range(5):
64
+ try:
65
+ response = requests.post(FINBERT_API, headers=HEADERS, json=payload, timeout=30)
66
+ response.raise_for_status()
67
+ output = response.json()
68
+
69
+ # Get raw scores
70
+ scores_raw = {item["label"].lower(): item["score"] for item in output[0]}
71
+
72
+ # Ensure fixed column order
73
+ aligned_scores = {
74
+ "positive": scores_raw.get("positive", 0.0),
75
+ "neutral": scores_raw.get("neutral", 0.0),
76
+ "negative": scores_raw.get("negative", 0.0)
77
+ }
78
+
79
+ results_df.append(aligned_scores)
80
+ break
81
+ except requests.exceptions.RequestException as e:
82
+ st.warning(f"⚠️ FinBERT error on article {idx+1}, attempt {attempt+1}/5: {e}")
83
+ time.sleep(2)
84
+ except Exception as ex:
85
+ st.warning(f"❌ Failed to analyze article {idx+1}: {ex}")
86
+ results_df.append({"positive": 0.0, "neutral": 0.0, "negative": 0.0})
87
+ break
88
+ return pd.DataFrame(results_df)
89
+
90
+ def aggregate_sentiments(sentiment_df):
91
+ scaled = sentiment_df.copy()
92
+ for col in scaled.columns:
93
+ scaled[col] = (scaled[col] - scaled[col].min()) / (scaled[col].max() - scaled[col].min() + 1e-8)
94
+ weighted = scaled.copy()
95
+ for col in ["positive", "negative"]:
96
+ weighted[col] = np.where(scaled[col] > 0.75, scaled[col] * 1.5, scaled[col])
97
+ weighted[col] = np.clip(weighted[col], 0, 1)
98
+ weighted["neutral"] = scaled["neutral"]
99
+ return weighted.mean().to_dict(), (scaled > 0.75).sum().to_dict()
100
+
101
+ def fetch_yahoo_data(ticker, date):
102
+ data = yf.Ticker(ticker).history(start=date, end=date + timedelta(days=1))
103
+ if not data.empty:
104
+ return {
105
+ "open": round(data["Open"].iloc[0], 2),
106
+ "high": round(data["High"].iloc[0], 2),
107
+ "low": round(data["Low"].iloc[0], 2),
108
+ "close": round(data["Close"].iloc[0], 2),
109
+ "volume": int(data["Volume"].iloc[0]) if ticker != TICKERS["dxy"] else None,
110
+ "change_pct": round(((data["Close"].iloc[0] - data["Open"].iloc[0]) / data["Open"].iloc[0]) * 100, 2)
111
+ }
112
+ else:
113
+ st.warning(f"⚠️ No trading data for {ticker} on {date.strftime('%Y-%m-%d')}, using previous available data.")
114
+ return fetch_yahoo_data(ticker, date - timedelta(days=1))
115
+
116
+ def fetch_fred(code, month):
117
+ url = f"https://api.stlouisfed.org/fred/series/observations"
118
+ params = {
119
+ "series_id": code,
120
+ "observation_start": f"{month}-01",
121
+ "api_key": FRED_API_KEY,
122
+ "file_type": "json"
123
+ }
124
+ res = requests.get(url, params=params).json()
125
+ try:
126
+ return float(res["observations"][0]["value"])
127
+ except:
128
+ prev_month = (datetime.strptime(month, "%Y-%m") - timedelta(days=30)).strftime("%Y-%m")
129
+ return fetch_fred(code, prev_month)
130
+
131
+ def make_prediction(input_data):
132
+ expected_cols = list(scaler.feature_names_in_)
133
+
134
+ # SAFETY CHECK
135
+ if len(input_data) != len(expected_cols):
136
+ raise ValueError(f"❌ Input length mismatch! Got {len(input_data)}, expected {len(expected_cols)}")
137
+
138
+ # Align input values to expected column order
139
+ input_dict = dict(zip(expected_cols, input_data))
140
+ input_df = pd.DataFrame([input_dict])[expected_cols]
141
+
142
+ # DEBUG VIEW
143
+ st.write("📄 Aligned Input DataFrame:")
144
+ st.dataframe(input_df)
145
+
146
+ # Transform
147
+ x_scaled = scaler.transform(input_df)
148
+ x_pca = pca.transform(x_scaled)
149
+ proba = model.predict_proba(x_pca)[0][1]
150
+ prediction = "Increase" if proba >= 0.62 else "Decrease"
151
+ return prediction, round(proba, 4)
152
+
153
+
154
+ import gspread
155
+ from oauth2client.service_account import ServiceAccountCredentials
156
+
157
+ def log_prediction(record):
158
+ try:
159
+ scope = ["https://spreadsheets.google.com/feeds",
160
+ "https://www.googleapis.com/auth/drive"]
161
+
162
+ creds = ServiceAccountCredentials.from_json_keyfile_name("creds.json", scope)
163
+ client = gspread.authorize(creds)
164
+
165
+ sheet = client.open("BTC Predictions Log").sheet1 # Must match your actual Google Sheet name
166
+ sheet.append_row(list(record.values()))
167
+ st.success("✅ Logged to Google Sheet successfully.")
168
+ except Exception as e:
169
+ st.warning(f"⚠️ Logging to Google Sheets failed: {e}")
170
+
171
+ # ---------------------------- STREAMLIT UI ----------------------------
172
+ st.set_page_config(page_title="Next Day Bitcoin Price Movement", layout="wide")
173
+ st.title("🔮 Next Day Bitcoin Price Movement Predictor")
174
+
175
+ date = st.date_input("Select a date", datetime.today() - timedelta(days=1))
176
+ month = date.strftime("%Y-%m")
177
+
178
+ if "news_loaded" not in st.session_state:
179
+ st.session_state.news_loaded = False
180
+
181
+ sentiment_features = []
182
+ aggregated_display = {}
183
+ news_by_source = {"CryptoNews": [], "CryptoPotato": []}
184
+ edited_news_by_source = {}
185
+
186
+ # ------------------------------------
187
+ # STEP 1: FETCH NEWS + ENABLE EDITING
188
+ # ------------------------------------
189
+ if not st.session_state.news_loaded:
190
+ if st.button("📥 Fetch News"):
191
+ for src in ["CryptoNews", "CryptoPotato"]:
192
+ try:
193
+ news = fetch_news(src)
194
+ news_by_source[src] = news
195
+ st.session_state[src] = "\n\n".join(news) # store for text_area default
196
+ except Exception as e:
197
+ st.warning(f"⚠️ Could not fetch {src}: {e}")
198
+ st.session_state[src] = ""
199
+ st.session_state.news_loaded = True
200
+ st.rerun()
201
+
202
+ # ------------------------------------
203
+ # STEP 2: SHOW TEXT BOXES + RUN PREDICTION
204
+ # ------------------------------------
205
+ if st.session_state.news_loaded:
206
+ st.subheader("📝 Edit News Articles")
207
+ for src in ["CryptoNews", "CryptoPotato"]:
208
+ default_text = st.session_state.get(src, "")
209
+ user_input = st.text_area(f"{src} Articles (5 max, one per paragraph)", default_text, height=300)
210
+ edited_news_by_source[src] = [para.strip() for para in user_input.split("\n\n") if para.strip()]
211
+
212
+ if st.button("🔮 Make Prediction"):
213
+ for src in ["CryptoNews", "CryptoPotato"]:
214
+ try:
215
+ news_by_source[src] = edited_news_by_source[src]
216
+ scores_df = call_finbert(news_by_source[src])
217
+ st.write(f"📊 FinBERT Scores for {src}:", scores_df)
218
+
219
+ weighted_avg, extreme_count = aggregate_sentiments(scores_df)
220
+ total_articles = len(scores_df)
221
+
222
+ pct_scores = {
223
+ "positive_pct": extreme_count.get("positive", 0) / total_articles,
224
+ "neutral_pct": extreme_count.get("neutral", 0) / total_articles,
225
+ "negative_pct": extreme_count.get("negative", 0) / total_articles
226
+ }
227
+
228
+ sentiment_features.extend([
229
+ weighted_avg["positive"],
230
+ weighted_avg["neutral"],
231
+ weighted_avg["negative"],
232
+ pct_scores["positive_pct"],
233
+ pct_scores["neutral_pct"],
234
+ pct_scores["negative_pct"]
235
+ ])
236
+ except Exception as e:
237
+ st.warning(f"⚠️ Failed for {src}: {e}")
238
+ sentiment_features.extend([0.0] * 6)
239
+ news_by_source[src] = []
240
+
241
+ st.markdown("**Aggregated Sentiment**")
242
+ st.write("🔎 News by Source:", news_by_source)
243
+ sentiment_feature_labels = {
244
+ "cryptonews_positive_weighted": sentiment_features[0],
245
+ "cryptonews_neutral_weighted": sentiment_features[1],
246
+ "cryptonews_negative_weighted": sentiment_features[2],
247
+ "cryptonews_positive_pct": sentiment_features[3],
248
+ "cryptonews_neutral_pct": sentiment_features[4],
249
+ "cryptonews_negative_pct": sentiment_features[5],
250
+ "cryptopotato_positive_weighted": sentiment_features[6],
251
+ "cryptopotato_neutral_weighted": sentiment_features[7],
252
+ "cryptopotato_negative_weighted": sentiment_features[8],
253
+ "cryptopotato_positive_pct": sentiment_features[9],
254
+ "cryptopotato_neutral_pct": sentiment_features[10],
255
+ "cryptopotato_negative_pct": sentiment_features[11],
256
+ }
257
+ st.markdown("### 🧠 Sentiment Features by Source")
258
+ st.json(sentiment_feature_labels)
259
+
260
+ # Average across both sources
261
+ if len(sentiment_features) == 12:
262
+ aggregated_sentiments = [
263
+ (sentiment_features[0] + sentiment_features[6]) / 2,
264
+ (sentiment_features[1] + sentiment_features[7]) / 2,
265
+ (sentiment_features[2] + sentiment_features[8]) / 2,
266
+ (sentiment_features[3] + sentiment_features[9]) / 2,
267
+ (sentiment_features[4] + sentiment_features[10]) / 2,
268
+ (sentiment_features[5] + sentiment_features[11]) / 2
269
+ ]
270
+ elif len(sentiment_features) == 6:
271
+ aggregated_sentiments = sentiment_features
272
+ else:
273
+ st.warning("⚠️ Sentiment features incomplete. Defaulting to 0s.")
274
+ aggregated_sentiments = [0.0] * 6
275
+
276
+ # Fetch BTC + macro data
277
+ st.subheader("📈 Bitcoin Price Data")
278
+ btc = fetch_yahoo_data(TICKERS["bitcoin"], date)
279
+ st.json(btc)
280
+
281
+ st.subheader("📊 Macroeconomic Indicators")
282
+ macro = {}
283
+ for k, t in TICKERS.items():
284
+ if k != "bitcoin":
285
+ try:
286
+ macro[k] = fetch_yahoo_data(t, date)
287
+ except Exception as e:
288
+ st.warning(f"⚠️ Failed to fetch {k.upper()} data: {e}")
289
+ macro[k] = {"open": 0, "high": 0, "low": 0, "close": 0, "volume": 0, "change_pct": 0}
290
+ st.json(macro)
291
+
292
+ st.subheader("🏩 Fed Indicators")
293
+ fed = {
294
+ "interest_rate": fetch_fred(FRED_CODES["interest_rate"], month),
295
+ "inflation": fetch_fred(FRED_CODES["inflation"], month)
296
+ }
297
+ st.json(fed)
298
+
299
+ # ========== BUILD FINAL INPUT DICT SAFELY ==========
300
+ final_input_dict = {
301
+ "S&P_500_Open": macro["sp500"].get("open", 0),
302
+ "S&P_500_High": macro["sp500"].get("high", 0),
303
+ "S&P_500_Low": macro["sp500"].get("low", 0),
304
+ "S&P_500_Close": macro["sp500"].get("close", 0),
305
+ "S&P_500_Volume": macro["sp500"].get("volume", 0),
306
+ "S&P_500_%_Change": macro["sp500"].get("change_pct", 0),
307
+
308
+ "Gold_Prices_Open": macro["gold"].get("open", 0),
309
+ "Gold_Prices_High": macro["gold"].get("high", 0),
310
+ "Gold_Prices_Low": macro["gold"].get("low", 0),
311
+ "Gold_Prices_Close": macro["gold"].get("close", 0),
312
+ "Gold_Prices_Volume": macro["gold"].get("volume", 0),
313
+ "Gold_Prices_%_Change": macro["gold"].get("change_pct", 0),
314
+
315
+ "US_Dollar_Index_DXY_Open": macro["dxy"].get("open", 0),
316
+ "US_Dollar_Index_DXY_High": macro["dxy"].get("high", 0),
317
+ "US_Dollar_Index_DXY_Low": macro["dxy"].get("low", 0),
318
+ "US_Dollar_Index_DXY_Close": macro["dxy"].get("close", 0),
319
+ "US_Dollar_Index_DXY_%_Change": macro["dxy"].get("change_pct", 0),
320
+
321
+ "Federal_Reserve_Interest_Rates_FEDFUNDS": fed.get("interest_rate", 0),
322
+ "Inflation_CPIAUCNS": fed.get("inflation", 0),
323
+
324
+ "Open": btc.get("open", 0),
325
+ "High": btc.get("high", 0),
326
+ "Low": btc.get("low", 0),
327
+ "Close": btc.get("close", 0),
328
+ "Volume": btc.get("volume", 0),
329
+ "Change %": btc.get("change_pct", 0),
330
+
331
+ "positive_weighted": aggregated_sentiments[0],
332
+ "neutral_weighted": aggregated_sentiments[1],
333
+ "negative_weighted": aggregated_sentiments[2],
334
+ "negative_pct": aggregated_sentiments[5],
335
+ "neutral_pct": aggregated_sentiments[4],
336
+ "positive_pct": aggregated_sentiments[3],
337
+ }
338
+
339
+ # ========== PREPARE & PREDICT ==========
340
+ expected_cols = list(scaler.feature_names_in_)
341
+ final_input = [final_input_dict[col] for col in expected_cols]
342
+
343
+ if any(pd.isna(x) for x in final_input):
344
+ st.error("❌ Missing or invalid input data. Please check news, market, or macro feeds.")
345
+ else:
346
+ # Prepare aligned input
347
+ input_df = pd.DataFrame([final_input_dict])[expected_cols]
348
+ x_scaled = scaler.transform(input_df)
349
+ x_pca = pca.transform(x_scaled)
350
+
351
+ # Model prediction
352
+ proba = model.predict_proba(x_pca)[0][1]
353
+ prediction = "Increase" if proba >= 0.62 else "Decrease"
354
+
355
+ # PCA features table
356
+ pca_df = pd.DataFrame(x_pca, columns=[f"PC{i+1}" for i in range(x_pca.shape[1])])
357
+ st.markdown("### 🧬 PCA-Transformed Features")
358
+ st.dataframe(pca_df.style.format("{:.4f}"))
359
+
360
+ # Prediction display
361
+ st.subheader("🔮 Prediction")
362
+ if prediction == "Decrease":
363
+ st.markdown(
364
+ f"<div style='background-color:#fbeaea;color:#9e1c1c;padding:10px;border-radius:8px;'>"
365
+ f"<b>Next Day BTC Price:</b> {prediction} (Prob: {proba:.2f})</div>",
366
+ unsafe_allow_html=True
367
+ )
368
+ else:
369
+ st.success(f"Next Day BTC Price: **{prediction}** (Prob: {proba:.2f})")
370
+
371
+ # Log prediction
372
+ log = {
373
+ "fetch_date": datetime.today().strftime("%Y-%m-%d"),
374
+ "btc_open": btc["open"],
375
+ "btc_close": btc["close"],
376
+ "sent_pos": aggregated_sentiments[0],
377
+ "sent_neu": aggregated_sentiments[1],
378
+ "sent_neg": aggregated_sentiments[2],
379
+ "sent_pos_pct": aggregated_sentiments[3],
380
+ "sent_neu_pct": aggregated_sentiments[4],
381
+ "sent_neg_pct": aggregated_sentiments[5],
382
+ "macro_gold": macro["gold"]["close"],
383
+ "macro_sp500": macro["sp500"]["close"],
384
+ "macro_dxy": macro["dxy"]["close"],
385
+ "interest_rate": fed["interest_rate"],
386
+ "inflation": fed["inflation"],
387
+ "prediction": prediction,
388
+ "prob": proba,
389
+ "news_cryptonews": " || ".join(news_by_source["CryptoNews"]),
390
+ "news_cryptopotato": " || ".join(news_by_source["CryptoPotato"])
391
+ }
392
+
393
+ log_prediction(log)
394
+ st.success("✅ Logged to predictions_log.csv")
395
+
396
+
397
+
398
+
creds.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "service_account",
3
+ "project_id": "bitcoin-predictor-log",
4
+ "private_key_id": "c66f0bb452bb5ffcaa9b23ac93eb42347000d94f",
5
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCagBsCFFxFDHOJ\nuJGm7iQMU8+j/xnCpvep4/+lMvtCVlZsHD+C8SN23IIVC73oNHSMIOkUT7vjxlRP\nZ9bQSgLZ1KXspfH0PdjYJH1pxJdnElgKIUTK5NTcj33ZAKWa/XpkAsaZMHs7QUuQ\nlkhxsATVNOK7c/QtBMqqcMh14SHDUHBm8zncDoRg8Rmf0uaMEs+zXPPbOevBtEFo\nQH82k5jGSR8oevGfCFdOnXngfyyhX9Unm21C9TqpdL0ROGIj5V8yTMHTBKkM62MQ\nLfeKABufx+uGb4H4Paa2ZlJacFcXY7bsj6OsXxdeulNZLB5AQg6QQm1jtx40t0oK\nsxr2k+ItAgMBAAECggEAELFKGbuq62/otMbK/9LndKiCfOjFNv10sTeXyZi8QmLT\nJAuaRhK5HvC8ojr6SfIO7Ivqut3Rgk7NkaW5tRfl/nSF989HGLks6k9o+Go75HA8\nMF3/UX+PSwQ6190Ex33fARq2q9rr2Y9Ys3a1PYlDwGheHdwtk8aagfc9BVVtYS1u\n+CHntFZb8J6ozTDQREe3qL9v0YzEKaWeReBVE1d+j++EtpeFij9v/sl2d1oQMTLH\nPzrKeMzbMU3OqTWENhAIVjihuqZFZY99dAMdmyh1QtGrUV7Iic8ixM9fFB/aF9EQ\nsu7pmOQu2jPdB6Dkxwwy3S8yimOb2MyGPrBTAHx8wQKBgQDKiZ0ZcZbB7HsGLUAK\n0s7AJ3DoVh2KTxwsmmfoZIJgfu16JlSRWUznQdORJ/kTR4b6E6o36N2GrSjmIC5O\n0UKoaeoGoo3azjf+5C+dJ18nQ3wd18gDvMb2/yehsBPASwVnPie28ZN1664Tx8wP\ni0rJuadlqVzgY2n3d8ueHK8YwQKBgQDDSGcwNzLZMBAIeumA9rzmW+WESPlbgy3D\nYnkLP9jy9I1zazAbGW9S8AU4XdRc0tkHjCdzY7dC9hGUAEyTRS1n4C93U5HpJAzP\nQH/ob1Wsz2DCTR18dRZeTFKECHcHMjWjFJVMHvpjNGuyS4TLLoSJRhE4PnSEZOzJ\n8FVqzIRYbQKBgQDJKlfMPsLzR/OFVgpm9az+s+30Bhx/FEYykwYjjzjjqQ4sJcgX\nObAlfX8qjJ5apM+OsVt+/p6QtoqJz2rPRA9GATS9dFUa/3okg/Y6zDE5aVDsvzbd\nZ3HjP9jYQm/LrXfnbJe7oEPLetPCt86Zncshg3GditNB19wXPHgUSf8rwQKBgQCD\nql6AgMcU7rXwscacQEAO5SjzrywJSoHheZR5RDwnW0G/7yZJLzYC6nfqkEDtsO/J\nifLTdwkJ6dTiP+1hYkQCBIKcZsk7MyY72pYjBmXylQP9HXdjAaLqQ3VjNj3iqTTG\n1sruvvg9SQvP8+D+CUgtMgPMMzfmSBHq+dLtpiUZZQKBgQCYzwgY9k6xD1EEMljJ\n66UAEiQiIJOomatjmxc4MNrK1Zsows6f40qnJ22PhqNuKHqCGyj+qnQP9o1MWJGC\n7McvxUR16Z2oEtgRPUDMXe+I2PBaRL76m1SZ3cYV4Hm5HJXacoJU35bqo5NKQQi7\nlf6mgxpvZipUt/s3tChvhDpeeg==\n-----END PRIVATE KEY-----\n",
6
+ "client_email": "btc-predictor-logger@bitcoin-predictor-log.iam.gserviceaccount.com",
7
+ "client_id": "114024114053631353428",
8
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9
+ "token_uri": "https://oauth2.googleapis.com/token",
10
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/btc-predictor-logger%40bitcoin-predictor-log.iam.gserviceaccount.com",
12
+ "universe_domain": "googleapis.com"
13
+ }
histgb_pca_model_clean.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3465f30c06057d147bba5342686788c87fab93a67be6a158e5e5c93faeb8a1f9
3
+ size 33944
pca.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8ed7c470f4c88de93c41296fde8af77202271d070be7eab95ae4425dabb7f5ed
3
+ size 4223
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ numpy==1.24.4
3
+ pandas
4
+ scikit-learn
5
+ matplotlib
6
+ seaborn
7
+ joblib
8
+ optuna
9
+ shap
10
+ yfinance
11
+ requests
12
+ gspread
13
+ oauth2client
scaler.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3ec8c4153f48ee2b3843dfc80bc967528a93bb152aeafe43fbfd4257fc90f46b
3
+ size 2319