Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| sys.stdout.reconfigure(encoding='utf-8') | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| import ccxt | |
| from typing import Optional | |
| import modal | |
| from modal.functions import FunctionCall | |
| app = FastAPI(title="Binance Data Dashboard API") | |
| # CORS | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Binance client | |
| try: | |
| binance = ccxt.binance() | |
| except Exception as e: | |
| print(f"Warning: Could not init Binance client: {e}") | |
| binance = None | |
| class DownloadRequest(BaseModel): | |
| symbol: str | |
| interval: str | |
| data_type: str | |
| start_date: str | |
| end_date: str | |
| threshold: Optional[float] = 1_000_000 | |
| agg_mode: Optional[str] = "Standard (Klines + Liq)" | |
| def health(): | |
| return {"status": "ok", "version": "2026-03-11-v2", "message": "Binance Data Dashboard API on Hugging Face"} | |
| def get_symbols(): | |
| try: | |
| markets = binance.load_markets() | |
| return {"symbols": sorted(list(markets.keys()))} | |
| except: | |
| fallback = ["BTC/USDT", "ETH/USDT", "BNB/USDT", "SOL/USDT"] | |
| return {"symbols": sorted(fallback)} | |
| def download_data(request: DownloadRequest): | |
| print(f"[CLOUD] New Task: {request.data_type} for {request.symbol}") | |
| hf_repo = os.environ.get("HF_REPO", "Vycka12/Base") | |
| hf_token = os.environ.get("HF_TOKEN") | |
| mapping = { | |
| 'Klines (OHLCV)': ('fetch_klines_cloud', (request.symbol, request.interval, request.start_date, request.end_date, hf_repo, hf_token)), | |
| 'Liquidations': ('fetch_liquidations_cloud', (request.symbol, request.start_date, request.end_date, hf_repo, hf_token)), | |
| 'AggTrades': ('fetch_aggtrades_cloud', (request.symbol, request.start_date, request.end_date, hf_repo, hf_token)), | |
| 'Raw Trades (Tick-Level)': ('fetch_raw_trades_cloud', (request.symbol, request.start_date, request.end_date, hf_repo, hf_token)), | |
| 'Dollar Bars (ML Ready)': ('fetch_dollar_bars_cloud', (request.symbol, request.start_date, request.end_date, request.threshold, hf_repo, hf_token)), | |
| 'Volume Bars (ML Ready)': ('fetch_volume_bars_cloud', (request.symbol, request.start_date, request.end_date, request.threshold, hf_repo, hf_token)), | |
| 'VPIN (Flow Toxicity)': ('fetch_vpin_cloud', (request.symbol, request.start_date, request.end_date, int(request.threshold or 50), hf_repo, hf_token)), | |
| 'CDF Table (Flow Toxicity)': ('fetch_flow_toxicity_cloud', (request.symbol, request.start_date, request.end_date, int(request.threshold or 50), 50, hf_repo, hf_token)) | |
| } | |
| if request.data_type not in mapping: | |
| raise HTTPException(status_code=400, detail="Unknown data type.") | |
| fn_name, args = mapping[request.data_type] | |
| try: | |
| # SPAWN instead of REMOTE for async execution | |
| fn = modal.Function.from_name("snowsliper", "binance-data-dashboard", fn_name) | |
| call = fn.spawn(*args) | |
| return { | |
| "success": True, | |
| "job_id": call.object_id, | |
| "status": "pending", | |
| "message": "Task started in cloud background." | |
| } | |
| except Exception as e: | |
| print(f"SPAWN ERROR: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| def check_status(job_id: str): | |
| try: | |
| call = FunctionCall.from_id(job_id) | |
| try: | |
| # Attempt to get result without long poll | |
| result = call.get(timeout=0) | |
| return { | |
| "status": "completed", | |
| "result": result | |
| } | |
| except TimeoutError: | |
| # If not ready, it's still running | |
| return {"status": "running"} | |
| except Exception as e: | |
| if "not finished" in str(e).lower(): | |
| return {"status": "running"} | |
| raise e | |
| except Exception as e: | |
| print(f"STATUS ERR: {e}") | |
| return {"status": "error", "message": str(e)} | |