diff --git a/backend/src/alphapulse/api/trading.py b/backend/src/alphapulse/api/trading.py index ec308ba..a3340e5 100644 --- a/backend/src/alphapulse/api/trading.py +++ b/backend/src/alphapulse/api/trading.py @@ -32,11 +32,15 @@ SNAPSHOT_FRESH_SECONDS = 30 LIVE_PLAN_STATUSES = ("pending", "open", "closing") -class TodayUsage(BaseModel): +class TodayStats(BaseModel): analyses: int limit: int input_tokens: int output_tokens: int + # 今日结束(平仓)的计划:笔数、净盈亏为正的笔数、净盈亏合计(含手续费) + trades: int + wins: int + net_pnl: Decimal class StatusOut(BaseModel): @@ -50,7 +54,7 @@ class StatusOut(BaseModel): reasoning_effort: str | None # trader 进程的心跳快照;进程离线时为空 snapshot: dict | None - today: TodayUsage + today: TodayStats class ConfigIn(BaseModel): @@ -156,6 +160,10 @@ async def get_status(session: Annotated[AsyncSession, Depends(get_session)]) -> func.coalesce(func.sum(TradingAnalysis.usage["output_tokens"].as_integer()), 0), ).where(TradingAnalysis.started_at >= today) )).one() + # 计划 closed 时 executor.finish 一定写了 result + nets = [Decimal(r["net"]) for r in (await session.scalars( + select(TradingAnalysis.result).where(TradingAnalysis.plan_status == "closed", TradingAnalysis.closed_at >= today) + )).all()] snapshot = await read_snapshot() equity = snapshot and snapshot["account"] and Decimal(snapshot["account"]["margin_balance"]) return StatusOut( @@ -163,7 +171,8 @@ async def get_status(session: Annotated[AsyncSession, Depends(get_session)]) -> running_changed_at=account.running_changed_at, capital_usdt=account.capital_usdt, effective_capital=min(account.capital_usdt, equity) if account.capital_usdt and equity else None, provider_id=account.provider_id, reasoning_effort=account.reasoning_effort, snapshot=snapshot, - today=TodayUsage(analyses=count, limit=DAILY_ANALYSIS_LIMIT, input_tokens=input_tokens, output_tokens=output_tokens), + today=TodayStats(analyses=count, limit=DAILY_ANALYSIS_LIMIT, input_tokens=input_tokens, output_tokens=output_tokens, + trades=len(nets), wins=sum(n > 0 for n in nets), net_pnl=sum(nets, Decimal(0))), ) diff --git a/web/index.html b/web/index.html index 836119e..0fcc12a 100644 --- a/web/index.html +++ b/web/index.html @@ -1,5 +1,5 @@ - + diff --git a/web/package.json b/web/package.json index 7094504..9c2bb46 100644 --- a/web/package.json +++ b/web/package.json @@ -10,6 +10,8 @@ }, "dependencies": { "@element-plus/icons-vue": "^2.3.2", + "@fontsource-variable/jetbrains-mono": "^5.3.0", + "@fontsource-variable/space-grotesk": "^5.3.0", "axios": "^1.20.0", "dayjs": "^1.11.23", "element-plus": "^2.14.7", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index e1b4979..e626b29 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -11,6 +11,12 @@ importers: '@element-plus/icons-vue': specifier: ^2.3.2 version: 2.3.2(vue@3.5.43(typescript@6.0.3)) + '@fontsource-variable/jetbrains-mono': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource-variable/space-grotesk': + specifier: ^5.3.0 + version: 5.3.0 axios: specifier: ^1.20.0 version: 1.20.0(debug@4.4.3) @@ -303,6 +309,12 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@fontsource-variable/jetbrains-mono@5.3.0': + resolution: {integrity: sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw==} + + '@fontsource-variable/space-grotesk@5.3.0': + resolution: {integrity: sha512-2IxmvfB08i9vnGB3Ym/AXvhRE+8XOjWMXIyDum03c+tPwH0FUoMNQfGpU8NXPxjbws0Vvss3AH0Zqt4oJBBAdw==} + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -2889,6 +2901,10 @@ snapshots: '@floating-ui/utils@0.2.12': {} + '@fontsource-variable/jetbrains-mono@5.3.0': {} + + '@fontsource-variable/space-grotesk@5.3.0': {} + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 diff --git a/web/src/apis/trading.ts b/web/src/apis/trading.ts index ed9f345..670177a 100644 --- a/web/src/apis/trading.ts +++ b/web/src/apis/trading.ts @@ -64,7 +64,16 @@ export interface TradingStatus { reasoning_effort: Effort | null /** trader 进程离线时为空 */ snapshot: Snapshot | null - today: { analyses: number, limit: number, input_tokens: number, output_tokens: number } + today: { + analyses: number + limit: number + input_tokens: number + output_tokens: number + /** 今日结束的计划笔数、其中净盈亏为正的笔数、净盈亏合计(含手续费) */ + trades: number + wins: number + net_pnl: string + } } export interface TradingSymbol { diff --git a/web/src/components/SkillTree.vue b/web/src/components/SkillTree.vue index 44271fd..44f5142 100644 --- a/web/src/components/SkillTree.vue +++ b/web/src/components/SkillTree.vue @@ -126,8 +126,8 @@ function onCollapse(item: TreeItem) { >