67 lines
3.6 KiB
TypeScript
67 lines
3.6 KiB
TypeScript
export const dynamic = 'force-dynamic';
|
||
|
||
import { Card, CardContent } from "@/components/ui/card";
|
||
import { Badge } from "@/components/ui/badge";
|
||
|
||
async function getSignals() {
|
||
const DB = "/home/fzq1228/polyscout/data/polyscout.db";
|
||
const { execSync } = await import("child_process");
|
||
const py = `import sqlite3,json; conn=sqlite3.connect('${DB}'); c=conn.cursor(); c.execute("""SELECT market_id, score, expected_edge, risk, created_at FROM signals ORDER BY score DESC LIMIT 50"""); print(json.dumps(c.fetchall(), ensure_ascii=False, default=str)); conn.close()`;
|
||
try { return JSON.parse(execSync(`python3 -c ${JSON.stringify(py)}`, { encoding: "utf8" })); }
|
||
catch { return []; }
|
||
}
|
||
|
||
const riskMap: Record<string, string> = { low: "低", medium: "中", high: "高" };
|
||
const riskVariant: Record<string, "default" | "secondary" | "destructive"> = {
|
||
low: "default", medium: "secondary", high: "destructive"
|
||
};
|
||
|
||
export default async function SignalsPage() {
|
||
const rows = await getSignals();
|
||
return (
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h1 className="text-2xl font-bold">套利信号</h1>
|
||
<p className="text-muted-foreground text-sm mt-1">评分 ≥ 75 自动推送 Discord,按评分排序</p>
|
||
</div>
|
||
<Card>
|
||
<CardContent className="p-0">
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="border-b border-border">
|
||
<th className="text-left px-4 py-3 text-muted-foreground font-medium text-xs uppercase tracking-wider">市场</th>
|
||
<th className="text-left px-4 py-3 text-muted-foreground font-medium text-xs uppercase tracking-wider">评分</th>
|
||
<th className="text-left px-4 py-3 text-muted-foreground font-medium text-xs uppercase tracking-wider">预期收益</th>
|
||
<th className="text-left px-4 py-3 text-muted-foreground font-medium text-xs uppercase tracking-wider">风险</th>
|
||
<th className="text-left px-4 py-3 text-muted-foreground font-medium text-xs uppercase tracking-wider hidden md:table-cell">时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{rows.length === 0 ? (
|
||
<tr><td colSpan={5} className="text-center py-8 text-muted-foreground">暂无信号</td></tr>
|
||
) : rows.map((r: any[], i: number) => (
|
||
<tr key={i} className="border-b border-border last:border-0 hover:bg-accent/50 transition-colors">
|
||
<td className="px-4 py-3 font-mono text-xs text-muted-foreground">{String(r[0]).slice(0, 16)}</td>
|
||
<td className="px-4 py-3">
|
||
<div className="flex items-center gap-2">
|
||
<span className="font-bold">{r[1]}</span>
|
||
<div className="w-16 h-1.5 rounded-full bg-border overflow-hidden">
|
||
<div className={`h-full rounded-full ${r[1] >= 75 ? "bg-green-500" : r[1] >= 60 ? "bg-yellow-500" : "bg-blue-500"}`} style={{ width: `${r[1]}%` }} />
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td className="px-4 py-3 text-green-400 font-medium">+{r[2]}%</td>
|
||
<td className="px-4 py-3"><Badge variant={riskVariant[r[3]] ?? "default"}>{riskMap[r[3]] ?? r[3]}</Badge></td>
|
||
<td className="px-4 py-3 font-mono text-xs text-muted-foreground hidden md:table-cell">{String(r[4]).slice(0, 16)}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|