File size: 4,131 Bytes
c426a07
 
a38d5ba
c6390ac
a38d5ba
 
c426a07
 
 
c6390ac
 
c426a07
 
c6390ac
c426a07
 
c6390ac
 
 
c426a07
 
 
c6390ac
c426a07
 
c6390ac
c426a07
 
 
 
 
c6390ac
c426a07
 
 
c6390ac
c426a07
 
 
 
 
 
 
 
 
c6390ac
c426a07
 
 
 
 
 
 
 
 
 
 
 
 
c6390ac
c426a07
 
 
 
 
c6390ac
c426a07
 
 
c6390ac
 
c426a07
 
c6390ac
 
 
 
c426a07
c6390ac
 
 
c426a07
 
c6390ac
 
 
c426a07
 
9dfe6eb
 
 
 
 
 
 
 
 
 
c426a07
 
c6390ac
c426a07
c6390ac
 
c426a07
 
a38d5ba
c426a07
 
c6390ac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import os
import sys

# Force UTF-8 stdout encoding for Windows terminal compatibility
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
from dotenv import load_dotenv

# Load .env variables (MODAL_TOKEN_ID, MODAL_TOKEN_SECRET)
load_dotenv()

# Import Modal cloud functions
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import modal

app = FastAPI(title="Binance Data Dashboard API")

# CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Binance client for symbol list only
try:
    binance = ccxt.binance()
except Exception as e:
    print(f"Warning: Could not init Binance client: {e}")

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)"
    datasetType: Optional[str] = None

@app.get("/api/symbols")
def get_symbols():
    try:
        markets = binance.load_markets()
        symbols = sorted(list(markets.keys()))
        return {"symbols": symbols}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/api/download")
def download_data(request: DownloadRequest):
    print(f"\n{'='*60}")
    print(f"[CLOUD] Download request:")
    print(f"   Symbol: {request.symbol}")
    print(f"   Interval: {request.interval}")
    print(f"   Data Type: {request.data_type}")
    print(f"   Start: {request.start_date}")
    print(f"   End: {request.end_date}")
    print(f"   Threshold: {request.threshold}")
    print(f"{'='*60}")
    
    try:
        # Reference the deployed Modal app
        modal_app = modal.App.lookup("binance-data-dashboard")
        
        if request.data_type == 'Klines (OHLCV)':
            print("[CLOUD] Calling fetch_klines_cloud...")
            fn = modal.Function.from_name("binance-data-dashboard", "fetch_klines_cloud")
            result = fn.remote(request.symbol, request.interval, request.start_date, request.end_date)
            
        elif request.data_type == 'Liquidations':
            print("[CLOUD] Calling fetch_liquidations_cloud...")
            fn = modal.Function.from_name("binance-data-dashboard", "fetch_liquidations_cloud")
            result = fn.remote(request.symbol, request.start_date, request.end_date)

        elif request.data_type == 'AggTrades':
            print("[CLOUD] Calling fetch_aggtrades_cloud...")
            fn = modal.Function.from_name("binance-data-dashboard", "fetch_aggtrades_cloud")
            result = fn.remote(request.symbol, request.start_date, request.end_date)

        elif request.data_type == 'Dollar Bars (ML Ready)':
            print(f"[CLOUD] Calling fetch_dollar_bars_cloud (threshold: {request.threshold})...")
            fn = modal.Function.from_name("binance-data-dashboard", "fetch_dollar_bars_cloud")
            result = fn.remote(request.symbol, request.start_date, request.end_date, request.threshold)

        elif request.data_type == 'VPIN (Flow Toxicity)':
            print(f"[CLOUD] Calling fetch_vpin_cloud (buckets: {request.threshold})...")
            # Using threshold field temporarily to pass buckets_per_day or just hardcode
            fn = modal.Function.from_name("binance-data-dashboard", "fetch_vpin_cloud")
            # In UI we'll use threshold field for buckets per day
            result = fn.remote(request.symbol, request.start_date, request.end_date, int(request.threshold))

        else:
            raise HTTPException(status_code=400, detail="Unknown data type.")

        print(f"[CLOUD] Result received: success={result.get('success')}, rows={result.get('row_count', 0)}")
        return result
            
    except Exception as e:
        print(f"KLAIDA: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

# Run with: uvicorn backend.api:app --reload