Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ AI_MODEL=glm-4
NOTIFY_TELEGRAM_BOT_TOKEN=your_bot_token
NOTIFY_TELEGRAM_CHAT_ID=your_chat_id

# ETF 机会提醒邮件配置 - SMTP/QQ邮箱
# 如果用 QQ 邮箱发信,SMTP_PASSWORD 填 QQ 邮箱“授权码”,不是登录密码。
ETF_ALERT_TO=your_qq_number@qq.com
SMTP_HOST=smtp.qq.com
SMTP_PORT=465
SMTP_USER=your_sender@qq.com
SMTP_PASSWORD=your_qq_smtp_authorization_code
SMTP_FROM=your_sender@qq.com

# 代理配置(国内访问 Telegram 可能需要)
# HTTP_PROXY=http://127.0.0.1:7890

Expand Down
1 change: 1 addition & 0 deletions frontend/packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export * from './home'
export * from './paper-trading'
export * from './chat'
export * from './tradingagents'
export * from './watchlist'
46 changes: 46 additions & 0 deletions frontend/packages/api/src/watchlist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { fetchAPI } from './client'

export type WatchDecisionLabel =
| '观察'
| '买入候选'
| '继续持有'
| '加仓观察'
| '减仓警告'
| '止损触发'
| '禁止追高'

export interface WatchlistSignalPayload {
symbol: string
name?: string
market?: string
quote?: Record<string, unknown>
technical?: Record<string, unknown>
position?: Record<string, unknown>
news_flags?: string[]
sector_strength?: number | null
already_no_chase_today?: boolean
}

export interface WatchlistSignalResult {
symbol: string
name: string
market: string
label: WatchDecisionLabel
score: number
reasons: string[]
risks: string[]
confirm_conditions: string[]
invalidation_conditions: string[]
risk_level: string
generated_at: string
}

export const watchlistApi = {
evaluateSignal: (payload: WatchlistSignalPayload) =>
fetchAPI<WatchlistSignalResult>('/watchlist/signals/evaluate', {
method: 'POST',
body: JSON.stringify(payload),
timeoutMs: 30_000,
}),
}

2 changes: 2 additions & 0 deletions frontend/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
packages:
- "packages/*"
allowBuilds:
esbuild: true
5 changes: 4 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useState, useEffect, useRef } from 'react'
import { Routes, Route, NavLink, useLocation, Navigate } from 'react-router-dom'
import { TrendingUp, Bot, ScrollText, Settings, List, Database, Clock, LayoutDashboard, Github, BellRing, Sparkles, Activity } from 'lucide-react'
import { TrendingUp, Bot, ScrollText, Settings, List, Database, Clock, LayoutDashboard, Github, BellRing, Sparkles, Activity, Radar } from 'lucide-react'
import { useTheme } from '@/hooks/use-theme'
import { appApi, fetchAPI, isAuthenticated } from '@panwatch/api'
import DashboardPage from '@/pages/Dashboard'
import OpportunitiesPage from '@/pages/Opportunities'
import WatchlistPage from '@/pages/Watchlist'
import StocksPage from '@/pages/Stocks'
import AgentsPage from '@/pages/Agents'
import SettingsPage from '@/pages/Settings'
Expand All @@ -24,6 +25,7 @@ import { Button } from '@panwatch/base-ui/components/ui/button'

const navItems = [
{ to: '/', icon: LayoutDashboard, label: '首页' },
{ to: '/watchlist', icon: Radar, label: '盯盘' },
{ to: '/portfolio', icon: List, label: '持仓' },
{ to: '/opportunities', icon: Sparkles, label: '机会' },
{ to: '/paper-trading', icon: Activity, label: '模拟盘' },
Expand Down Expand Up @@ -256,6 +258,7 @@ function App() {
<main className="px-4 md:px-6 py-4 md:py-6 w-full">
<Routes>
<Route path="/" element={<DashboardPage />} />
<Route path="/watchlist" element={<WatchlistPage />} />
<Route path="/opportunities" element={<OpportunitiesPage />} />
<Route path="/portfolio" element={<StocksPage />} />
<Route path="/agents" element={<AgentsPage />} />
Expand Down
Loading