import {StockIdentity} from "./StockIdentity" import {useEffect,useRef,useState} from "react" import {ArrowUpRight,RefreshCw} from "lucide-react" import {request,ApiError} from "@/lib/auth" import type {Candidate} from "@/lib/candidates" import {Button} from "./ui/button" import {Badge} from "./ui/badge" import {Card,CardHeader,CardTitle,CardContent} from "./ui/card" import {StockHistoryChart} from "./StockHistoryChart" import {ScreeningRunControl} from "./ScreeningRunControl" type Stock={id:string;name:string;symbol:string;exchange:string;price:string;observed_at:string} type Sync={id:string;status:string;instrument_count:number;finished_at:string;message:string} type Strategy={id:string;status:string;name:string} type Data={stocks:Stock[];candidates:Candidate[];syncs:Sync[];strategies:Strategy[]} export function TradingOverview({onSessionExpired,onNavigate}:{onSessionExpired:()=>void;onNavigate:(page:string,symbol?:string,candidate?:string)=>void}){ const [data,setData]=useState(null),[error,setError]=useState(""),[loading,setLoading]=useState(false),[selected,setSelected]=useState(null) const controller=useRef(null) async function refresh(){controller.current?.abort();const c=new AbortController();controller.current=c;setLoading(true);try{const [stocks,candidates,syncs,strategies]=await Promise.all([request("/api/admin/v1/instruments",{signal:c.signal}),request("/api/admin/v1/candidates",{signal:c.signal}),request("/api/admin/v1/market/syncs",{signal:c.signal}),request("/api/admin/v1/strategies",{signal:c.signal})]);if(c.signal.aborted)return;setData({stocks,candidates,syncs,strategies});setSelected(current=>current??stocks[0]??null);setError("")}catch(e){if(!c.signal.aborted){if(e instanceof ApiError&&e.status===401)onSessionExpired();else setError(e instanceof Error?e.message:"股票数据读取失败")}}finally{if(!c.signal.aborted)setLoading(false)}} useEffect(()=>{void refresh();return()=>controller.current?.abort()},[]) const success=data?.syncs.find(x=>x.status==="COMPLETED"),pending=data?.candidates.filter(c=>c.status==="PENDING_REVIEW")??[],published=data?.candidates.filter(c=>c.is_active_publication)??[] return
沪深 A 股公开行情 · 开发研究
{error&&

{error}

}
{[{title:"已同步股票",value:success?.instrument_count??"—",note:success?`最近成功同步 ${new Date(success.finished_at).toLocaleTimeString("zh-CN")}`:"等待行情源同步",page:"股票行情"},{title:"待审核股票候选",value:data?pending.length:"—",note:"最近 100 条候选修订",page:"候选审核"},{title:"有效发布候选",value:data?published.length:"—",note:"最近 100 条候选修订",page:"候选审核"},{title:"已启用研究策略",value:data?data.strategies.filter(s=>s.status==="ACTIVE").length:"—",note:"最近 100 个策略 · 非收益指标",page:"策略中心"}].map(x=>)}
void refresh()}/>
股票观察

按证券代码排序的行情样本 · 非涨幅排名或投资推荐

{selected&&

{selected.exchange} · {selected.symbol}

¥{selected.price}

报价 {new Date(selected.observed_at).toLocaleString("zh-CN")}

}
{data?.stocks.slice(0,8).map(stock=>)}
股票 / 代码最新报价行情时间
¥{stock.price}{new Date(stock.observed_at).toLocaleTimeString("zh-CN")}
{data?.stocks.length===0&&

暂无股票快照,请在行情源设置中检查同步配置。

}
待审核信号

从股票、参考区间与信号依据开始审核

{pending.length===0&&
当前没有待审核股票。点击“立即自动筛选”后,真实日线命中结果会自动进入这里;不符合规则的股票不会生成候选。
}{pending.slice(0,8).map(c=>)}

价格按供应商观察时间展示,收盘后保留收盘快照。曲线来自实际未复权日线;当前未接入指数、涨跌幅、成交额与行业分类,因此不展示相应统计。

}