polyscout-web/app/trades/page.tsx

114 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
async function getTrades() {
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, question, bet_direction, entry_price, bet_amount, end_date, status, pnl, created_at FROM paper_trades ORDER BY created_at 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 []; }
}
async function getStats() {
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 status, COUNT(*) FROM paper_trades GROUP BY status"); print(json.dumps(c.fetchall(), ensure_ascii=False)); conn.close()`;
try { return JSON.parse(execSync(`python3 -c ${JSON.stringify(py)}`, { encoding: "utf8" })); }
catch { return []; }
}
const statusMap: Record<string, string> = { open: "进行中", won: "盈利 ✅", lost: "亏损 ❌" };
const statusVariant: Record<string, "default" | "secondary" | "destructive"> = {
open: "secondary", won: "default", lost: "destructive"
};
export default async function TradesPage() {
const [rows, stats] = await Promise.all([getTrades(), getStats()]);
const statsMap = Object.fromEntries(stats.map((s: any[]) => [s[0], s[1]]));
const totalPnl = rows.reduce((sum: number, r: any[]) => sum + (r[7] || 0), 0);
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold"></h1>
<p className="text-muted-foreground text-sm mt-1"> $1</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-4">
<div className="text-sm text-muted-foreground"></div>
<div className="text-2xl font-bold mt-1">{rows.length}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm text-muted-foreground"></div>
<div className="text-2xl font-bold mt-1">{statsMap['open'] || 0}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm text-muted-foreground"></div>
<div className="text-2xl font-bold mt-1 text-green-400">{statsMap['won'] || 0}</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4">
<div className="text-sm text-muted-foreground"></div>
<div className={`text-2xl font-bold mt-1 ${totalPnl >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{totalPnl >= 0 ? '+' : ''}{totalPnl.toFixed(2)}
</div>
</CardContent>
</Card>
</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 hidden md:table-cell"></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>
</tr>
</thead>
<tbody>
{rows.length === 0 ? (
<tr><td colSpan={6} 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 max-w-xs">
<div className="font-mono text-xs text-muted-foreground">{String(r[0]).slice(0, 8)}</div>
<div className="text-xs mt-0.5 truncate max-w-[200px]">{r[1]}</div>
</td>
<td className="px-4 py-3">
<span className={`font-bold text-xs px-2 py-1 rounded ${r[2] === 'YES' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}`}>
{r[2]}
</span>
</td>
<td className="px-4 py-3 font-mono">{(Number(r[3]) * 100).toFixed(1)}¢</td>
<td className="px-4 py-3 font-mono text-xs text-muted-foreground hidden md:table-cell">{String(r[5]).slice(0, 10)}</td>
<td className="px-4 py-3"><Badge variant={statusVariant[r[6]] ?? "secondary"}>{statusMap[r[6]] ?? r[6]}</Badge></td>
<td className="px-4 py-3">
{r[7] != null ? (
<span className={r[7] >= 0 ? 'text-green-400' : 'text-red-400'}>
{r[7] >= 0 ? '+' : ''}{Number(r[7]).toFixed(2)}
</span>
) : <span className="text-muted-foreground">-</span>}
</td>
</tr>
))}
</tbody>
</table>
</div>
</CardContent>
</Card>
</div>
);
}