Spaces:
Running on Zero
Running on Zero
deveshc23
feat: Add Free Gradio Space support, Git LFS tracking for data artifacts, and Quant Allocator
3e33e2e Download frontend/src/api/client.ts from deveshc23/drift-quant: direct link, hf CLI and curl.
- Browser
- Download file 6.4 kB
-
https://huggingface.co/spaces/deveshc23/drift-quant/resolve/main/frontend/src/api/client.ts
- Command line
-
hf download hf://spaces/deveshc23/drift-quant/frontend/src/api/client.ts
-
curl -L -o client.ts https://huggingface.co/spaces/deveshc23/drift-quant/resolve/main/frontend/src/api/client.ts
6.4 kB
| import type { | |
| AttributionItem, | |
| BacktestRunRequest, | |
| CoverageTimeline, | |
| DatasetSummary, | |
| DrawdownPoint, | |
| ExperimentDetail, | |
| ExperimentSummary, | |
| Health, | |
| MarketBarItem, | |
| MonthlyReturns, | |
| PortfolioAllocationRequest, | |
| PortfolioAllocationResponse, | |
| SeriesPoint, | |
| StrategyCatalogItem, | |
| SymbolCatalogItem, | |
| TradePageResponse, | |
| } from "./types"; | |
| async function request<T>(path: string, options: RequestInit = {}): Promise<T> { | |
| const response = await fetch(path, { | |
| ...options, | |
| headers: { | |
| "Content-Type": "application/json", | |
| ...(options.headers || {}), | |
| }, | |
| }); | |
| if (!response.ok) { | |
| let errorDetail = `HTTP ${response.status}`; | |
| try { | |
| const errorJson = await response.json(); | |
| if (errorJson.detail) { | |
| errorDetail = typeof errorJson.detail === "string" ? errorJson.detail : JSON.stringify(errorJson.detail); | |
| } | |
| } catch { | |
| // ignore json parse error on non-json error responses | |
| } | |
| throw new Error(errorDetail); | |
| } | |
| return response.json() as Promise<T>; | |
| } | |
| export const api = { | |
| // System | |
| health: () => request<Health>("/health"), | |
| // Dataset | |
| datasetSummary: () => request<DatasetSummary>("/api/v1/dataset/summary"), | |
| symbols: (search?: string, sector?: string) => { | |
| const params = new URLSearchParams(); | |
| if (search) params.append("search", search); | |
| if (sector && sector !== "ALL") params.append("sector", sector); | |
| const qs = params.toString() ? `?${params.toString()}` : ""; | |
| return request<SymbolCatalogItem[]>(`/api/v1/dataset/symbols${qs}`); | |
| }, | |
| coverageTimeline: () => request<CoverageTimeline>("/api/v1/dataset/coverage-timeline"), | |
| // Market Data | |
| bars: ( | |
| symbol: string, | |
| start?: string, | |
| end?: string, | |
| resolution: "15m" | "1h" | "1d" = "15m", | |
| limit = 1500 | |
| ) => { | |
| const params = new URLSearchParams({ | |
| symbol, | |
| resolution, | |
| limit: limit.toString(), | |
| }); | |
| if (start) params.append("start", start); | |
| if (end) params.append("end", end); | |
| return request<MarketBarItem[]>(`/api/v1/market/bars?${params.toString()}`); | |
| }, | |
| // Strategies & Backtesting | |
| strategies: () => request<StrategyCatalogItem[]>("/api/v1/strategies"), | |
| runBacktest: (payload: BacktestRunRequest) => | |
| request<ExperimentDetail>("/api/v1/backtest/run", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }), | |
| streamBacktest: async ( | |
| payload: BacktestRunRequest, | |
| onProgress: (progress: { | |
| progress_pct: number; | |
| current_date: string; | |
| current_equity: number; | |
| trades_count: number; | |
| point?: { timestamp: string; equity: number }; | |
| last_trade?: { symbol: string; net_pnl: number; exit_time: string; exit_reason: string }; | |
| }) => void, | |
| onComplete: (detail: ExperimentDetail) => void, | |
| onError: (err: Error) => void | |
| ) => { | |
| try { | |
| const response = await fetch("/api/v1/backtest/stream", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify(payload), | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP ${response.status}`); | |
| } | |
| const reader = response.body?.getReader(); | |
| if (!reader) throw new Error("ReadableStream not supported"); | |
| const decoder = new TextDecoder(); | |
| let buffer = ""; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| buffer += decoder.decode(value, { stream: true }); | |
| const lines = buffer.split("\n\n"); | |
| buffer = lines.pop() || ""; | |
| for (const block of lines) { | |
| const line = block.trim(); | |
| if (line.startsWith("data: ")) { | |
| try { | |
| const data = JSON.parse(line.slice(6)); | |
| if (data.type === "progress") { | |
| onProgress(data); | |
| } else if (data.type === "complete") { | |
| onComplete(data); | |
| } | |
| } catch (e) { | |
| console.error("Error parsing stream chunk:", e); | |
| } | |
| } | |
| } | |
| } | |
| } catch (err: any) { | |
| onError(err instanceof Error ? err : new Error(String(err))); | |
| } | |
| }, | |
| // Experiments | |
| experiments: (offset = 0, limit = 50) => | |
| request<{ items: ExperimentSummary[]; total: number }>( | |
| `/api/v1/experiments?offset=${offset}&limit=${limit}` | |
| ), | |
| experiment: (id: string) => | |
| request<ExperimentDetail>(`/api/v1/experiments/${encodeURIComponent(id)}`), | |
| equity: (id: string, maxPoints = 1500) => | |
| request<SeriesPoint[]>( | |
| `/api/v1/experiments/${encodeURIComponent(id)}/equity?maximum_points=${maxPoints}` | |
| ), | |
| drawdown: (id: string, maxPoints = 1500) => | |
| request<DrawdownPoint[]>( | |
| `/api/v1/experiments/${encodeURIComponent(id)}/drawdown?maximum_points=${maxPoints}` | |
| ), | |
| trades: ( | |
| id: string, | |
| options: { | |
| symbol?: string; | |
| outcome?: "win" | "loss"; | |
| exitReason?: string; | |
| offset?: number; | |
| limit?: number; | |
| } = {} | |
| ) => { | |
| const params = new URLSearchParams({ | |
| offset: (options.offset ?? 0).toString(), | |
| limit: (options.limit ?? 100).toString(), | |
| }); | |
| if (options.symbol) params.append("symbol", options.symbol); | |
| if (options.outcome) params.append("outcome", options.outcome); | |
| if (options.exitReason) params.append("exit_reason", options.exitReason); | |
| return request<TradePageResponse>( | |
| `/api/v1/experiments/${encodeURIComponent(id)}/trades?${params.toString()}` | |
| ); | |
| }, | |
| attribution: (id: string) => | |
| request<AttributionItem[]>(`/api/v1/experiments/${encodeURIComponent(id)}/attribution`), | |
| monthly: (id: string) => | |
| request<MonthlyReturns>(`/api/v1/experiments/${encodeURIComponent(id)}/monthly`), | |
| compare: (ids: string[]) => { | |
| const params = new URLSearchParams(); | |
| ids.forEach((id) => params.append("ids", id)); | |
| return request<ExperimentSummary[]>(`/api/v1/experiments/compare?${params.toString()}`); | |
| }, | |
| // Portfolio Allocation | |
| allocatePortfolio: (req: PortfolioAllocationRequest) => | |
| request<PortfolioAllocationResponse>("/api/v1/portfolio/allocate", { | |
| method: "POST", | |
| body: JSON.stringify(req), | |
| }), | |
| // Export URLs | |
| exportTradesCsvUrl: (id: string) => | |
| `/api/v1/reports/${encodeURIComponent(id)}/export-csv?kind=trades`, | |
| exportEquityCsvUrl: (id: string) => | |
| `/api/v1/reports/${encodeURIComponent(id)}/export-csv?kind=equity`, | |
| }; | |