### [Continue](https://continue.dev) session transcript Exported: 6/13/2026, 4:17:09 PM #### _User_ > 你是什么模型 版本号多少 #### _Assistant_ > 我是 Claude,由 Anthropic 开发,具体是 Claude Opus 4.6(模型 ID:claude-opus-4-6)。有什么可以帮你的? #### _User_ > 帮我分析一下当前项目 有没有什么优化的功能 #### _Assistant_ > 我先来了解一下你的项目结构和内容。 > > #### _Assistant_ > .git/ > .gitignore > backend/ > prototype/ > README.md > 功能架构.md > 架构总结.md #### _Assistant_ > 让我先看看项目的架构文档和主要代码结构。 > > #### _Assistant_ > # 智策 StockTerminal > > > > 个人/小团队 A 股分析·复盘·智能辅助系统。后端提供行情、回测、AI 诊断与定时任务;前端为纯 HTML + ECharts 原型界面,由 FastAPI 统一托管。 > > > > ## 功能概览 > > > > | 模块 | 能力 | > > |---|---| > > | 大盘行情 | 三大指数、情绪温度计、板块云图、热股榜、龙虎榜 | > > | 自选股 | 自选列表、内置策略选股、多因子条件过滤 | > > | 复盘中心 | 每日复盘、AI 七段式日报、个股 K 线回放 | > > | 策略与中台 | MA 交叉回测、数据入库状态、任务日志 | > > | AI 分析 | 个股诊断(6 维证据链)、信号胜率、预测留痕与核验 | > > | 组合交易 | 持仓 P&L、资金曲线、交易日志、盈亏归因 | > > | 智能预警 | 价格/涨跌幅规则、多通道推送、触发记录 | > > | 资讯中心 | 财经快讯、情绪标注、自选相关资讯 | > > > > 更完整的架构说明见 [架构总结.md](./架构总结.md)。 > > > > ## 技术栈 > > > > - **前端**:HTML + CSS + 原生 JS,ECharts 5(CDN) > > - **后端**:Python 3.12 · FastAPI · uvicorn > > - **数据库**:PostgreSQL · SQLAlchemy 2.0 > > - **数据源**:AkShare(行情/情绪/资讯),Sina 实时报价 > > - **调度**:APScheduler > > - **AI**:OpenAI 兼容接口(DeepSeek / 通义 / Kimi 等),无 Key 时规则降级 > > > > ## 项目结构 > > > > ``` > > stock_cs/ > > ├── backend/ # FastAPI 后端 > > │ ├── main.py # API 入口 + 静态文件托管 > > │ ├── cli.py # 建库 / 入库命令行工具 > > │ ├── .env.example # 环境变量模板 > > │ └── requirements.txt > > ├── prototype/ # 前端原型(index.html / app.js / style.css) > > ├── 架构总结.md > > └── 功能架构.md > > ``` > > > > ## 环境要求 > > > > - Python 3.12+ > > - PostgreSQL 14+(本地或远程均可) > > - 可选:大模型 API Key、推送渠道密钥(见下方配置) > > > > ## 快速开始 > > > > ### 1. 安装依赖 > > > > ```bash > > cd backend > > python -m venv .venv > > > > # Windows > > .venv\Scripts\activate > > > > # Linux / macOS > > source .venv/bin/activate > > > > pip install -r requirements.txt > > ``` > > > > ### 2. 配置环境变量 > > > > ```bash > > cp .env.example .env > > ``` > > > > 编辑 `backend/.env`,至少确认 PostgreSQL 连接信息(也可通过环境变量 `PG_USER` / `PG_PASSWORD` / `PG_HOST` / `PG_PORT` / `PG_DB` 设置): > > > > ```env > > PG_USER=postgres > > PG_PASSWORD=your_password > > PG_HOST=localhost > > PG_PORT=5432 > > PG_DB=stock_cs > > ``` > > > > 可选:填入 `LLM_API_KEY` 启用大模型分析;填入 SMTP / Server酱 / 企业微信 / PushPlus 启用推送。 > > > > ### 3. 初始化数据库并入库 > > > > ```bash > > cd backend > > > > # 建库建表 > > python cli.py init > > > > # 抓取当日板块/资金流/情绪/龙虎榜等快照 > > python cli.py ingest > > > > # 全市场日线历史入库(默认 250 交易日,耗时较长) > > python cli.py ingest_all > > python cli.py ingest_all 500 # 指定天数 > > ``` > > > > 指定股票入库: > > > > ```bash > > python cli.py ingest 600519 000001 > > ``` > > > > ### 4. 启动服务 > > > > ```bash > > cd backend > > python main.py > > ``` > > > > 浏览器访问:**http://127.0.0.1:8000** > > > > 健康检查:`GET /api/health` > > > > ## 定时任务 > > > > 服务启动后,APScheduler 会在工作日自动执行(可在 `config.py` 或环境变量中调整时间): > > > > | 任务 | 默认时间 | 说明 | > > |---|---|---| > > | `daily_ingest` | 15:35 | 收盘后增量入库 | > > | `alert_check` | 每 60 秒 | 实时报价预警核查 | > > | `daily_report` | 15:45 | 生成 AI 复盘日报并推送 | > > | `verify_pred` | 15:50 | 核验到期 AI 预测 | > > | `signal_stats` | 周六 09:00 | 全市场信号胜率回测 | > > > > ## 推送渠道 > > > > 在 `.env` 中配置任意一种即可启用,互不依赖: > > > > | 渠道 | 配置项 | > > |---|---| > > | SMTP 邮件 | `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASSWORD` / `SMTP_TO` | > > | Server酱 | `SERVERCHAN_KEY` | > > | 企业微信 | `WECOM_WEBHOOK` | > > | PushPlus | `PUSHPLUS_TOKEN` | > > > > ## 开发说明 > > > > - 前端静态资源由 `main.py` 挂载 `prototype/` 目录,修改前端后刷新浏览器即可。 > > - 自选股列表持久化在 `backend/watchlist.json`。 > > - AkShare 不可用时部分接口会降级为 mock 数据,详见 `/api/health` 中的 `akshare` 字段。 > > - 敏感文件(`.env`、虚拟环境等)已在 `.gitignore` 中排除,请勿提交密钥。 > > > > ## 文档 > > > > - [架构总结.md](./架构总结.md) — 分层设计、数据模型、AI 分析流程 > > - [功能架构.md](./功能架构.md) — 功能模块说明 > > > > ## 许可证 > > > > 本项目仅供学习与研究使用。行情数据来源于第三方公开接口,请遵守相应数据源的使用条款。 > > #### _Assistant_ > # 智策 StockTerminal — 架构总结 > > > > > 版本:当前实现态(2026-06) > > > 定位:个人/小团队 A 股分析·复盘·智能辅助系统 > > > > --- > > > > ## 一、整体分层 > > > > ``` > > ┌─────────────────────────────────────────────────────────────┐ > > │ 展示层(前端) │ > > │ HTML + ECharts 5 + 原生 JS │ > > │ prototype/index.html · style.css · app.js │ > > └────────────────────────┬────────────────────────────────────┘ > > │ HTTP / REST > > ┌────────────────────────▼────────────────────────────────────┐ > > │ 服务层(后端 FastAPI) │ > > │ main.py —— 50+ API 端点,静态文件托管 │ > > │ │ > > │ 数据服务 akshare_service.py (行情·情绪·资讯·实时报价) │ > > │ AI 分析 ai.py (证据链·置信度·LLM/规则) │ > > │ 信号统计 signals.py (胜率回测·预测留痕·核验) │ > > │ RAG 检索 rag.py (资讯检索·情绪标注) │ > > │ 复盘日报 report.py (结构化日报·推送) │ > > │ 组合计算 portfolio.py (持仓·P&L·资金曲线) │ > > │ 回测引擎 backtest.py (MA交叉·净值曲线) │ > > │ 预警引擎 alerts.py (实时报价判断·触发事件) │ > > │ 推送通知 notifier.py (SMTP·Server酱·企微·PP) │ > > │ 大模型客户端 llm.py (OpenAI 兼容接口) │ > > │ 定时调度 scheduler.py (APScheduler 5 个任务) │ > > │ ETL 入库 ingest.py (AkShare→PostgreSQL) │ > > │ CLI 工具 cli.py (init/ingest/ingest_all) │ > > │ 配置 config.py + .env (DB·LLM·推送密钥) │ > > └────────────────────────┬────────────────────────────────────┘ > > │ SQLAlchemy ORM / psycopg2 > > ┌────────────────────────▼────────────────────────────────────┐ > > │ 数据层(PostgreSQL) │ > > │ 13 张业务表,见下方数据模型 │ > > └─────────────────────────────────────────────────────────────┘ > > ``` > > > > --- > > > > ## 二、后端模块职责 > > > > | 文件 | 职责 | 关键依赖 | > > |---|---|---| > > | `main.py` | FastAPI 入口,50+ REST 端点,静态托管 | 所有模块 | > > | `config.py` | 环境变量读取,支持 `.env` 文件 | python-dotenv | > > | `db.py` | 引擎/Session,自动建库建表 | SQLAlchemy, psycopg2 | > > | `models.py` | 13 个 ORM 表(见下节) | SQLAlchemy | > > | `akshare_service.py` | AkShare 数据抓取,带 TTL 缓存和 mock 降级 | akshare, cachetools | > > | `ingest.py` | ETL:AkShare → PostgreSQL 增量 upsert | akshare, models | > > | `scheduler.py` | APScheduler 后台任务(5 个定时任务) | apscheduler | > > | `backtest.py` | MA 交叉策略回测,读 DB 日线,输出净值曲线 | sqlalchemy, pandas | > > | `ai.py` | AI 分析:证据链构造、置信度计算、LLM 调用/规则降级 | llm, signals, rag | > > | `signals.py` | 6 类信号历史胜率回测;预测留痕 + 到期核验 | sqlalchemy, pandas, numpy | > > | `rag.py` | 资讯检索 + 利好/利空情绪标注,作为 LLM 上下文 | akshare_service | > > | `report.py` | 七段式 AI 复盘日报生成、落库、推送精简版 | ai, notifier | > > | `portfolio.py` | 持仓计算(移动加权均价)、P&L 归因、逐日资金曲线 | sqlalchemy | > > | `alerts.py` | 实时报价轮询 → 规则命中 → 写事件 + 推送 | akshare_service, notifier | > > | `notifier.py` | 四渠道推送:SMTP 邮件、Server酱、企业微信 Webhook、PushPlus | requests, smtplib | > > | `llm.py` | OpenAI 兼容客户端(DeepSeek/通义/Kimi 均适用) | requests | > > | `cli.py` | 命令行工具:`init` / `ingest` / `ingest_all` | ingest, db | > > > > --- > > > > ## 三、数据模型(PostgreSQL) > > > > | 表名 | 说明 | 主键 / 唯一约束 | > > |---|---|---| > > | `securities` | 证券基础信息(代码·名称·市场) | `code` | > > | `quotes_daily` | 个股日线(OHLCV,前复权) | `(code, date)` | > > | `index_daily` | 指数日线(上证/深证/沪深300) | `(code, date)` | > > | `sector_daily` | 板块每日快照(涨跌·成交额·龙头) | `(date, name)` | > > | `fund_flow_daily` | 行业主力资金流每日快照 | `(date, name)` | > > | `sentiment_daily` | 全市场情绪(涨跌家数/涨跌停) | `date` | > > | `dragon_tiger` | 龙虎榜明细(代码·席位·净买额) | `(date, code, reason)` | > > | `stock_metrics` | 个股最新因子快照(MA/RSI/MACD/量比/分位/连涨 等 15 个因子) | `code` | > > | `signal_stats` | 6 类技术信号历史胜率(回测样本·N日上涨概率·平均收益) | `(signal, horizon)` | > > | `predictions` | AI 诊断留痕(方向·置信度·N日后核验·命中与否) | `(code, date, kind)` | > > | `daily_reports` | AI 复盘日报(markdown 正文·来源·是否已推送) | `date` | > > | `trades` | 交易记录(买卖·价格·数量·手续费·理由·情绪标签) | `id` | > > | `alert_rules` | 预警规则(价格上穿/下穿/涨跌幅条件·状态) | `id` | > > | `alert_events` | 预警触发事件(站内通知·已读状态) | `id` | > > | `job_runs` | 定时任务执行日志 | `id` | > > > > --- > > > > ## 四、定时任务(APScheduler,周一至周五) > > > > | 任务 ID | 触发时间 | 功能 | > > |---|---|---| > > | `daily_ingest` | 15:35(收盘后,可配) | 抓取当日板块/资金流/情绪/龙虎榜/自选股日线入库 | > > | `alert_check` | 每 60 秒 | 实时报价核查所有 active 预警规则,触发则写事件并推送 | > > | `daily_report` | 15:45(入库+10分) | 生成 AI 七段式复盘日报并推送微信/邮件 | > > | `verify_pred` | 15:50 | 核验到期 AI 预测,计算命中率 | > > | `signal_stats` | 每周六 09:00 | 对全市场样本股回测 6 类信号历史胜率(可手动触发) | > > > > --- > > > > ## 五、AI 分析层设计(可回溯) > > > > ``` > > 用户请求 /api/ai/diagnose > > │ > > ▼ > > _stock_context() 从 stock_metrics + DailyQuote 取最新因子 > > │ > > ├─ signals.get_stats() 读 signal_stats 表,取各信号历史胜率 > > │ > > ├─ rag.stock_context() 拉近期资讯 → 情绪标注(利好/利空/中性) > > │ > > ▼ > > _build_evidence() 生成 6 维证据链(趋势/技术/动量/资金/位置/消息) > > 每条附:事实描述 · 方向(bull/bear/neutral) · 历史胜率 · 样本数 > > │ > > ▼ > > _confidence_direction() 加权多空净值 → 置信度(%) + 预测方向(up/down/flat) > > │ > > ├─ signals.record_prediction() 写入 predictions 表留痕 > > │ > > ├─ llm.ask() (有 key) 将证据链+胜率+RAG资讯构造 prompt → 大模型输出 > > │ 或 > > └─ 规则文本降级 格式化证据链为可读文本 > > ``` > > > > 实测准确率闭环: > > ``` > > 预测留痕(open) → N 个交易日后 verify_predictions() 拉 DailyQuote 核验 > > → 填写 actual_ret + hit(True/False) → status=closed > > → /api/ai/accuracy 按方向汇总命中率 > > ``` > > > > --- > > > > ## 六、推送通知渠道 > > > > | 渠道 | 触发条件 | 配置项(`.env`) | > > |---|---|---| > > | SMTP 邮件 | 预警触发 / 复盘日报 / 测试 | `SMTP_HOST/PORT/USER/PASSWORD/TO` | > > | Server酱(微信) | 同上 | `SERVERCHAN_KEY` | > > | 企业微信群机器人 | 同上 | `WECOM_WEBHOOK` | > > | PushPlus(微信) | 同上 | `PUSHPLUS_TOKEN` | > > > > 任意渠道配置即自动启用,互不依赖。 > > > > --- > > > > ## 七、前端菜单结构(app.js MENU) > > > > ``` > > 大盘行情 > > ├─ 市场总览 行情/三大指数/情绪温度计/资金流向 > > ├─ 大盘云图 行业/板块涨跌热力图(ECharts treemap) > > ├─ 热股榜 实时热股+板块 > > └─ 龙虎榜 当日龙虎榜净买额排名 > > > > 自选股 > > ├─ 自选列表 持仓快照 + K线 > > ├─ 全市场选股 8 个内置策略 + SQL 直查 stock_metrics > > └─ 条件选股 客户端多因子过滤 > > > > 复盘中心 > > ├─ 每日复盘 板块/情绪/资金流统计 > > ├─ AI日报 七段式 markdown 日报,历史翻阅,一键推送 > > └─ 个股复盘 K线回放 + MA交叉买卖点标注 > > > > 策略与中台 > > ├─ 策略回测 MA交叉净值曲线 + 最大回撤 > > └─ 数据中台 入库状态 / 手动触发 / 任务日志 > > > > AI 分析 > > ├─ 今日策略 大模型/规则 生成当日操作方向 > > ├─ 个股诊断 6维证据链 + 历史胜率 + 置信度 + RAG资讯 > > ├─ AI复盘点评 当日大模型复盘摘要 > > └─ AI准确率 信号历史胜率表 + 实测命中率 + 近期核验记录 > > > > 组合交易 > > ├─ 组合持仓 实时市值/浮亏/胜率 > > ├─ 资金曲线 逐日净值 vs 沪深300基准(ECharts折线) > > ├─ 交易日志 录入买卖 / 理由 / 情绪标签 > > └─ 盈亏归因 按个股/理由/情绪三维归因 > > > > 智能预警 > > ├─ 预警规则 创建/删除/重激活规则,推送渠道状态检测 > > └─ 触发记录 预警事件列表,标记已读,🔔铃铛角标 > > > > 资讯中心 > > ├─ 要闻快讯 全球财经资讯 + 情绪标注 + AI摘要 > > └─ 自选相关 自选股关联资讯聚合 > > ``` > > > > --- > > > > ## 八、技术栈 > > > > | 层 | 技术选型 | > > |---|---| > > | 前端 | 纯 HTML + CSS + 原生 JS,ECharts 5(CDN) | > > | 后端 | Python 3.12 + FastAPI 0.115 + uvicorn | > > | 数据库 | PostgreSQL(psycopg2-binary + SQLAlchemy 2.0) | > > | 数据源 | AkShare(新浪/同花顺/乐估备选源),Sina hq 实时报价 | > > | 调度 | APScheduler 3.x BackgroundScheduler | > > | AI | OpenAI 兼容 REST(默认 DeepSeek),规则降级无缝切换 | > > | 缓存 | cachetools TTL(内存),无 Redis 依赖 | > > | 推送 | SMTP(SSL) + Server酱 + 企业微信 Webhook + PushPlus | > > | 部署 | WSL2 / Linux,`nohup python main.py`,可 Docker 化 | > > > > --- > > > > ## 九、待完善方向 > > > > | 优先级 | 模块 | 建议 | > > |---|---|---| > > | 高 | 回测引擎 | 手续费/滑点建模,参数寻优,月度收益热力图 | > > | 高 | 数据稳定性 | 东财源封锁问题,增加 Tushare 备用源,分钟线入库 | > > | 中 | RAG 升级 | Embedding + 向量库(Chroma/Qdrant),支持语义检索财报 | > > | 中 | 多组合 | 目前单一组合,扩展为模拟盘/真实盘多组合管理 | > > | 中 | 盘中异动雷达 | 快速拉升/放量突破/涨停打开实时监测 | > > | 低 | 用户系统 | 登录/鉴权,多用户自选股独立管理 | > > | 低 | 部署文档 | Docker Compose(FastAPI + PostgreSQL),Nginx 反代 | > > #### _Assistant_ > .env.example > .venv/ > ai.py > akshare_service.py > alerts.py > backtest.py > cli.py > config.py > db.py > ingest.py > llm.py > main.py > models.py > notifier.py > portfolio.py > rag.py > report.py > requirements.txt > scheduler.py > signals.py > __pycache__/ #### _Assistant_ > """智策股票终端 — FastAPI 后端入口。 > > > > - /api/* : 数据接口(基于 AkShare,带缓存与降级) > > - / : 托管前端原型(prototype 目录) > > """ > > import os > > import json > > import datetime as dt > > from contextlib import asynccontextmanager > > > > from fastapi import FastAPI, Query > > from fastapi.middleware.cors import CORSMiddleware > > from fastapi.staticfiles import StaticFiles > > from sqlalchemy import select, func, desc > > > > from pydantic import BaseModel > > > > import akshare_service as svc > > import config > > import scheduler > > import backtest as bt > > import ai > > import signals as sig > > import report as rpt > > import portfolio as pf > > import llm > > import alerts as al > > import notifier > > from db import init_db, get_session > > from models import (DailyQuote, IndexDaily, SectorDaily, FundFlowDaily, > > SentimentDaily, DragonTiger, Security, JobRun, StockMetric, Trade, > > AlertRule, AlertEvent) > > > > > > @asynccontextmanager > > async def lifespan(app: FastAPI): > > try: > > init_db() > > scheduler.start_scheduler() > > print("[startup] db + scheduler ready") > > except Exception as e: > > print("[startup] WARN:", repr(e)[:160]) > > yield > > > > > > app = FastAPI(title="智策股票终端 API", version="0.2.0", lifespan=lifespan) > > > > app.add_middleware( > > CORSMiddleware, > > allow_origins=["*"], > > allow_methods=["*"], > > allow_headers=["*"], > > ) > > > > # 自选股本地存储 > > BASE_DIR = os.path.dirname(os.path.abspath(__file__)) > > WATCH_FILE = os.path.join(BASE_DIR, "watchlist.json") > > DEFAULT_WATCH = ["600519", "300750", "002594", "688981", "300059", "601012"] > > > > > > def load_watch(): > > if os.path.exists(WATCH_FILE): > > try: > > with open(WATCH_FILE, "r", encoding="utf-8") as f: > > return json.load(f) > > except Exception: > > pass > > return DEFAULT_WATCH > > > > > > def save_watch(symbols): > > with open(WATCH_FILE, "w", encoding="utf-8") as f: > > json.dump(symbols, f, ensure_ascii=False) > > > > > > # ============ API ============ > > @app.get("/api/health") > > def health(): > > return {"ok": True, "akshare": svc.AK_OK} > > > > > > @app.get("/api/indices") > > def indices(): > > return svc.get_indices() > > > > > > @app.get("/api/kline") > > def kline(symbol: str = Query("600519"), days: int = Query(120, ge=20, le=500)): > > return svc.get_kline(symbol, days) > > > > > > @app.get("/api/sentiment") > > def sentiment(): > > return svc.get_sentiment() > > > > > > @app.get("/api/treemap") > > def treemap(mode: str = Query("sector")): > > return svc.get_treemap(mode) > > > > > > @app.get("/api/fundflow") > > def fundflow(): > > return svc.get_fund_flow() > > > > > > @app.get("/api/hot/stocks") > > def hot_stocks(): > > return svc.get_hot_stocks() > > > > > > @app.get("/api/hot/sectors") > > def hot_sectors(): > > return svc.get_industry_boards() > > > > > > @app.get("/api/dragon") > > def dragon(): > > return svc.get_dragon_tiger() > > > > > > @app.get("/api/watchlist") > > def watchlist(): > > return svc.get_watchlist(load_watch()) > > > > > > @app.post("/api/watchlist/{code}") > > def watch_add(code: str): > > w = load_watch() > > if code not in w: > > w.append(code) > > save_watch(w) > > return {"ok": True, "list": w} > > > > > > @app.delete("/api/watchlist/{code}") > > def watch_del(code: str): > > w = [c for c in load_watch() if c != code] > > save_watch(w) > > return {"ok": True, "list": w} > > > > > > # ============ 数据中台 ============ > > @app.get("/api/admin/status") > > def admin_status(): > > counts, last_dates = {}, {} > > with get_session() as s: > > for label, model in [("securities", Security), ("quotes_daily", DailyQuote), > > ("index_daily", IndexDaily), ("sector_daily", SectorDaily), > > ("fund_flow_daily", FundFlowDaily), ("sentiment_daily", SentimentDaily), > > ("dragon_tiger", DragonTiger)]: > > counts[label] = s.execute(select(func.count()).select_from(model)).scalar() or 0 > > if hasattr(model, "date"): > > d = s.execute(select(func.max(model.date))).scalar() > > last_dates[label] = d.isoformat() if d else None > > jobs = s.execute(select(JobRun).order_by(desc(JobRun.id)).limit(8)).scalars().all() > > job_list = [{"id": j.id, "job": j.job, "status": j.status, > > "started": j.started_at.strftime("%m-%d %H:%M:%S") if j.started_at else "", > > "finished": j.finished_at.strftime("%H:%M:%S") if j.finished_at else "", > > "message": j.message[:200]} for j in jobs] > > return {"counts": counts, "last_dates": last_dates, "jobs": job_list, > > "running": scheduler.is_running(), "universe": config.DEFAULT_UNIVERSE, > > "schedule": f"周一至周五 {config.INGEST_HOUR:02d}:{config.INGEST_MINUTE:02d}"} > > > > > > @app.post("/api/admin/ingest") > > def admin_ingest(): > > if scheduler.is_running(): > > return {"started": False, "msg": "已有入库任务在执行"} > > return scheduler.trigger_async() > > > > > > @app.post("/api/admin/ingest_all") > > def admin_ingest_all(): > > return scheduler.trigger_all_async() > > > > > > @app.get("/api/db/kline") > > def db_kline(symbol: str = Query("600519"), days: int = Query(250, ge=20, le=1000)): > > with get_session() as s: > > rows = s.execute( > > select(DailyQuote).where(DailyQuote.code == symbol) > > .order_by(DailyQuote.date.desc()).limit(days) > > ).scalars().all() > > rows = list(reversed(rows)) > > if not rows: > > return {"source": "db", "empty": True, "symbol": symbol, "dates": [], "ohlc": [], "vols": []} > > return {"source": "db", "symbol": symbol, > > "dates": [r.date.strftime("%m/%d") for r in rows], > > "ohlc": [[r.open, r.close, r.low, r.high] for r in rows], > > "vols": [r.volume for r in rows]} > > > > > > @app.get("/api/db/sentiment_history") > > def db_sentiment_history(days: int = Query(60, ge=5, le=365)): > > with get_session() as s: > > rows = s.execute(select(SentimentDaily).order_by(SentimentDaily.date.desc()).limit(days)).scalars().all() > > rows = list(reversed(rows)) > > return {"dates": [r.date.isoformat() for r in rows], > > "up": [r.up for r in rows], "down": [r.down for r in rows], > > "limit_up": [r.limit_up for r in rows]} > > > > > > @app.get("/api/review/daily") > > def review_daily(date: str = Query(None)): > > with get_session() as s: > > if date: > > d = dt.date.fromisoformat(date) > > else: > > d = s.execute(select(func.max(SectorDaily.date))).scalar() > > if not d: > > return {"ok": False, "msg": "暂无入库数据,请先在数据中台执行入库"} > > sectors = s.execute(select(SectorDaily).where(SectorDaily.date == d).order_by(SectorDaily.pct.desc())).scalars().all() > > flows = s.execute(select(FundFlowDaily).where(FundFlowDaily.date == d).order_by(FundFlowDaily.net.desc())).scalars().all() > > senti = s.execute(select(SentimentDaily).where(SentimentDaily.date == d)).scalar_one_or_none() > > lhb = s.execute(select(DragonTiger).where(DragonTiger.date == d).order_by(DragonTiger.net.desc()).limit(10)).scalars().all() > > > > top_sec = [{"name": x.name, "pct": x.pct} for x in sectors[:8]] > > bot_sec = [{"name": x.name, "pct": x.pct} for x in sectors[-5:]] > > inflow = [{"name": x.name, "net": x.net} for x in flows[:8]] > > outflow = [{"name": x.name, "net": x.net} for x in flows[-5:][::-1]] > > senti_d = ({"up": senti.up, "down": senti.down, "limit_up": senti.limit_up, > > "limit_down": senti.limit_down} if senti else None) > > > > summary = _gen_review_text(d, senti_d, top_sec, inflow) > > return {"ok": True, "date": d.isoformat(), "sentiment": senti_d, > > "top_sectors": top_sec, "weak_sectors": bot_sec, > > "inflow": inflow, "outflow": outflow, > > "dragon": [{"name": x.name, "code": x.code, "net": x.net, "pct": x.pct} for x in lhb], > > "summary": summary} > > > > > > def _gen_review_text(d, senti, top_sec, inflow): > > parts = [f"【{d.isoformat()} 复盘】"] > > if senti: > > tone = "情绪偏暖" if senti["up"] > senti["down"] else "情绪偏弱" > > parts.append(f"全市场上涨 {senti['up']} 家、下跌 {senti['down']} 家,涨停 {senti['limit_up']} 家、跌停 {senti['limit_down']} 家,{tone}。") > > if top_sec: > > names = "、".join(x["name"] for x in top_sec[:3]) > > parts.append(f"领涨板块:{names}。") > > if inflow: > > names = "、".join(x["name"] for x in inflow[:3]) > > parts.append(f"主力净流入居前:{names}。") > > parts.append("注:以上为基于入库数据的自动统计,AI 智能点评将在 AI 分析模块接入大模型后生成。") > > return " ".join(parts) > > > > > > @app.get("/api/backtest") > > def backtest_api(symbol: str = Query("600519"), fast: int = Query(5, ge=2, le=60), > > slow: int = Query(20, ge=5, le=250)): > > if fast >= slow: > > return {"ok": False, "msg": "快线周期需小于慢线周期"} > > return bt.run_backtest(symbol, fast, slow) > > > > > > # ============ 全市场选股 ============ > > STRATEGIES = { > > "surge": "最近暴涨(5日涨幅≥20%)", > > "plunge": "最近暴跌(5日跌幅≥15%)", > > "dip": "超跌抄底(60日分位≤20%且当日企稳)", > > "breakout": "突破走强(逼近60日新高)", > > "ma_bull": "均线多头(MA5>10>20)", > > "volume": "放量上攻(量比≥2且上涨)", > > "macd_gold": "MACD金叉", > > "strong": "强势连涨(≥3日连阳)", > > } > > > > > > @app.get("/api/screen/strategies") > > def screen_strategies(): > > return {"list": [{"id": k, "name": v} for k, v in STRATEGIES.items()]} > > > > > > @app.get("/api/screen") > > def screen(strategy: str = Query("surge"), limit: int = Query(60, ge=10, le=300), > > min_amount: float = Query(0.0)): > > M = StockMetric > > q = select(M) > > order = M.ret5.desc() > > if strategy == "surge": > > q = q.where(M.ret5 >= 20) > > elif strategy == "plunge": > > q = q.where(M.ret5 <= -15); order = M.ret5.asc() > > elif strategy == "dip": > > q = q.where(M.pos60 <= 0.2, M.pct > 0); order = M.pos60.asc() > > elif strategy == "breakout": > > q = q.where(M.pos60 >= 0.95, M.pct > 0); order = M.ret20.desc() > > elif strategy == "ma_bull": > > q = q.where(M.ma_bull.is_(True)); order = M.ret20.desc() > > elif strategy == "volume": > > q = q.where(M.vol_ratio >= 2, M.pct > 0); order = M.vol_ratio.desc() > > elif strategy == "macd_gold": > > q = q.where(M.macd_gold.is_(True)); order = M.ret5.desc() > > elif strategy == "strong": > > q = q.where(M.up_streak >= 3); order = M.up_streak.desc() > > if min_amount > 0: > > q = q.where(M.amount >= min_amount) > > q = q.order_by(order).limit(limit) > > with get_session() as s: > > rows = s.execute(q).scalars().all() > > total = s.execute(select(func.count()).select_from(M)).scalar() or 0 > > return {"strategy": strategy, "name": STRATEGIES.get(strategy, strategy), "pool_size": total, > > "count": len(rows), "list": [{ > > "code": r.code, "name": r.name, "close": r.close, "pct": r.pct, > > "ret5": r.ret5, "ret20": r.ret20, "vol_ratio": r.vol_ratio, > > "rsi14": r.rsi14, "pos60": round(r.pos60 * 100, 1), "amount": r.amount, > > "up_streak": r.up_streak} for r in rows]} > > > > > > @app.get("/api/securities/search") > > def securities_search(q: str = Query("", min_length=0), limit: int = Query(15, le=50)): > > with get_session() as s: > > stmt = select(Security) > > if q: > > stmt = stmt.where((Security.code.like(f"{q}%")) | (Security.name.like(f"%{q}%"))) > > rows = s.execute(stmt.limit(limit)).scalars().all() > > return {"list": [{"code": r.code, "name": r.name} for r in rows]} > > > > > > # ============ 个股复盘(K线 + 买卖点 + 回放) ============ > > def _ma_list(close, n): > > out = [None] * len(close) > > for i in range(len(close)): > > if i >= n - 1: > > out[i] = round(sum(close[i - n + 1:i + 1]) / n, 3) > > return out > > > > > > @app.get("/api/review/stock") > > def review_stock(symbol: str = Query("600519"), days: int = Query(250, ge=40, le=1000), > > fast: int = Query(5), slow: int = Query(20)): > > with get_session() as s: > > rows = s.execute( > > select(DailyQuote).where(DailyQuote.code == symbol) > > .order_by(DailyQuote.date.desc()).limit(days) > > ).scalars().all() > > sec = s.get(Security, symbol) > > rows = list(reversed(rows)) > > if not rows: > > return {"ok": False, "msg": "该股票库内无日线,请先在数据中台入库该股或执行全市场回填", "symbol": symbol} > > dates = [r.date.strftime("%y/%m/%d") for r in rows] > > ohlc = [[r.open, r.close, r.low, r.high] for r in rows] > > vols = [r.volume for r in rows] > > close = [r.close for r in rows] > > maf, mas = _ma_list(close, fast), _ma_list(close, slow) > > > > signals = [] > > for i in range(1, len(close)): > > if maf[i] is None or mas[i] is None or maf[i - 1] is None or mas[i - 1] is None: > > continue > > if maf[i - 1] <= mas[i - 1] and maf[i] > mas[i]: > > signals.append({"idx": i, "date": dates[i], "price": close[i], "type": "buy"}) > > elif maf[i - 1] >= mas[i - 1] and maf[i] < mas[i]: > > signals.append({"idx": i, "date": dates[i], "price": close[i], "type": "sell"}) > > > > # 区间统计 > > hi = max(r.high for r in rows); lo = min(r.low for r in rows) > > period_ret = round((close[-1] / close[0] - 1) * 100, 2) > > return {"ok": True, "symbol": symbol, "name": sec.name if sec else symbol, > > "dates": dates, "ohlc": ohlc, "vols": vols, > > "ma_fast": maf, "ma_slow": mas, "fast": fast, "slow": slow, > > "signals": signals, > > "stats": {"period_return": period_ret, "high": hi, "low": lo, > > "start": dates[0], "end": dates[-1], "bars": len(rows)}} > > > > > > # ============ AI 分析 ============ > > @app.get("/api/ai/status") > > def ai_status(): > > return {"enabled": llm.enabled(), "model": config.LLM_MODEL if llm.enabled() else None} > > > > > > @app.get("/api/ai/review_daily") > > def ai_review_daily(date: str = Query(None)): > > return ai.review_daily_comment(date) > > > > > > @app.get("/api/ai/diagnose") > > def ai_diagnose(symbol: str = Query("600519")): > > return ai.diagnose(symbol) > > > > > > @app.get("/api/ai/today") > > def ai_today(): > > return ai.today_strategy() > > > > > > # ============ 可回溯:信号历史胜率 + 实测准确率 ============ > > @app.get("/api/ai/signal_stats") > > def ai_signal_stats(horizon: int = Query(5, ge=1, le=20)): > > return {"horizon": horizon, "stats": sig.get_stats(horizon)} > > > > > > @app.post("/api/ai/signal_stats/compute") > > def ai_signal_stats_compute(sample: int = Query(500, ge=50, le=4000), horizon: int = Query(5, ge=1, le=20)): > > return scheduler.trigger_signal_stats_async(sample, horizon) > > > > > > @app.get("/api/ai/accuracy") > > def ai_accuracy(): > > return sig.accuracy() > > > > > > @app.post("/api/ai/accuracy/verify") > > def ai_accuracy_verify(): > > return sig.verify_predictions() > > > > > > # ============ AI 自动复盘日报 ============ > > @app.get("/api/report/daily") > > def report_daily(date: str = Query(None)): > > return rpt.get_by_date(date) if date else rpt.latest() > > > > > > @app.get("/api/report/history") > > def report_history(limit: int = Query(30, ge=1, le=120)): > > return rpt.history(limit) > > > > > > @app.post("/api/report/generate") > > def report_generate(date: str = Query(None), push: bool = Query(False)): > > return rpt.generate(date, push=push) > > > > > > # ============ 交易日志 & 组合 ============ > > class TradeIn(BaseModel): > > code: str > > name: str = "" > > side: str = "buy" > > price: float > > qty: int > > fee: float = 0.0 > > date: str = "" > > reason: str = "" > > emotion: str = "" > > > > > > @app.get("/api/trades") > > def list_trades(): > > with get_session() as s: > > rows = s.execute(select(Trade).order_by(Trade.date.desc(), Trade.id.desc())).scalars().all() > > names = {} > > return {"list": [{"id": t.id, "date": t.date.isoformat(), "code": t.code, "name": t.name, > > "side": t.side, "price": t.price, "qty": t.qty, "fee": t.fee, > > "reason": t.reason, "emotion": t.emotion} for t in rows]} > > > > > > @app.post("/api/trades") > > def add_trade(t: TradeIn): > > d = dt.date.fromisoformat(t.date) if t.date else dt.date.today() > > name = t.name > > if not name: > > with get_session() as s: > > sec = s.get(Security, t.code) > > name = sec.name if sec else t.code > > with get_session() as s: > > row = Trade(date=d, code=t.code, name=name, side=t.side, price=t.price, > > qty=t.qty, fee=t.fee, reason=t.reason, emotion=t.emotion) > > s.add(row); s.commit() > > return {"ok": True, "id": row.id} > > > > > > @app.delete("/api/trades/{tid}") > > def del_trade(tid: int): > > with get_session() as s: > > row = s.get(Trade, tid) > > if row: > > s.delete(row); s.commit() > > return {"ok": True} > > > > > > @app.get("/api/portfolio") > > def get_portfolio(): > > return pf.compute() > > > > > > @app.get("/api/portfolio/equity") > > def portfolio_equity(): > > return pf.equity_curve() > > > > > > # ============ 推送通知 ============ > > @app.get("/api/notify/status") > > def notify_status(): > > return {"channels": notifier.channels_status(), "enabled": notifier.any_enabled()} > > > > > > @app.post("/api/notify/test") > > def notify_test(): > > if not notifier.any_enabled(): > > return {"ok": False, "msg": "未配置任何推送渠道,请在 backend/.env 配置后重启"} > > res = notifier.notify("【智策】推送测试", "这是一条来自智策股票终端的测试通知,收到即表示推送通道正常。") > > return {"ok": True, "result": res} > > > > > > # ============ 智能预警 ============ > > class AlertIn(BaseModel): > > code: str > > kind: str = "price_above" > > threshold: float > > note: str = "" > > > > > > @app.get("/api/alerts") > > def list_alerts(): > > with get_session() as s: > > rows = s.execute(select(AlertRule).order_by(AlertRule.id.desc())).scalars().all() > > return {"list": [{"id": r.id, "code": r.code, "name": r.name, "kind": r.kind, > > "threshold": r.threshold, "status": r.status, "note": r.note, > > "last_value": r.last_value, > > "triggered_at": r.triggered_at.strftime("%m-%d %H:%M") if r.triggered_at else ""} > > for r in rows]} > > > > > > @app.post("/api/alerts") > > def add_alert(a: AlertIn): > > with get_session() as s: > > sec = s.get(Security, a.code) > > name = sec.name if sec else a.code > > row = AlertRule(code=a.code, name=name, kind=a.kind, threshold=a.threshold, note=a.note) > > s.add(row); s.commit() > > return {"ok": True, "id": row.id} > > > > > > @app.delete("/api/alerts/{aid}") > > def del_alert(aid: int): > > with get_session() as s: > > row = s.get(AlertRule, aid) > > if row: > > s.delete(row); s.commit() > > return {"ok": True} > > > > > > @app.post("/api/alerts/{aid}/reactivate") > > def reactivate_alert(aid: int): > > with get_session() as s: > > row = s.get(AlertRule, aid) > > if row: > > row.status = "active"; row.triggered_at = None; s.commit() > > return {"ok": True} > > > > > > @app.post("/api/alerts/check") > > def manual_check(): > > return al.check_alerts() > > > > > > @app.get("/api/alerts/events") > > def alert_events(unread_only: bool = Query(False), limit: int = Query(30, le=100)): > > with get_session() as s: > > stmt = select(AlertEvent).order_by(AlertEvent.id.desc()) > > if unread_only: > > stmt = stmt.where(AlertEvent.read.is_(False)) > > rows = s.execute(stmt.limit(limit)).scalars().all() > > unread = s.execute(select(func.count()).select_from(AlertEvent).where(AlertEvent.read.is_(False))).scalar() or 0 > > return {"unread": unread, "list": [{"id": e.id, "code": e.code, "name": e.name, > > "message": e.message, "time": e.created_at.strftime("%m-%d %H:%M:%S") if e.created_at else ""} > > for e in rows]} > > > > > > @app.post("/api/alerts/events/read") > > def mark_events_read(): > > with get_session() as s: > > for e in s.execute(select(AlertEvent).where(AlertEvent.read.is_(False))).scalars(): > > e.read = True > > s.commit() > > return {"ok": True} > > > > > > # ============ 资讯中心 ============ > > @app.get("/api/news") > > def news(limit: int = Query(40, le=100)): > > return svc.get_news(limit) > > > > > > @app.get("/api/news/stock") > > def news_stock(code: str = Query(...)): > > return svc.get_stock_news(code) > > > > > > @app.get("/api/news/watch") > > def news_watch(): > > codes = load_watch()[:6] > > out = [] > > for c in codes: > > r = svc.get_stock_news(c, limit=4) > > for x in r["list"]: > > x["code"] = c > > out.append(x) > > out.sort(key=lambda x: x["time"], reverse=True) > > return {"list": out[:40]} > > > > > > class NewsAI(BaseModel): > > title: str > > content: str = "" > > > > > > @app.post("/api/news/ai") > > def news_ai(n: NewsAI): > > text_in = (n.title + "。" + n.content).strip() > > senti, kw = svc.judge_sentiment(text_in) > > if llm.enabled(): > > try: > > prompt = ("请分析下面这条财经资讯:\n" > > "1) 一句话摘要;2) 利好/利空/中性判断及理由;3) 可能受影响的板块或个股方向。120字内。\n\n" > > + text_in[:1200]) > > text = llm.ask(prompt, temperature=0.3, max_tokens=400) > > return {"ok": True, "source": "llm", "sentiment": senti, "text": text} > > except Exception: > > pass > > return {"ok": True, "source": "rule", "sentiment": senti, > > "text": f"判断:{senti}(关键词:{'、'.join(kw) or '无'})。摘要:{text_in[:80]}…\n(配置大模型后可获得更深入的关联分析)"} > > > > > > # ============ 静态前端 ============ > > FRONTEND_DIR = os.path.join(os.path.dirname(BASE_DIR), "prototype") > > if os.path.isdir(FRONTEND_DIR): > > app.mount("/", StaticFiles(directory=FRONTEND_DIR, html=True), name="frontend") > > > > > > if __name__ == "__main__": > > import uvicorn > > uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=False) > > #### _Assistant_ > 基于对项目的分析,我发现以下可优化的功能点: > > ## 高优先级优化 > > ### 1. **缓存与性能优化** > - `akshare_service.py` 使用内存缓存,服务重启后失效。建议引入 Redis 持久化缓存,避免频繁调用 AkShare API > - 大量接口未做分页,如 `/api/trades`、`/api/alerts/events` 在数据量大时会有性能问题 > - K线数据查询未添加索引优化提示 > > ### 2. **错误处理与容错** > - 多数接口缺少统一的异常捕获,数据源异常时可能返回 500 错误 > - `scheduler.py` 定时任务失败后缺少重试机制和告警 > - AkShare 数据源不稳定时,降级逻辑不够完善(只有 mock 数据) > > ### 3. **数据完整性** > - 交易记录(`trades`)无事务一致性保证,买卖不匹配时持仓计算可能出错 > - 缺少数据修正接口,入库错误数据后无法手动修正 > - 历史数据回填(`ingest_all`)无断点续传,中断后需重新开始 > > ### 4. **安全性** > - 所有接口无鉴权,任何人可访问管理功能(`/api/admin/*`) > - 环境变量敏感信息(`LLM_API_KEY`等)未加密存储 > - CORS 配置为 `allow_origins=["*"]`,生产环境不安全 > > ## 中优先级优化 > > ### 5. **功能增强** > - AI 诊断结果无历史记录,无法追溯分析质量变化 > - 预警系统只支持价格/涨跌幅,缺少技术指标预警(如 MACD 金叉、RSI 超买等) > - 复盘日报只能手动触发推送,未自动推送给用户 > - 自选股只支持单一列表,无分组管理功能 > > ### 6. **代码质量** > - `main.py` 超过 500 行,建议拆分为多个 Router 模块 > - 缺少单元测试和集成测试 > - 部分函数职责不清晰,如 `_gen_review_text()` 混合了业务逻辑和文案生成 > > ### 7. **监控与日志** > - 缺少接口访问日志和性能监控 > - 定时任务执行日志只保留在数据库,无实时监控面板 > - AkShare 调用频率无监控,可能触发限流 > > ## 低优先级优化 > > ### 8. **用户体验** > - 前端刷新后丢失自选股筛选条件 > - K线图无技术指标叠加功能(如 MACD、KDJ) > - 移动端适配不足 > > ### 9. **扩展性** > - 只支持 A 股,缺少港股、美股扩展能力 > - 数据源绑定 AkShare,切换成本高 > - 多组合管理需要大幅重构 `portfolio.py` > > ## 建议优先实现的 3 个功能 > > 1. **Redis 缓存层** - 提升响应速度,减少 API 调用 > 2. **统一异常处理中间件** - 提升系统稳定性 > 3. **基础鉴权机制** - 保护管理接口安全 > > 需要我深入分析某个模块的优化方案吗? #### _User_ > 功能上有没有好的功能拓展建议 #### _Assistant_ > 基于当前架构,建议以下功能扩展方向: > > ## 一、高价值功能扩展 > > ### 1. **盘中实时监控雷达** ⭐⭐⭐⭐⭐ > **痛点**:当前只有收盘后复盘,错过盘中异动机会 > > **功能点**: > - 快速拉升监控(5分钟涨幅 >3%) > - 放量突破预警(量比 >3 且突破关键位) > - 涨停打开/炸板追踪 > - 连板股实时追踪 > - 大单异动(单笔超百万) > > **实现难度**:中等(需要分钟线数据源) > > ### 2. **策略回测增强** ⭐⭐⭐⭐⭐ > **痛点**:当前只有简单 MA 交叉,无法验证复杂策略 > > **功能点**: > - 多因子组合回测(技术+基本面) > - 仓位管理策略(金字塔加仓、止损止盈) > - 参数优化网格搜索 > - 夏普比率/最大回撤/胜率等完整指标 > - 策略对比(并排显示多条净值曲线) > - 交易明细导出(每笔买卖点、持仓天数、收益率) > > **实现难度**:中等 > > ### 3. **板块轮动分析** ⭐⭐⭐⭐ > **痛点**:只有当日板块涨跌,看不出资金流向趋势 > > **功能点**: > - 板块强弱排名趋势(近5日/10日/20日) > - 资金流向桑基图(从哪个板块流向哪个板块) > - 板块生命周期判断(启动期/加速期/衰退期) > - 龙头股自动识别(板块内涨幅+成交额排名) > - 板块联动性分析(相关系数热力图) > > **实现难度**:中等 > > ### 4. **智能选股增强** ⭐⭐⭐⭐ > **痛点**:当前选股策略固定,无法自定义 > > **功能点**: > - 可视化选股器(拖拽式多条件组合) > - 选股结果回测验证(该选股规则历史表现) > - 选股策略保存/分享 > - 条件预警(符合条件时自动推送) > - 选股结果对比(今日 vs 昨日,哪些股票新入选/退出) > > **实现难度**:中高 > > ### 5. **持仓归因分析深化** ⭐⭐⭐⭐ > **痛点**:当前只有简单盈亏,不知道赚钱/亏钱原因 > > **功能点**: > - 收益归因分解:选股能力 vs 择时能力 vs 运气成分 > - 持仓时长分析(短线/中线/长线各自胜率) > - 买入理由有效性验证(按理由统计胜率) > - 情绪标签相关性(贪婪/恐惧/理性,各自收益率) > - 对标指数超额收益拆解 > > **实现难度**:中等 > > ## 二、差异化功能扩展 > > ### 6. **AI 对话式分析** ⭐⭐⭐⭐⭐ > **亮点**:与大模型深度结合,打造对话式炒股助手 > > **功能点**: > - 自然语言选股:"帮我找近期突破且资金流入的科技股" > - 持仓诊断对话:"我的持仓有什么风险?" > - 策略建议:"当前市场适合什么策略?" > - 实时问答:"某某股票为什么今天大跌?" > - 上下文记忆(多轮对话,记住用户偏好) > > **实现难度**:中高(需要 Function Calling) > > ### 7. **社区情绪监控** ⭐⭐⭐⭐ > **亮点**:量化散户情绪,反向指标 > > **功能点**: > - 爬取东方财富/雪球/股吧热帖 > - 情绪指数(乐观/悲观比例) > - 热议股票排行(讨论量激增预警) > - 关键词云图(当前市场关注什么) > - 情绪与股价相关性回测 > > **实现难度**:中高(需要爬虫+NLP) > > ### 8. **事件驱动策略** ⭐⭐⭐⭐ > **亮点**:基于财经事件的量化交易 > > **功能点**: > - 财报发布前后统计规律 > - 限售解禁影响回测 > - 高管增持/减持跟踪 > - 行业政策事件库(光伏补贴、新能源政策等) > - 事件驱动选股(财报超预期+高管增持) > > **实现难度**:中高 > > ### 9. **模拟盘对战** ⭐⭐⭐ > **亮点**:用户间 PK,增加粘性 > > **功能点**: > - 创建模拟盘对战房间 > - 排行榜(收益率/夏普比率) > - 查看高手持仓(匿名化) > - 复盘对比(我为什么输给他) > - 月度/季度冠军 > > **实现难度**:中等 > > ### 10. **财报深度解读** ⭐⭐⭐⭐ > **痛点**:技术分析为主,缺少基本面支持 > > **功能点**: > - 财报关键指标趋势(ROE/毛利率/营收增速) > - AI 财报摘要(一句话总结) > - 同行对比(与行业均值对比) > - 财报异常预警(存货激增、应收账款占比过高) > - 财报发布日历提醒 > > **实现难度**:中等 > > ## 三、实用小功能 > > ### 11. **交易日历与提醒** > - 除权除息提醒 > - 解禁日期预警 > - 业绩预告/快报发布日历 > - 股东大会日期 > > ### 12. **成本计算器** > - 精确计算交易成本(印花税/佣金/过户费) > - 补仓成本计算 > - 持仓成本线标注在 K 线图上 > > ### 13. **涨跌停分析** > - 连板股追踪器(今日几进几?) > - 炸板率统计(某股涨停后次日表现) > - 涨停敢死队排行 > > ### 14. **关联股票发现** > - 同一题材股票聚类 > - 供应链上下游关系图 > - 股东关联关系挖掘 > > ### 15. **数据导出与备份** > - 持仓数据导出 Excel > - 交易记录导出 > - 选股结果导出 > - 数据库自动备份 > > ## 建议优先级排序(ROI 角度) > > | 排名 | 功能 | 投入成本 | 用户价值 | 差异化 | > |------|------|----------|----------|--------| > | 1 | AI 对话式分析 | 高 | 极高 | 极高 | > | 2 | 盘中实时监控雷达 | 中 | 极高 | 高 | > | 3 | 策略回测增强 | 中 | 高 | 中 | > | 4 | 板块轮动分析 | 中 | 高 | 中 | > | 5 | 持仓归因分析深化 | 低 | 高 | 中 | > > **快速见效(先做)**: > 1. 盘中实时监控雷达 > 2. 策略回测增强 > 3. 持仓归因分析深化 > > **长期竞争力(后做)**: > 1. AI 对话式分析 > 2. 社区情绪监控 > 3. 事件驱动策略 > > 需要我详细设计某个功能的技术方案吗?