Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -62,7 +62,7 @@ SERVER = StdioServerParameters(
|
|
| 62 |
# Tool şemasındaki argüman adları sürümler arası değişebilir; şemaya bakıp eşleştiriyoruz.
|
| 63 |
ALIASES = {
|
| 64 |
"symbol": ["symbol", "pair"],
|
| 65 |
-
"timeframe": ["timeframe", "interval", "tf"],
|
| 66 |
"exchange": ["exchange"],
|
| 67 |
"limit": ["limit", "count", "n", "bars"],
|
| 68 |
"until": ["until", "end", "end_time", "endTime", "to", "before"],
|
|
@@ -147,6 +147,113 @@ async def fetch_ohlcv(session, tools, symbol, timeframe, exchange, total):
|
|
| 147 |
return out.to_dict("records")
|
| 148 |
|
| 149 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
def _numeric_list(o, min_len):
|
| 151 |
return (isinstance(o, list) and len(o) >= min_len and
|
| 152 |
all(x is None or (isinstance(x, (int, float)) and not isinstance(x, bool)) for x in o[:50]))
|
|
@@ -491,11 +598,17 @@ def _root(e):
|
|
| 491 |
return f"{type(e).__name__}: {e}"
|
| 492 |
|
| 493 |
|
| 494 |
-
def run(symbol, timeframe, exchange, horizon, limit):
|
| 495 |
try:
|
| 496 |
ohlcv, deep, ind, extra = asyncio.run(run_async(symbol, timeframe, exchange, horizon, limit))
|
| 497 |
df = to_df(ohlcv)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
ext = indicator_features(ind, df)
|
|
|
|
|
|
|
| 499 |
res = predict(df, int(horizon), ext=ext)
|
| 500 |
except BaseException as e: # noqa: BLE001
|
| 501 |
import traceback
|
|
@@ -548,12 +661,13 @@ with gr.Blocks(title="Tickscope Tahmin") as demo:
|
|
| 548 |
with gr.Row():
|
| 549 |
horizon = gr.Slider(1, 24, value=4, step=1, label="Tahmin ufku (bar)")
|
| 550 |
limit = gr.Slider(300, 20000, value=5000, step=100, label="Mum sayısı (1000 üstü sayfalanır)")
|
|
|
|
| 551 |
btn = gr.Button("Tahmin et", variant="primary")
|
| 552 |
out_md = gr.Markdown()
|
| 553 |
out_plot = gr.Plot()
|
| 554 |
with gr.Accordion("Tickscope ham çıktı (deep_analyze / indicators)", open=False):
|
| 555 |
out_raw = gr.Code(language="json")
|
| 556 |
-
btn.click(run, [symbol, timeframe, exchange, horizon, limit], [out_md, out_plot, out_raw])
|
| 557 |
|
| 558 |
if __name__ == "__main__":
|
| 559 |
demo.launch()
|
|
|
|
| 62 |
# Tool şemasındaki argüman adları sürümler arası değişebilir; şemaya bakıp eşleştiriyoruz.
|
| 63 |
ALIASES = {
|
| 64 |
"symbol": ["symbol", "pair"],
|
| 65 |
+
"timeframe": ["timeframe", "interval", "tf", "period"],
|
| 66 |
"exchange": ["exchange"],
|
| 67 |
"limit": ["limit", "count", "n", "bars"],
|
| 68 |
"until": ["until", "end", "end_time", "endTime", "to", "before"],
|
|
|
|
| 147 |
return out.to_dict("records")
|
| 148 |
|
| 149 |
|
| 150 |
+
# ----------------------------------------------------------------- 2. MCP: crypto-market-data-mcp (türev verisi)
|
| 151 |
+
# Kurulum: git clone https://github.com/eliasfire617/crypto-market-data-mcp
|
| 152 |
+
# (kendi venv'inde) pip install -r requirements.txt
|
| 153 |
+
# Sonra ortam değişkenleri:
|
| 154 |
+
# CRYPTO_MCP_SERVER=/yol/crypto-market-data-mcp/server.py
|
| 155 |
+
# CRYPTO_MCP_PYTHON=/yol/crypto-market-data-mcp/.venv/bin/python (opsiyonel; yoksa bu uygulamanın python'u)
|
| 156 |
+
DERIV_PATH = os.environ.get("CRYPTO_MCP_SERVER", "")
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def _perp(symbol):
|
| 160 |
+
s = symbol.strip().upper()
|
| 161 |
+
if "/" in s and ":" not in s:
|
| 162 |
+
s = f"{s}:{s.split('/')[1]}"
|
| 163 |
+
return s
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def parse_funding(payload):
|
| 167 |
+
"""Funding geçmişi çıktısında (şemadan bağımsız) zaman + oran kolonlarını bulur."""
|
| 168 |
+
def find_rows(o):
|
| 169 |
+
if isinstance(o, list) and len(o) >= 5 and all(isinstance(x, dict) for x in o[:3]):
|
| 170 |
+
return o
|
| 171 |
+
if isinstance(o, dict):
|
| 172 |
+
for v in o.values():
|
| 173 |
+
r = find_rows(v)
|
| 174 |
+
if r:
|
| 175 |
+
return r
|
| 176 |
+
return None
|
| 177 |
+
|
| 178 |
+
rows = find_rows(payload)
|
| 179 |
+
if not rows:
|
| 180 |
+
return None
|
| 181 |
+
d = pd.DataFrame(rows)
|
| 182 |
+
tcol = next((c for c in ("timestamp", "fundingTime", "time", "ts", "datetime") if c in d), None)
|
| 183 |
+
rcol = next((c for c in ("fundingRate", "funding_rate", "rate", "value") if c in d), None)
|
| 184 |
+
if tcol is None or rcol is None:
|
| 185 |
+
return None
|
| 186 |
+
ts = d[tcol]
|
| 187 |
+
if pd.api.types.is_numeric_dtype(ts):
|
| 188 |
+
ts = pd.to_datetime(ts, unit="ms" if ts.iloc[-1] > 1e11 else "s")
|
| 189 |
+
else:
|
| 190 |
+
ts = pd.to_datetime(ts)
|
| 191 |
+
if getattr(ts.dt, "tz", None) is not None:
|
| 192 |
+
ts = ts.dt.tz_convert(None)
|
| 193 |
+
out = pd.DataFrame({"ts": ts.astype("datetime64[ns]"), "r": pd.to_numeric(d[rcol], errors="coerce")})
|
| 194 |
+
return out.dropna().drop_duplicates("ts").sort_values("ts").reset_index(drop=True)
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
async def fetch_deriv_features(symbol, exchange, timeframe, df):
|
| 198 |
+
"""2. MCP'den funding geçmişi (özellik) + OI / long-short / likidasyon (canlı bağlam) çeker.
|
| 199 |
+
Dönen: (özellik tablosu | None, df (funding kapsamına kırpılmış olabilir), ham çıktı sözlüğü)."""
|
| 200 |
+
import sys
|
| 201 |
+
extra = {}
|
| 202 |
+
if not DERIV_PATH or not os.path.exists(DERIV_PATH):
|
| 203 |
+
extra["deriv_note"] = "CRYPTO_MCP_SERVER ayarlı değil / dosya yok; türev verisi atlandı"
|
| 204 |
+
return None, df, extra
|
| 205 |
+
params = StdioServerParameters(
|
| 206 |
+
command=os.environ.get("CRYPTO_MCP_PYTHON", sys.executable),
|
| 207 |
+
args=[DERIV_PATH],
|
| 208 |
+
cwd=os.path.dirname(os.path.abspath(DERIV_PATH)),
|
| 209 |
+
env={**os.environ},
|
| 210 |
+
)
|
| 211 |
+
perp = _perp(symbol)
|
| 212 |
+
try:
|
| 213 |
+
async with stdio_client(params) as (r, w):
|
| 214 |
+
async with ClientSession(r, w) as session:
|
| 215 |
+
await session.initialize()
|
| 216 |
+
tools = {t.name: t for t in (await session.list_tools()).tools}
|
| 217 |
+
hist = await call_tool(session, tools, "get_funding_rate_history",
|
| 218 |
+
symbol=perp, exchange=exchange, limit=1000)
|
| 219 |
+
for name in ("get_funding_rate", "compare_funding", "get_open_interest",
|
| 220 |
+
"get_long_short_ratio", "get_liquidations"):
|
| 221 |
+
if name in tools:
|
| 222 |
+
try:
|
| 223 |
+
extra[f"deriv_{name}"] = await call_tool(
|
| 224 |
+
session, tools, name, symbol=perp, exchange=exchange, timeframe=timeframe)
|
| 225 |
+
except Exception as e: # noqa: BLE001
|
| 226 |
+
extra[f"deriv_{name}"] = {"error": _root(e)}
|
| 227 |
+
except Exception as e: # noqa: BLE001
|
| 228 |
+
extra["deriv_error"] = _root(e)
|
| 229 |
+
return None, df, extra
|
| 230 |
+
|
| 231 |
+
bad = hist is None or (isinstance(hist, dict) and "error" in hist)
|
| 232 |
+
f = None if bad else parse_funding(hist)
|
| 233 |
+
if f is None or len(f) < 60:
|
| 234 |
+
extra["deriv_note"] = "Funding geçmişi okunamadı veya yetersiz; özellik eklenmedi"
|
| 235 |
+
extra["deriv_funding_raw"] = _strip_series(hist)
|
| 236 |
+
return None, df, extra
|
| 237 |
+
|
| 238 |
+
rr = f["r"]
|
| 239 |
+
f["fund"] = rr * 1e4 # baz puan
|
| 240 |
+
f["fund_z"] = (rr - rr.rolling(30).mean()) / (rr.rolling(30).std() + 1e-12)
|
| 241 |
+
f["fund_chg"] = rr.diff() * 1e4
|
| 242 |
+
f["fund_ma9"] = rr.rolling(9).mean() * 1e4
|
| 243 |
+
f = f.dropna().reset_index(drop=True)
|
| 244 |
+
start = f["ts"].iloc[0]
|
| 245 |
+
d2 = df[df["ts"] >= start].reset_index(drop=True)
|
| 246 |
+
if len(d2) < 800:
|
| 247 |
+
extra["deriv_note"] = (f"Funding geçmişi mumların yalnızca {len(d2)} barını kapsıyor (<800); "
|
| 248 |
+
"özellik eklenmedi")
|
| 249 |
+
return None, df, extra
|
| 250 |
+
d2["ts"] = d2["ts"].astype("datetime64[ns]")
|
| 251 |
+
m = pd.merge_asof(d2[["ts"]], f.drop(columns=["r"]), on="ts", direction="backward") # lookahead yok
|
| 252 |
+
m.index = d2.index
|
| 253 |
+
extra["deriv_funding_used"] = {"funding_gozlem": len(f), "baslangic": str(start), "bar": len(d2)}
|
| 254 |
+
return m.drop(columns=["ts"]).add_prefix("dv_"), d2, extra
|
| 255 |
+
|
| 256 |
+
|
| 257 |
def _numeric_list(o, min_len):
|
| 258 |
return (isinstance(o, list) and len(o) >= min_len and
|
| 259 |
all(x is None or (isinstance(x, (int, float)) and not isinstance(x, bool)) for x in o[:50]))
|
|
|
|
| 598 |
return f"{type(e).__name__}: {e}"
|
| 599 |
|
| 600 |
|
| 601 |
+
def run(symbol, timeframe, exchange, horizon, limit, use_deriv=True):
|
| 602 |
try:
|
| 603 |
ohlcv, deep, ind, extra = asyncio.run(run_async(symbol, timeframe, exchange, horizon, limit))
|
| 604 |
df = to_df(ohlcv)
|
| 605 |
+
dfeat = None
|
| 606 |
+
if use_deriv:
|
| 607 |
+
dfeat, df, dextra = asyncio.run(fetch_deriv_features(symbol, exchange, timeframe, df))
|
| 608 |
+
extra.update(dextra)
|
| 609 |
ext = indicator_features(ind, df)
|
| 610 |
+
if dfeat is not None:
|
| 611 |
+
ext = dfeat if ext is None else pd.concat([ext, dfeat], axis=1)
|
| 612 |
res = predict(df, int(horizon), ext=ext)
|
| 613 |
except BaseException as e: # noqa: BLE001
|
| 614 |
import traceback
|
|
|
|
| 661 |
with gr.Row():
|
| 662 |
horizon = gr.Slider(1, 24, value=4, step=1, label="Tahmin ufku (bar)")
|
| 663 |
limit = gr.Slider(300, 20000, value=5000, step=100, label="Mum sayısı (1000 üstü sayfalanır)")
|
| 664 |
+
use_deriv = gr.Checkbox(True, label="Türev verisi (funding; 2. MCP) — mumları funding geçmişinin kapsadığı aralığa kırpabilir")
|
| 665 |
btn = gr.Button("Tahmin et", variant="primary")
|
| 666 |
out_md = gr.Markdown()
|
| 667 |
out_plot = gr.Plot()
|
| 668 |
with gr.Accordion("Tickscope ham çıktı (deep_analyze / indicators)", open=False):
|
| 669 |
out_raw = gr.Code(language="json")
|
| 670 |
+
btn.click(run, [symbol, timeframe, exchange, horizon, limit, use_deriv], [out_md, out_plot, out_raw])
|
| 671 |
|
| 672 |
if __name__ == "__main__":
|
| 673 |
demo.launch()
|