Recreate Next.js source — shadcn components, chart, types, hooks
Source files were untracked and lost during reset. Recreated: - page.tsx (full 3-tab dashboard), layout.tsx, globals.css (shadcn theme) - types.ts, api.ts, utils.ts - strategy-card.tsx, equity-chart.tsx, positions-panel.tsx - ui/ (7 shadcn wrappers: card, badge, button, table, tabs, sheet, collapsible, select) - Config files: next.config.ts, tsconfig.json, postcss.config.mjs, components.json
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { LiveMetrics, PaperMetrics } from "./types";
|
||||
|
||||
const API_BASE = "/cv/api";
|
||||
|
||||
export function useLiveMetrics() {
|
||||
const [data, setData] = useState<LiveMetrics | null>(null);
|
||||
const [connected, setConnected] = useState(false);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const WS_BASE = `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/cv/ws`;
|
||||
|
||||
function connect() {
|
||||
try {
|
||||
const ws = new WebSocket(WS_BASE);
|
||||
wsRef.current = ws;
|
||||
ws.onopen = () => setConnected(true);
|
||||
ws.onclose = () => {
|
||||
setConnected(false);
|
||||
timerRef.current = setTimeout(connect, 5000);
|
||||
};
|
||||
ws.onmessage = (e) => {
|
||||
try {
|
||||
const d = JSON.parse(e.data) as LiveMetrics;
|
||||
setData(d);
|
||||
} catch { /* ignore */ }
|
||||
};
|
||||
} catch {
|
||||
timerRef.current = setTimeout(connect, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
connect();
|
||||
return () => {
|
||||
wsRef.current?.close();
|
||||
clearTimeout(timerRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { data, connected };
|
||||
}
|
||||
|
||||
export function usePaperMetrics() {
|
||||
const [data, setData] = useState<PaperMetrics | null>(null);
|
||||
const [connected, setConnected] = useState(false);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const base = `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/cv/ws/paper`;
|
||||
const connect = () => {
|
||||
try {
|
||||
const ws = new WebSocket(base);
|
||||
wsRef.current = ws;
|
||||
ws.onopen = () => setConnected(true);
|
||||
ws.onclose = () => {
|
||||
setConnected(false);
|
||||
setTimeout(connect, 5000);
|
||||
};
|
||||
ws.onmessage = (e) => {
|
||||
try {
|
||||
const d = JSON.parse(e.data) as PaperMetrics;
|
||||
setData(d);
|
||||
} catch { /* ignore */ }
|
||||
};
|
||||
} catch {
|
||||
setTimeout(connect, 5000);
|
||||
}
|
||||
};
|
||||
connect();
|
||||
return () => wsRef.current?.close();
|
||||
}, []);
|
||||
|
||||
return { data, connected };
|
||||
}
|
||||
|
||||
export async function fetchHistorical(): Promise<Record<string, import("./types").BacktestSummary>> {
|
||||
const res = await fetch(`${API_BASE}/backtests/historical`);
|
||||
const list: import("./types").BacktestSummary[] = await res.json();
|
||||
const byStrat: Record<string, import("./types").BacktestSummary> = {};
|
||||
for (const b of list) {
|
||||
if (!byStrat[b.strategy]) byStrat[b.strategy] = b;
|
||||
}
|
||||
return byStrat;
|
||||
}
|
||||
|
||||
export async function fetchBacktestDetail(name: string): Promise<import("./types").BacktestFull> {
|
||||
const res = await fetch(`${API_BASE}/backtest/historical/${encodeURIComponent(name)}`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function recalcBacktest(
|
||||
name: string,
|
||||
feeTier: number,
|
||||
stakingTier: string,
|
||||
): Promise<import("./types").BacktestFull> {
|
||||
const res = await fetch(
|
||||
`${API_BASE}/backtest/${encodeURIComponent(name)}/recalc?fee_tier=${feeTier}&staking_tier=${stakingTier}`,
|
||||
);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// ═══════════ FTDT Quant Lab — Type Definitions ═══════════
|
||||
|
||||
export interface Strategy {
|
||||
allocation: number;
|
||||
instrument: string;
|
||||
pnl: number;
|
||||
pnl_pct: number;
|
||||
position: number;
|
||||
trades_today: number;
|
||||
wins: number;
|
||||
win_rate: number;
|
||||
status: "running" | "idle";
|
||||
size: number;
|
||||
fee_paid: number;
|
||||
fee_model: "maker" | "taker";
|
||||
type: string;
|
||||
description: string;
|
||||
signals: { signal: string; ts: number }[];
|
||||
max_position?: number;
|
||||
stop_loss?: number;
|
||||
entry_price?: number;
|
||||
}
|
||||
|
||||
export interface Trade {
|
||||
time: string;
|
||||
strategy: string;
|
||||
side: string;
|
||||
size: number;
|
||||
price: number;
|
||||
pnl: number;
|
||||
fee: number;
|
||||
reason?: string;
|
||||
pnl_net?: number;
|
||||
pnl_gross?: number;
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
strategy: string;
|
||||
size: number;
|
||||
entry_px: number;
|
||||
pnl?: number;
|
||||
}
|
||||
|
||||
export interface Order {
|
||||
coin: string;
|
||||
side: "B" | "A";
|
||||
sz: number;
|
||||
limitPx?: string;
|
||||
cloid?: string;
|
||||
}
|
||||
|
||||
export interface LiveMetrics {
|
||||
timestamp: number;
|
||||
wallet: string;
|
||||
total_equity: number;
|
||||
base_equity: number;
|
||||
total_pnl: number;
|
||||
total_pnl_pct: number;
|
||||
reserve: number;
|
||||
equity_history: { t: number; v: number }[];
|
||||
strategy_equity: Record<string, { t: number; v: number }[]>;
|
||||
strategies: Record<string, Strategy>;
|
||||
trades: Trade[];
|
||||
status: string;
|
||||
testnet_up: boolean;
|
||||
open_positions: Position[];
|
||||
open_orders: Order[];
|
||||
}
|
||||
|
||||
export interface PaperMetrics {
|
||||
timestamp: number;
|
||||
total_equity: number;
|
||||
base_equity: number;
|
||||
total_pnl: number;
|
||||
total_pnl_pct: number;
|
||||
regime: string;
|
||||
equity_history: { t: number; v: number }[];
|
||||
strategy_equity: Record<string, { t: number; v: number }[]>;
|
||||
strategies: Record<string, Strategy>;
|
||||
per_strategy_trades: Record<string, Trade[]>;
|
||||
open_positions: Position[];
|
||||
open_orders: Order[];
|
||||
}
|
||||
|
||||
export interface BacktestSummary {
|
||||
name: string;
|
||||
strategy: string;
|
||||
start: string;
|
||||
end: string;
|
||||
sharpe: number;
|
||||
sortino: number;
|
||||
pnl_pct: number;
|
||||
max_dd: number;
|
||||
win_rate: number;
|
||||
total_trades: number;
|
||||
coin?: string;
|
||||
}
|
||||
|
||||
export interface BacktestFull {
|
||||
name?: string;
|
||||
strategy: string;
|
||||
pnl_net?: number;
|
||||
pnl_gross?: number;
|
||||
pnl_pct?: number;
|
||||
pnl_net_pct?: number;
|
||||
pnl_gross_pct?: number;
|
||||
pnl: number;
|
||||
sharpe: number;
|
||||
sortino: number;
|
||||
max_dd: number;
|
||||
win_rate: number;
|
||||
total_trades: number;
|
||||
fees_total: number;
|
||||
num_periods: number;
|
||||
fee_model: string;
|
||||
equity_curve: { t: number | string; v: number }[];
|
||||
trades: Trade[];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
Reference in New Issue
Block a user