Vycka12's picture
Upload app.py with huggingface_hub
c7ff124 verified
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)"
@app.get("/")
def health():
return {"status": "ok", "version": "2026-03-11-v2", "message": "Binance Data Dashboard API on Hugging Face"}
@app.get("/api/symbols")
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)}
@app.post("/api/download")
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))
@app.get("/api/status/{job_id}")
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)}