"use client";
import { useEffect, useState, useCallback } from "react";
import { authFetch } from "@/lib/auth";
import { useAuth } from "@/lib/auth";
import Link from "next/link";
import {
TrendingUp,
TrendingDown,
Minus,
Clock,
Activity,
AlertCircle,
CheckCircle,
PauseCircle,
} from "lucide-react";
interface StrategyCard {
id: string;
display_name: string;
status: "running" | "paused" | "error";
started_at: number;
initial_balance: number;
current_balance: number;
net_usdt: number;
net_r: number;
trade_count: number;
win_rate: number;
avg_win_r: number;
avg_loss_r: number;
open_positions: number;
pnl_usdt_24h: number;
pnl_r_24h: number;
std_r: number;
last_trade_at: number | null;
}
function formatDuration(ms: number): string {
const totalSec = Math.floor((Date.now() - ms) / 1000);
const d = Math.floor(totalSec / 86400);
const h = Math.floor((totalSec % 86400) / 3600);
const m = Math.floor((totalSec % 3600) / 60);
if (d > 0) return `${d}天 ${h}小时`;
if (h > 0) return `${h}小时 ${m}分`;
return `${m}分钟`;
}
function formatTime(ms: number | null): string {
if (!ms) return "—";
const d = new Date(ms);
return d.toLocaleString("zh-CN", {
timeZone: "Asia/Shanghai",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
}
function StatusBadge({ status }: { status: string }) {
if (status === "running") {
return (
运行中
);
}
if (status === "paused") {
return (
已暂停
);
}
return (
异常
);
}
function StrategyCardComponent({ s }: { s: StrategyCard }) {
const isProfit = s.net_usdt >= 0;
const is24hProfit = s.pnl_usdt_24h >= 0;
const balancePct = ((s.current_balance / s.initial_balance) * 100).toFixed(1);
return (
{/* Header */}
{s.display_name}
已运行 {formatDuration(s.started_at)}
{isProfit ? "+" : ""}{s.net_usdt.toLocaleString()} U
{balancePct}% 余额
{/* Balance Bar */}
当前余额
{s.current_balance.toLocaleString()} / {s.initial_balance.toLocaleString()} USDT
= s.initial_balance ? "bg-emerald-500" : "bg-red-500"}`}
style={{ width: `${Math.min(100, Math.max(0, (s.current_balance / s.initial_balance) * 100))}%` }}
/>
{/* Stats Grid */}
胜率
= 50 ? "text-emerald-400" : s.win_rate >= 45 ? "text-yellow-400" : "text-red-400"}`}>
{s.win_rate}%
净R
= 0 ? "text-emerald-400" : "text-red-400"}`}>
{s.net_r >= 0 ? "+" : ""}{s.net_r}R
{/* P&L Ratio */}
{/* 24h & Last Trade */}
{is24hProfit ? (
) : (
)}
24h {is24hProfit ? "+" : ""}{s.pnl_usdt_24h.toLocaleString()} U ({is24hProfit ? "+" : ""}{s.pnl_r_24h}R)
{s.open_positions > 0 ? (
{s.open_positions}仓持仓中
) : (
上次: {formatTime(s.last_trade_at)}
)}
);
}
export default function StrategyPlazaPage() {
const { token } = useAuth();
const [strategies, setStrategies] = useState
([]);
const [loading, setLoading] = useState(true);
const [lastUpdated, setLastUpdated] = useState(null);
const fetchData = useCallback(async () => {
try {
const res = await authFetch("/api/strategy-plaza");
const data = await res.json();
setStrategies(data.strategies || []);
setLastUpdated(new Date());
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 30000);
return () => clearInterval(interval);
}, [fetchData]);
if (loading) {
return (
);
}
return (
{/* Header */}
{lastUpdated ? (
<>
{lastUpdated.toLocaleTimeString("zh-CN", { timeZone: "Asia/Shanghai", hour12: false })} 更新
>
) : null}
{/* Strategy Cards */}
{strategies.map((s) => (
))}
{strategies.length === 0 && (
暂无策略数据
)}
);
}