Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -13,13 +13,12 @@ logging.basicConfig(level=logging.INFO)
|
|
13 |
OKX_TICKERS_ENDPOINT = "https://www.okx.com/api/v5/market/tickers?instType=SPOT"
|
14 |
OKX_CANDLE_ENDPOINT = "https://www.okx.com/api/v5/market/candles"
|
15 |
|
16 |
-
# For demonstration, only these mappings
|
17 |
TIMEFRAME_MAPPING = {
|
18 |
"1m": "1m",
|
19 |
"5m": "5m",
|
20 |
"15m": "15m",
|
21 |
"30m": "30m",
|
22 |
-
"1h": "1H",
|
23 |
"2h": "2H",
|
24 |
"4h": "4H",
|
25 |
"6h": "6H",
|
@@ -43,7 +42,6 @@ def fetch_okx_symbols():
|
|
43 |
return ["Error: Could not fetch OKX symbols"]
|
44 |
|
45 |
data = json_data.get("data", [])
|
46 |
-
# Example item in data: { "instId": "ETH-USDT", "instType": "SPOT", ... }
|
47 |
symbols = [item["instId"] for item in data if item.get("instType") == "SPOT"]
|
48 |
if not symbols:
|
49 |
logging.warning("No spot symbols found.")
|
@@ -56,25 +54,12 @@ def fetch_okx_symbols():
|
|
56 |
logging.error(f"Error fetching OKX symbols: {e}")
|
57 |
return [f"Error: {str(e)}"]
|
58 |
|
59 |
-
def fetch_okx_candles(symbol, timeframe="1H", limit=
|
60 |
"""
|
61 |
Fetch historical candle data for a symbol from OKX.
|
62 |
|
63 |
OKX data columns:
|
64 |
[ts, o, h, l, c, vol, volCcy, volCcyQuote, confirm]
|
65 |
-
|
66 |
-
Example:
|
67 |
-
[
|
68 |
-
"1597026383085", # ts
|
69 |
-
"3.721", # o
|
70 |
-
"3.743", # h
|
71 |
-
"3.677", # l
|
72 |
-
"3.708", # c
|
73 |
-
"8422410", # vol
|
74 |
-
"22698348.04828491", # volCcy
|
75 |
-
"12698348.04828491", # volCcyQuote
|
76 |
-
"0" # confirm
|
77 |
-
]
|
78 |
"""
|
79 |
logging.info(f"Fetching {limit} candles for {symbol} @ {timeframe} from OKX...")
|
80 |
params = {
|
@@ -99,24 +84,14 @@ def fetch_okx_candles(symbol, timeframe="1H", limit=100):
|
|
99 |
logging.warning(warning_msg)
|
100 |
return pd.DataFrame(), warning_msg
|
101 |
|
102 |
-
# OKX returns newest
|
103 |
items.reverse()
|
104 |
|
105 |
-
# Expecting 9 columns per the docs
|
106 |
columns = [
|
107 |
-
"ts",
|
108 |
-
"
|
109 |
-
"h", # high
|
110 |
-
"l", # low
|
111 |
-
"c", # close
|
112 |
-
"vol", # volume (base currency)
|
113 |
-
"volCcy", # volume in quote currency (for SPOT)
|
114 |
-
"volCcyQuote",
|
115 |
-
"confirm"
|
116 |
]
|
117 |
df = pd.DataFrame(items, columns=columns)
|
118 |
-
|
119 |
-
# Rename columns to be more descriptive or consistent
|
120 |
df.rename(columns={
|
121 |
"ts": "timestamp",
|
122 |
"o": "open",
|
@@ -125,8 +100,6 @@ def fetch_okx_candles(symbol, timeframe="1H", limit=100):
|
|
125 |
"c": "close"
|
126 |
}, inplace=True)
|
127 |
|
128 |
-
# Convert numeric columns
|
129 |
-
# 'confirm' often is "0" or "1" string, which you can parse as float or int if you want
|
130 |
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
|
131 |
numeric_cols = ["open", "high", "low", "close", "vol", "volCcy", "volCcyQuote", "confirm"]
|
132 |
df[numeric_cols] = df[numeric_cols].astype(float)
|
@@ -154,9 +127,10 @@ def prepare_data_for_prophet(df):
|
|
154 |
df_prophet = df.rename(columns={"timestamp": "ds", "close": "y"})
|
155 |
return df_prophet[["ds", "y"]]
|
156 |
|
157 |
-
def prophet_forecast(df_prophet, periods=10, freq="
|
158 |
"""
|
159 |
Train a Prophet model and forecast.
|
|
|
160 |
"""
|
161 |
if df_prophet.empty:
|
162 |
logging.warning("Prophet input is empty, no forecast can be generated.")
|
@@ -174,7 +148,7 @@ def prophet_forecast(df_prophet, periods=10, freq="H"):
|
|
174 |
|
175 |
def prophet_wrapper(df_prophet, forecast_steps, freq):
|
176 |
"""
|
177 |
-
|
178 |
"""
|
179 |
if len(df_prophet) < 10:
|
180 |
return pd.DataFrame(), "Not enough data for forecasting (need >=10 rows)."
|
@@ -183,8 +157,8 @@ def prophet_wrapper(df_prophet, forecast_steps, freq):
|
|
183 |
if err:
|
184 |
return pd.DataFrame(), err
|
185 |
|
186 |
-
#
|
187 |
-
future_only = full_forecast.
|
188 |
return future_only, ""
|
189 |
|
190 |
########################################
|
@@ -198,15 +172,13 @@ def predict(symbol, timeframe, forecast_steps):
|
|
198 |
# Convert user timeframe to OKX bar param
|
199 |
okx_bar = TIMEFRAME_MAPPING.get(timeframe, "1H")
|
200 |
|
201 |
-
# Let's fetch 500 candles
|
202 |
df_raw, err = fetch_okx_candles(symbol, timeframe=okx_bar, limit=500)
|
203 |
if err:
|
204 |
return pd.DataFrame(), err
|
205 |
|
206 |
df_prophet = prepare_data_for_prophet(df_raw)
|
207 |
-
|
208 |
-
|
209 |
-
freq = "H" if "h" in timeframe.lower() else "D"
|
210 |
|
211 |
future_df, err2 = prophet_wrapper(df_prophet, forecast_steps, freq)
|
212 |
if err2:
|
@@ -233,9 +205,8 @@ def main():
|
|
233 |
with gr.Blocks() as demo:
|
234 |
gr.Markdown("# OKX Price Forecasting with Prophet")
|
235 |
gr.Markdown(
|
236 |
-
"This app
|
237 |
-
"
|
238 |
-
"please try a different symbol or timeframe."
|
239 |
)
|
240 |
|
241 |
symbol_dd = gr.Dropdown(
|
@@ -249,7 +220,7 @@ def main():
|
|
249 |
value="1h"
|
250 |
)
|
251 |
steps_slider = gr.Slider(
|
252 |
-
label="Forecast Steps
|
253 |
minimum=1,
|
254 |
maximum=100,
|
255 |
value=10
|
|
|
13 |
OKX_TICKERS_ENDPOINT = "https://www.okx.com/api/v5/market/tickers?instType=SPOT"
|
14 |
OKX_CANDLE_ENDPOINT = "https://www.okx.com/api/v5/market/candles"
|
15 |
|
|
|
16 |
TIMEFRAME_MAPPING = {
|
17 |
"1m": "1m",
|
18 |
"5m": "5m",
|
19 |
"15m": "15m",
|
20 |
"30m": "30m",
|
21 |
+
"1h": "1H", # OKX expects '1H'
|
22 |
"2h": "2H",
|
23 |
"4h": "4H",
|
24 |
"6h": "6H",
|
|
|
42 |
return ["Error: Could not fetch OKX symbols"]
|
43 |
|
44 |
data = json_data.get("data", [])
|
|
|
45 |
symbols = [item["instId"] for item in data if item.get("instType") == "SPOT"]
|
46 |
if not symbols:
|
47 |
logging.warning("No spot symbols found.")
|
|
|
54 |
logging.error(f"Error fetching OKX symbols: {e}")
|
55 |
return [f"Error: {str(e)}"]
|
56 |
|
57 |
+
def fetch_okx_candles(symbol, timeframe="1H", limit=500):
|
58 |
"""
|
59 |
Fetch historical candle data for a symbol from OKX.
|
60 |
|
61 |
OKX data columns:
|
62 |
[ts, o, h, l, c, vol, volCcy, volCcyQuote, confirm]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
"""
|
64 |
logging.info(f"Fetching {limit} candles for {symbol} @ {timeframe} from OKX...")
|
65 |
params = {
|
|
|
84 |
logging.warning(warning_msg)
|
85 |
return pd.DataFrame(), warning_msg
|
86 |
|
87 |
+
# Reverse to chronological (OKX returns newest first)
|
88 |
items.reverse()
|
89 |
|
|
|
90 |
columns = [
|
91 |
+
"ts", "o", "h", "l", "c", "vol",
|
92 |
+
"volCcy", "volCcyQuote", "confirm"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
]
|
94 |
df = pd.DataFrame(items, columns=columns)
|
|
|
|
|
95 |
df.rename(columns={
|
96 |
"ts": "timestamp",
|
97 |
"o": "open",
|
|
|
100 |
"c": "close"
|
101 |
}, inplace=True)
|
102 |
|
|
|
|
|
103 |
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
|
104 |
numeric_cols = ["open", "high", "low", "close", "vol", "volCcy", "volCcyQuote", "confirm"]
|
105 |
df[numeric_cols] = df[numeric_cols].astype(float)
|
|
|
127 |
df_prophet = df.rename(columns={"timestamp": "ds", "close": "y"})
|
128 |
return df_prophet[["ds", "y"]]
|
129 |
|
130 |
+
def prophet_forecast(df_prophet, periods=10, freq="h"):
|
131 |
"""
|
132 |
Train a Prophet model and forecast.
|
133 |
+
Using 'h' or 'd' to avoid the future deprecation warning in pandas.
|
134 |
"""
|
135 |
if df_prophet.empty:
|
136 |
logging.warning("Prophet input is empty, no forecast can be generated.")
|
|
|
148 |
|
149 |
def prophet_wrapper(df_prophet, forecast_steps, freq):
|
150 |
"""
|
151 |
+
Forecast, then slice out only the new/future rows using .loc.
|
152 |
"""
|
153 |
if len(df_prophet) < 10:
|
154 |
return pd.DataFrame(), "Not enough data for forecasting (need >=10 rows)."
|
|
|
157 |
if err:
|
158 |
return pd.DataFrame(), err
|
159 |
|
160 |
+
# Slice from len(df_prophet) onward, for columns ds, yhat, yhat_lower, yhat_upper
|
161 |
+
future_only = full_forecast.loc[len(df_prophet):, ["ds", "yhat", "yhat_lower", "yhat_upper"]]
|
162 |
return future_only, ""
|
163 |
|
164 |
########################################
|
|
|
172 |
# Convert user timeframe to OKX bar param
|
173 |
okx_bar = TIMEFRAME_MAPPING.get(timeframe, "1H")
|
174 |
|
|
|
175 |
df_raw, err = fetch_okx_candles(symbol, timeframe=okx_bar, limit=500)
|
176 |
if err:
|
177 |
return pd.DataFrame(), err
|
178 |
|
179 |
df_prophet = prepare_data_for_prophet(df_raw)
|
180 |
+
# If timeframe string has 'h', use 'h' for freq. Else use 'd'
|
181 |
+
freq = "h" if "h" in timeframe.lower() else "d"
|
|
|
182 |
|
183 |
future_df, err2 = prophet_wrapper(df_prophet, forecast_steps, freq)
|
184 |
if err2:
|
|
|
205 |
with gr.Blocks() as demo:
|
206 |
gr.Markdown("# OKX Price Forecasting with Prophet")
|
207 |
gr.Markdown(
|
208 |
+
"This app pulls spot-market candles from OKX, trains a simple Prophet model, "
|
209 |
+
"and displays only future predictions. If you see errors or no data, try another symbol/timeframe."
|
|
|
210 |
)
|
211 |
|
212 |
symbol_dd = gr.Dropdown(
|
|
|
220 |
value="1h"
|
221 |
)
|
222 |
steps_slider = gr.Slider(
|
223 |
+
label="Forecast Steps",
|
224 |
minimum=1,
|
225 |
maximum=100,
|
226 |
value=10
|