docs: 文档重构 — 清理 AI agent 残留,整合 docs/ 目录结构

- 删除 11 个残留文件: continuation.md, init_plan.md, reasonix.toml, djapi/continuation.md, djapi/.serena/, djapi/.claude/, djapi/.mcp.json, .claude/skills/, docs/usage.html, docs/db_schema.md, docs/report_db_design.md
- 7 个 CLAUDE-*.md 移入 docs/ 并重命名去 CLAUDE- 前缀
- 新增 4 个文档: architecture.md, development.md, api.md, deployment.md
- 重写 usage.md, README.md
- 修复所有过时引用和交叉链接
This commit is contained in:
Simon
2026-08-22 11:56:40 +08:00
parent 6e1ac0c46b
commit 6acf938caf
34 changed files with 1191 additions and 3909 deletions
+120
View File
@@ -0,0 +1,120 @@
# Agent 工作指南
cc-cursor — Mac Mini 单机量化研究平台。全链路:Data → Factor → Backtest → Optimize → ML → Sentiment → Agent。
## 速查索引
| 模块 | 详情文件 | 核心入口 |
|------|---------|---------|
| 数据层 + 数据库 | [data-layer.md](data-layer.md) | `from data.data_manager import DataManager` |
| 因子引擎 + 情绪 | [factors.md](factors.md) | `from factors.registry import get_factor` |
| 回测 + 优化 | [backtest.md](backtest.md) | `from backtest.vectorbt.engine import VectorBTEngine` |
| ML 模型 | [ml-models.md](ml-models.md) | `from models.lightgbm.model import LightGBMModel` |
| Agent 系统 + CLI | [agents.md](agents.md) | `python finance/cli/agent_cli.py daily` | |
## 工作区布局
| 目录 | 内容 |
|------|------|
| `finance/` | 核心量化引擎(**代码实际位置**)。代码内 import 用顶层名 `data.*`/`factors.*` 等 — 由 CLI 把 `finance/` 加入 sys.path;文件路径为 `finance/data/xxx.py` 等 |
| `djapi/` | Django API 子项目,有独立 `djapi/CLAUDE.md` |
| `shared/script/` | `autossh.sh` — MariaDB SSH 隧道 |
| `docs/` | 项目文档目录,包含使用指南、架构说明、API 参考等;**新建 md 一律放这里** |
| `finance/strategy` `portfolio` `execution` `scheduler/` | 空壳占位(仅 `__init__.py`),逻辑未落地,别误以为有实现 |
| `finance/reports/` | 日报输出 `daily_YYYYMMDD.{md,html}` |
## 环境
```bash
conda activate quant # Python 3.11.13
bash shared/script/autossh.sh # DB SSH 隧道 (本地 13306 → 远程 3306)
# 环境变量在 finance/.env(示例见 finance/.env.example):QWEN / TUSHARE / DB / 情绪范围
```
## 任务→文档路由
| 任务类型 | 先读取 |
|---------|--------|
| 数据源/数据库/cache 相关 | [data-layer.md](data-layer.md) |
| 因子/情绪/新闻相关 | [factors.md](factors.md) + [reference.md](reference.md) |
| 回测/优化/策略相关 | [backtest.md](backtest.md) |
| ML 模型/特征工程相关 | [ml-models.md](ml-models.md) |
| Agent/CLI/报告相关 | [agents.md](agents.md) |
| 第三方库 API/参数 | 查官方文档 |
| 因子名/类名/表结构速查 | [reference.md](reference.md) |
## 多步任务规则
复杂任务(涉及 3+ 文件或 2+ 模块)执行前:
1. 输出执行计划清单(步骤 + 每步验证方法)
2. 每步完成后验证通过才继续
3. 遇到失败先定位根因,不跳过
## 核心设计约束(必须遵守)
- 数据流:`Data → Factor → Model → Strategy → Backtest → Report`
- 策略层禁止直接访问 AkShare/Tushare → 全部通过 `DataManager`
- 模型层禁止直接访问数据库 → 全部通过 DataManager
- 指数代码规则:`.SH`=指数, `.SZ` 开头非 399=个股
- 数据源优先级:Tushare → AkShare (fallback)
- 当日数据未缓存 → 先 `dm.sync_daily`;增量同步只更新已缓存股票
- 修改多文件前先说明:文件清单、原因、影响;优先小范围修改
## CLI 常用命令
```bash
python finance/cli/agent_cli.py daily # 5步完整流程
python finance/cli/agent_cli.py picks 15 # 选股
python finance/cli/agent_cli.py risk # 风险评估
python finance/cli/agent_cli.py research # 因子研究
python finance/cli/agent_cli.py report 20260603 # 生成日报
python finance/cli/agent_cli.py warmup 50 # 首次预热缓存
python finance/cli/demo_data_manager.py --ts_code 600519.SH
python finance/cli/demo_factor_engine.py --ts_code 300750.SZ
python finance/cli/demo_backtest.py --ts_code 000001.SZ
python finance/cli/demo_optimizer.py --ts_code 000001.SZ --trials 100
python finance/cli/demo_ml.py --ts_code 000001.SZ --lookahead 5
python finance/cli/demo_sentiment.py --ts_code 600519.SH
python finance/cli/demo_sentiment_detail.py --ts_code 600519.SH --date 20260603
```
## 技术栈
| 组件 | 技术 | 环境 |
|------|------|------|
| 数据获取 | AkShare + Tushare (双源) | conda quant |
| 数据库 | MariaDB (SSH 隧道) | mac_ 前缀表 |
| 因子/特征 | pandas / numpy / sklearn | conda quant |
| 回测 | VectorBT 1.0 | conda quant |
| 优化 | Optuna 4.9 | conda quant |
| ML | LightGBM 4.6 + CatBoost 1.2 | conda quant |
| NLP | Qwen (DashScope / Ollama) | .env 配置 |
| Agent | 自研编排器 | finance/agents/ |
| API | Django 5.2 + uWSGI | djapi/ |
## 安全规范
### 禁止提交
- `.env`(含真实 key)
- API Key(`sk-*`、`TUSHARE_TOKEN` 等)
- Cookie / Session
- Token / 密钥
- 个人隐私数据(手机号、身份证、密码)
### 必须提供
- `.env.example` — 仅含占位符的示例配置,如:
```
TUSHARE_TOKEN=your_token_here
QWEN_API_KEY=sk-your-key-here
MAC_DB_PASSWORD=your_password_here
```
### 提交前检查
```bash
grep -r "sk-\|token\|_H(lU\|password" --include="*.py" --include="*.md" --include="*.yaml" | grep -v ".example\|your_token\|your_password"
```
+84
View File
@@ -0,0 +1,84 @@
# Agent 系统 + CLI
## Agent 架构
4 个 Agent,通过依赖注入复用已有引擎(不重建轮子)。
```python
from agents.orchestrator import AgentOrchestrator
orch = AgentOrchestrator(dm=dm, fe=fe, bt=bt, opt=opt, sent=sent)
orch.setup() # 注册 4 个 Agent
results = orch.run_daily() # 5 步流程
```
## 5 步每日流程
```
[Step 1/5] 增量同步 → 只更新已缓存股票 (0.04s/只)
[Step 2/5] 风险评估 → RiskAgent: high/medium/low + 仓位
[Step 3/5] 股票打分 → SelectionAgent: 多因子等权打分
[Step 4/5] 情绪因子 → SentimentEngine.compute()
[Step 5/5] 生成日报 → ReportAgent: .md + .html + 解读
```
## 各 Agent 职责
| Agent | 文件 | 职责 |
|-------|------|------|
| ResearchAgent | `research_agent.py` | 因子 IC/IC_IR 评估 |
| SelectionAgent | `selection_agent.py` | 多因子股票打分(等权) |
| RiskAgent | `risk_agent.py` | 波动率+回撤→仓位建议 |
| ReportAgent | `report_agent.py` | 市场+选股+情绪+风险→日报 |
## CLI (`finance/cli/agent_cli.py`)
```bash
python finance/cli/agent_cli.py daily # 5 步流程
python finance/cli/agent_cli.py picks 15 # 选股
python finance/cli/agent_cli.py risk # 风险评估
python finance/cli/agent_cli.py research # 因子研究
python finance/cli/agent_cli.py report 20260603 # 生成日报
python finance/cli/agent_cli.py warmup 50 # 首次预热缓存
```
## Demo 脚本 (`finance/cli/demo_*.py`)
全部支持 `--ts_code` `--date` 等参数。
| 脚本 | 用途 |
|------|------|
| `demo_data_manager.py` | Sprint 0: DB→建表→日线→同步 |
| `demo_factor_engine.py` | Sprint 1: 34因子→NaN检查→截面 |
| `demo_backtest.py` | Sprint 2: 5策略回测 |
| `demo_optimizer.py` | Sprint 3: Optuna寻优+Walk-Forward |
| `demo_ml.py` | Sprint 4: LightGBM+CatBoost+回测 |
| `demo_sentiment.py` | Sprint 5: 情绪因子快速验证 |
| `demo_sentiment_detail.py` | Sprint 5: 情绪因子详细演示(14参数) |
## 报告 (`finance/reports/`)
- `daily_YYYYMMDD.md` + `.html` — 每日双格式输出
- `storage.py` — `save_report()` / `query_reports()` → 存入 mac_report 表
- 日报包含"昨日对比"区块:标注数据是否与前一日相同
## ⚠️ 已知 Bug 速查(避免重复踩坑)
### B6: 5 日/20 日涨跌幅恒为 0
- **症状**: 日报中 `| +0.00% | +0.00% |`,实际数据应该有非零值
- **根因**: `idx = daily.index.get_loc(date) if date in daily.index else -1`,目标日期不在索引时 `-1 >= 5` → False,计算被短路
- **修复**: 用 `pos = len(daily) - 1` 替代 `idx = -1`,确保位置值非负
### B7: 两日报告完全一致
- **症状**: 20260604 和 20260605 报告的 TOP 15、风险评估完全一样
- **根因**: ① T+1 数据未产出,两份报告基于同一份 DB 快照 ② 多因子在 1 天内变化极小(非 bug,是特性)
- **缓解**: 日报增加"昨日对比"区块 + 数据截止标注
### B8: 风险回撤恒为 -52%
- **症状**: 不管哪天看,风险等级一直是 high,回撤一直是 -52%
- **根因**: RiskAgent 默认用 `market_index="000001.SZ"`(平安银行个股),其 2020 年历史高价导致永远极端回撤
- **修复**: 改为 `"000001.SH"`(上证指数)
### B9: report 命令只 sync 一只股票
- **症状**: generate_report 只调了 `dm.sync_daily("000001.SZ")`,其他 279 只不变
- **影响**: 日报的选股排名不会反映最新行情
- **状态**: 已加 sync 调用,范围仍为单只(全量 sync 走 `daily` 命令或 `warmup`)
+148
View File
@@ -0,0 +1,148 @@
# DJAPI — Django API 参考
Django 5.2 项目,提供 A 股金融数据 API 和新闻联播视频处理能力。部署在 Linux 服务器上,通过 uWSGI + nginx 对外服务。
---
## 快速开始
```bash
# 开发服务器
python manage.py runserver 0.0.0.0:8000
# uWSGI 管理
uwsgi --ini uwsgi.ini
uwsgi --reload uwsgi.pid
uwsgi --stop uwsgi.pid
# API 文档
# /api/docs/ - Swagger UI
# /api/redoc/ - ReDoc
# /api/schema/ - OpenAPI Schema
```
## 部署
- 服务器:`simon@doorcome.cn`,路径 `/home/simon/myquant/djapi/`
- uWSGI 监听 `127.0.0.1:5004`,nginx 反向代理
- 虚拟环境:`/opt/miniconda/envs/django`(Python 3.10)
- 域名:`api.doorcome.cn`、`echart.doorcome.cn`
## 架构
```
djapi/
├── djapi/ # 项目配置
│ ├── settings.py # Django 设置、CORS、DRF
│ ├── urls.py # 根路由 + OpenAPI schema
│ └── env_loader.py # .env 加载器
├── api/ # 唯一 app
│ ├── views.py # 视图层(薄转发)
│ ├── urls.py # /api/* 路由
│ ├── serializers.py # DRF Serializer(13 个)
│ ├── stock/ # 股票数据模块
│ │ ├── data_source.py # 统一数据入口(全局单例)
│ │ ├── stock_utils.py # 通用工具
│ │ ├── stock_basic.py # 日线行情、基本信息
│ │ ├── getStockParam.py # 个股参数
│ │ ├── getStockEp.py # TTM / 季度 EPS
│ │ ├── getIndexs.py # 指数行情
│ │ ├── stockMargin.py # 融资融券
│ │ ├── getStockFina.py # 财务报表分析
│ │ ├── getStockDiv2.py # 股息率计算
│ │ └── xwlbDaily.py # 新闻联播数据
│ ├── video/ # 新闻联播视频处理(独立模块)
│ └── report/ # 日报查询 API
│ ├── query.py # 数据库查询
│ ├── views.py # 2 个视图
│ └── serializers.py # OpenAPI 文档
└── uwsgi.ini # uWSGI 配置
```
## 所有 API 端点(16 个)
基础 URL:`/api/`
| 端点 | 参数 | 说明 |
|------|------|------|
| `stockbasic/` | tscode, start_date, end_date | 日线行情 |
| `stockinfo/` | tscode | 个股基本信息 |
| `stockparam/` | tscode, start_date, end_date | 个股参数(市值等) |
| `industrys/` | industry | 按行业查股票列表 |
| `indexByName/` | index_name | 按名称查指数 |
| `indexDatas/` | tscode, start_date, end_date | 指数日行情 |
| `stockep/` | tscode, start_date, end_date | TTM EPS |
| `quarterlyEps/` | tscode, start_date, end_date | 季度 EPS |
| `finance/` | tscode, start_date, end_date | 财务报表分析 |
| `getdiv/` | tscode, start_date, end_date | 股息率(含 TTM) |
| `dailymargin/` | trade_date, exchange_id | 每日融资融券汇总 |
| `stockmargin/` | tscode, start_date, end_date | 个股融资融券 |
| `xwlbNews/` | start_date, end_date | 新闻联播(原始文本) |
| `xwlbFine/` | start_date, end_date | 新闻联播(AI 分割后) |
| `news/reports/` | report_type, start_date, end_date, id | 日报查询 |
| `news/events/` | days, importance, report_type, section, limit | 重要事件聚合 |
### 数据源
所有股票数据端点通过 `api/stock/data_source.py` 统一入口:
- `get_tushare_pro()` — 全局单例(线程安全)
- `get_daily()` — 双源 fallback (Tushare → AkShare)
- `get_mysql_db()` — MySQL 全局单例
## 日报查询 API
### `GET /api/news/reports/`
| 参数 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `report_type` | string | 两者 | `finance`(A 股)/ `intl`(国际) |
| `start_date` | string | 24h 前 | `YYYY-MM-DD` |
| `end_date` | string | 今天 | `YYYY-MM-DD` |
| `id` | int | 无 | 指定 id 返回单份详情(含事件) |
### `GET /api/news/events/`
| 参数 | 类型 | 默认 | 范围 | 说明 |
| --- | --- | --- | --- | --- |
| `days` | int | 7 | 1~365 | 最近 N 天 |
| `importance` | int | 4 | 1~5 | 最低重要度 |
| `report_type` | string | 两者 | `finance`/`intl` | 日报类型过滤 |
| `section` | string | 全部 | `xwlb`/`news`/`cninfo`/`intl` | 板块过滤 |
| `limit` | int | 100 | 1~500 | 返回条数上限 |
详细说明见 [news_report_api.md](news_report_api.md)。
## 新闻联播视频处理
离线批处理流水线:抓取视频 → 下载 → 提取音频 → ASR 转文字 → AI 分割+取标题 → 入库。
```bash
python api/video/main.py # 定时任务入口
python api/video/main_videos.py # 批量补缺
```
### 模块文件
| 文件 | 职责 |
|------|------|
| `getVideo5.py` | 主流程:抓取→下载→ASR→入库 |
| `audioRead.py` | 音频转换、分割、ASR 识别、文本纠错 |
| `deepseek.py` | DeepSeek API 封装 |
| `newsProcess.py` | AI 新闻分割+标题提取 |
| `newsRedo.py` | 手动重处理 |
| `main.py` | 定时任务入口 |
## 部署
详见 [部署说明](deployment.md)。
## 文档索引
| 文档 | 内容 |
|------|------|
| [使用指南](usage.md) | 各模块使用方法和代码示例 |
| [架构说明](architecture.md) | 项目架构、数据流、设计原则 |
| [开发指南](development.md) | 环境搭建、开发约定 |
| [部署说明](deployment.md) | 本地环境、服务器、uWSGI、rsync 部署 |
| [日报查询 API](news_report_api.md) | news/reports + news/events 详细说明 |
| [日报数据库](db_schema_v1.1.md) | news_report / news_event 表结构 |
+196
View File
@@ -0,0 +1,196 @@
# cc-cursor 项目架构
## 概述
cc-cursor 是一个 Mac Mini 单机量化研究平台,覆盖从数据获取到策略报告的全链路量化研究流程。
## 项目布局
```
cc-cursor/ # 项目根目录
│
├── finance/ # 🔥 核心量化引擎(代码实际位置)
│ ├── config/ # 全局配置:.env 加载、数据库/API 路径
│ ├── database/ # 数据库层:ORM 模型、SQLAlchemy 连接、DAO
│ ├── data/ # 数据层:DataManager 统一入口
│ │ └── sources/ # Tushare + AkShare 双数据源实现
│ ├── factors/ # 因子引擎:34 个注册因子
│ │ ├── technical/ # 技术因子(动量、RSI、MACD、布林等 10 类)
│ │ ├── fundamental/ # 基本面因子(ROE、PE、PB、EP)
│ │ └── sentiment/ # 情绪因子(Qwen NLP + 三源新闻聚合)
│ ├── backtest/ # 回测引擎:VectorBT 封装
│ │ ├── vectorbt/ # VectorBT 引擎适配
│ │ └── strategies/ # 5 个内置策略(均线、RSI、动量等)
│ ├── optimizer/ # 参数优化:Optuna 引擎 + Walk-Forward
│ ├── models/ # ML 模型:LightGBM + CatBoost + 特征工程
│ │ ├── lightgbm/ # LightGBM 模型封装
│ │ └── catboost/ # CatBoost 模型封装
│ ├── agents/ # Agent 系统:4 个 Agent + 编排器
│ ├── cli/ # 命令行入口:agent_cli + 7 个 demo 验证脚本
│ ├── reports/ # 日报输出:daily_YYYYMMDD.md + 存储层
│ ├── strategy/ # 策略层(空壳占位,仅 __init__.py)
│ ├── portfolio/ # 组合管理(空壳占位,仅 __init__.py)
│ ├── execution/ # 执行层(空壳占位,仅 __init__.py)
│ └── scheduler/ # 调度层(空壳占位,仅 __init__.py)
│
├── djapi/ # 🌐 Django API 后端
│ ├── djapi/ # 项目配置:settings、urls、env_loader
│ └── api/ # 唯一 Django app
│ ├── stock/ # A 股数据 API(16 端点)
│ ├── video/ # 新闻联播视频处理(独立模块)
│ └── report/ # 日报查询 API(news/reports + news/events)
│
├── shared/ # 🔧 共享工具
│ └── script/ # autossh.sh(MariaDB SSH 隧道)
│
├── docs/ # 📚 项目文档(13 个 md 文件)
│
├── .claude/ # Claude 配置(空目录,仅保留框架)
│
├── .git/ # Git 仓库
│
├── README.md # 项目入口文档
└── .gitignore # Git 忽略规则
```
### 各目录职责
| 目录 | 职责 | 状态 |
|------|------|------|
| `finance/` | **核心量化引擎**,全链路代码所在地 | ✅ 已实现 |
| `finance/config/` | 全局配置,`.env` 加载和路径管理 | ✅ |
| `finance/database/` | MariaDB 连接、ORM 模型、DAO 数据访问 | ✅ |
| `finance/data/` | 统一数据层,双源(Tushare→AkShare)fallback | ✅ |
| `finance/factors/` | 因子引擎,34 因子/12 分类(技术+基本面+情绪) | ✅ |
| `finance/backtest/` | VectorBT 回测,5 策略 + 截面回测 | ✅ |
| `finance/optimizer/` | Optuna 参数寻优 + Walk-Forward 验证 | ✅ |
| `finance/models/` | LightGBM/CatBoost ML 模型 + 特征工程 | ✅ |
| `finance/agents/` | 4 Agent(Research/Selection/Risk/Report)+ 编排器 | ✅ |
| `finance/cli/` | 命令行入口 + 7 个 demo 验证脚本 | ✅ |
| `finance/reports/` | 日报输出(daily_YYYYMMDD.md)+ 持久化存储 | ✅ |
| `finance/strategy/` | 策略层,预留扩展 | 🚧 空壳 |
| `finance/portfolio/` | 组合管理,预留扩展 | 🚧 空壳 |
| `finance/execution/` | 执行层,预留扩展 | 🚧 空壳 |
| `finance/scheduler/` | 调度层,预留扩展 | 🚧 空壳 |
| `djapi/` | Django API 后端,A 股数据 + 新闻联播 + 日报查询 | ✅ 已部署 |
| `shared/` | 跨项目共享工具(SSH 隧道脚本) | ✅ |
| `docs/` | 项目文档,13 个 md 文件 | ✅ |
## 数据流
```
Agent 编排层
├── ResearchAgent ── 因子发现(IC/IC_IR 评估)
├── SelectionAgent ─ 多因子打分 + ML 预测
├── RiskAgent ────── 仓位控制 + 风险预警
└── ReportAgent ──── 自动日报生成
基础引擎层
DataManager ──→ FactorEngine ──→ BaseStrategy ──→ VectorBTEngine ──→ BacktestReport
│ │ │
│ FeatureEngine OptunaEngine
│ │ │
└──────→ LightGBM/CatBoost ←────────┘
情绪增强层
NewsSource(AkShare/DB/MCP) ──→ QwenClient ──→ SentimentFactor ──→ FactorEngine
```
### 核心数据流
```
Data → Factor → Model → Strategy → Backtest → Report
```
## 设计原则
### 模块隔离
各引擎通过统一接口交互,可替换实现(VectorBT → Backtrader)。
### 接口标准化
- 因子:`calculate(df) → pd.Series`
- 策略:`generate_signals(df) → pd.Series`
- 模型:`fit/predict/save/load`
- 优化:`optimize() → OptimizationResult`
### 数据层统一
策略/模型不直连数据源,全部通过 `DataManager`。禁止:
- 策略直接访问 AkShare/Tushare
- 模型直接访问数据库
### Agent 不重建轮子
Agent 通过依赖注入复用已有引擎,编排而非重建。
### 防前视偏差
- 时间序列交叉验证(TimeSeriesSplit)
- expanding window 统计量
- 特征工程 fit 在训练集,transform 在测试集
## 技术栈
| 组件 | 技术 | 说明 |
|------|------|------|
| 数据获取 | AkShare + Tushare (双源) | Tushare 优先,AkShare fallback |
| 数据库 | MariaDB (SSH 隧道) | 本地 13306 → 远程 3306 |
| 因子/特征 | pandas / numpy / sklearn | — |
| 回测引擎 | VectorBT 1.0 | 只做多,10 万/万三 |
| 参数优化 | Optuna 4.9 | Walk-Forward 验证 |
| ML 模型 | LightGBM 4.6 + CatBoost 1.2 | 统一接口 |
| NLP 情绪 | Qwen (DashScope / Ollama) | 双后端 |
| Agent 编排 | 自研编排器 | finance/agents/ |
| API 后端 | Django 5.2 + uWSGI | djapi/ |
## 开发进度
| Sprint | 模块 | 状态 |
|--------|------|------|
| Sprint 0 | 基础设施(DataManager + MariaDB 3 表) | ✅ |
| Sprint 1 | 因子引擎(34 因子 / 12 分类) | ✅ |
| Sprint 2 | VectorBT 回测(5 策略 + 截面 + BacktestReport) | ✅ |
| Sprint 3 | Optuna 优化(+ Walk-Forward) | ✅ |
| Sprint 4 | ML 模型(LightGBM + CatBoost + MLStrategy) | ✅ |
| Sprint 5 | Qwen 情绪因子(三源新闻 + 日期对齐) | ✅ |
| Sprint 6 | Agent 系统(4 Agent + CLI + 日报 .md/.html) | ✅ |
| Sprint 7 | djapi API(日报查询 ×2) | ✅ |
**全部 8 个 Sprint 已完成。**
## 数据源架构
### finance/ 引擎层
```
DataManager
├── TushareSource(优先,需 TUSHARE_TOKEN)
├── AkShareSource(fallback,无需 token)
└── Database Cache(SQLAlchemy + MariaDB)
```
### djapi/ API 层
```
api/stock/data_source.py(统一入口)
├── get_tushare_pro() — 全局单例
├── get_daily() — 双源 fallback
└── get_mysql_db() — MySQL 全局单例
```
## 指数代码规则
- `.SH` 结尾且不以 `399` 开头 → 指数(如 `000001.SH`)
- `.SZ` 开头非 `399` → 个股(如 `000001.SZ`)
- `399*.SZ` → 指数(如 `399001.SZ`)
## 文档索引
| 文档 | 内容 |
|------|------|
| [使用指南](usage.md) | 各模块使用方法和代码示例 |
| [开发指南](development.md) | 环境搭建、开发约定、模块说明 |
| [因子与表结构速查](reference.md) | 34 因子注册表、DB 表结构、数据源接口 |
| [数据层详解](data-layer.md) | DataManager、数据库、缓存策略、已知 Bug |
| [因子引擎详解](factors.md) | 因子计算、情绪引擎、新闻源 |
| [回测引擎详解](backtest.md) | VectorBT、策略、信号工具、Optuna |
| [ML 模型详解](ml-models.md) | 特征工程、LightGBM/CatBoost、ML 策略 |
| [Agent 系统详解](agents.md) | Agent 架构、CLI、日报 |
| [部署说明](deployment.md) | 本地环境、服务器、uWSGI、rsync 部署 |
| [DJAPI 接口](api.md) | Django API 端点参考 |
| [日报查询 API](news_report_api.md) | news/reports + news/events 接口 |
+59
View File
@@ -0,0 +1,59 @@
# 回测引擎 + 参数优化
## VectorBTEngine (`finance/backtest/vectorbt/engine.py`)
只做多,10万/万三。
```python
from backtest.vectorbt.engine import VectorBTEngine
engine_bt = VectorBTEngine(initial_capital=100_000, commission=0.0003)
report = engine_bt.run(strategy, price_df, factor_df)
# → BacktestReport
report = engine_bt.run_cross_section(strategy, price_univ, factor_univ)
```
信号流:`1=buy, 0=sell, -1=hold` → `_signals_to_entries` → vbt.Portfolio.from_signals(direction="longonly")。
## 策略 (`finance/backtest/strategies/`)
| 策略 | 参数 | 逻辑 |
|------|------|------|
| `SMACrossStrategy` | fast=5, slow=20 | 金叉买/死叉卖 |
| `RSIMeanRevertStrategy` | oversold=30, overbought=70 | 超卖买/超买卖 |
| `MomentumBreakoutStrategy` | lookback=20, exit=10 | 新高买/跌破卖 |
| `FactorCrossStrategy` | factor_column, buy/sell_threshold | 阈值交叉(通用) |
| `FactorRotationStrategy` | factor_name, top_n=5 | 排序选股 |
## 自定义策略
继承 `backtest/base.py:BaseStrategy`,实现 `generate_signals(factor_df) → pd.Series`。
## 信号工具 (`backtest/signal.py`)
```python
factor_to_threshold_signal(series, buy, sell, direction)
cross_signal(fast, slow) # 金叉/死叉
factor_to_quantile_signal(...) # 分位数信号
```
## BacktestReport (`backtest/report.py`)
字段:total_return, cagr, max_drawdown, sharpe_ratio, calmar_ratio, annual_volatility, win_rate, profit_factor, total_trades, avg_hold_days, best/worst_trade_pct, equity_curve, drawdown_curve, monthly_returns, trades_df, stats_dict。`summary()` 一行摘要。
## OptunaEngine (`finance/optimizer/engine.py`)
```python
from optimizer.engine import OptunaEngine
from optimizer.space import rsi_revert_space
opt = OptunaEngine(bt_engine)
result = opt.optimize(StrategyClass, space, price_df, factor_df, metric="sharpe", n_trials=200)
# → OptimizationResult(best_params, best_value, best_report, trial_df, param_importance)
wf = opt.optimize_walk_forward(StrategyClass, space, price_df, factor_df,
train_window=756, test_window=252)
```
预置空间:`sma_cross_space`, `rsi_revert_space`, `momentum_breakout_space`, `factor_cross_space`(`optimizer/space.py`)。
目标指标:sharpe/cagr/calmar/total_return/return_over_dd。
+99
View File
@@ -0,0 +1,99 @@
# 数据层 + 数据库
## DataManager (`finance/data/data_manager.py`)
双数据源:Tushare(优先)→ AkShare(fallback)。DB 缓存优先。
```python
from data.data_manager import DataManager
dm = DataManager()
dm.init_db() # 首次建表(幂等)
```
### 关键方法
```python
stocks = dm.get_stock_list() # → 5524 只, DB 优先
daily = dm.get_daily("000001.SZ") # DB优先 → Tushare → AkShare
fina = dm.get_financial("000001.SZ") # 同花顺 + fallback
n = dm.sync_daily("000001.SZ") # 增量同步: latest>=today → 跳过
```
### 指数 vs 个股路由
`_try_fetch()` 自动检测 `is_index_code()`:
- `000001.SH` / `399001.SZ` → `fetch_index_daily()`(Tushare `index_daily` / AkShare `index_zh_a_hist`)
- `000001.SZ` / `600519.SH` → `fetch_daily()`(stock daily)
### 数据源 (`finance/data/sources/`)
- `akshare_source.py` — `AkShareSource`(个股+指数+财务)
- `tushare_source.py` — `TushareSource`(需 `TUSHARE_TOKEN`,`available=False` 自动跳过)
## 数据库 (`finance/database/`)
### 连接
```python
from database.connection import get_engine, test_connection
# get_engine() 自动检测断连 → 运行 autossh.sh → 重建引擎
```
### 表结构(mac_ 前缀)
| 表 | 内容 | 主键 |
|----|------|------|
| `mac_stock_basic` | A股列表, 5524只 | ts_code |
| `mac_stock_daily` | 日线 OHLCV | (ts_code, trade_date) |
| `mac_stock_financial` | 财务指标 | (ts_code, end_date) |
| `mac_report` | 报告持久化 | id |
### DAO (`finance/database/dao.py`)
- `_DAILY_COLS` / `_FINA_COLS` — 入库前字段筛选
- `get_latest_trade_date(ts_code)` → 增量判断
- `save_report()` / `query_reports()` → 报告管理
### 配置
```bash
# SSH 隧道
bash shared/script/autossh.sh
# host: 127.0.0.1:13306 user: myquant database: myquant
```
## 缓存策略
- `sync_daily`: 已缓存且最新 → 0.04s 跳过
- 未缓存 → 提示 `agent_cli.py warmup`
- 每日增量只更新已缓存股票,未缓存统计跳过
## ⚠️ 已知 Bug 速查(避免重复踩坑)
### B1: `.env` 加载路径
- **症状**: Tushare available=False, QWEN_API_KEY 读不到
- **根因**: `load_dotenv()` 不传路径,从 CWD 找 .env,而非 `finance/`
- **修复**: `config/settings.py` 用 `Path(__file__).parent.parent / ".env"`
- **加固**: `tushare_source.py`, `qwen_client.py`, `news_source.py` 顶部 `import config.settings`
### B2: SSH 重连只生效一次
- **症状**: 第一次断连能恢复,第二次断连无法恢复
- **根因**: `_ssh_auto_attempted` 全局变量设 True 后永不重置
- **修复**: 移除该变量。`get_engine()` 每次断连都触发 `_reconnect_ssh()` → `dispose()` → 重建
### B3: 连接池半开连接
- **症状**: `_test_engine()` 返回 True 但实际查询报 `_read_bytes` 超时
- **根因**: 池中有活连接也有死连接,test 取到活的,查询取到死的
- **修复**: `pool_pre_ping=True` + `pool_recycle=600` + 断连时 `_engine.dispose()`
### B4: save_daily 主键冲突
- **症状**: `IntegrityError: Duplicate entry '000001.SZ-20260603'`
- **根因**: Tushare 返回的数据含已存在的日期,`append` 模式遇主键冲突
- **修复**: 写入前 `DELETE FROM mac_stock_daily WHERE ts_code IN (...) AND trade_date IN (...)`
### B5: 数据不更新排查清单
- 检查 SSH 隧道: `lsof -i :13306 | grep LISTEN`
- 检查 Tushare: `python -c "from data.sources.tushare_source import TushareSource; print(TushareSource().available)"`
- 检查最新数据: `from database.dao import get_latest_trade_date; print(get_latest_trade_date('000001.SZ'))`
- 手动触发 sync: `dm.sync_daily('000001.SZ')`
- 预期行为: T+1 数据产出,节假日无新数据属于正常
+129
View File
@@ -0,0 +1,129 @@
# 日报结构化入库:数据库表结构与数据契约
> 版本:v1.1 | 2026-08-06
> 变更(v1.0 → v1.1):新增 §3.1 stats 口径说明,修正 §3 中 pipeline "M1→M6" 的过时描述(实际 key 为 raw_total/raw_by_source/proc 等)。
> 用途:供 API / 前端对接读取日报数据。表位于 MySQL `myquant` 库,表前缀 `news_`。
> 连接:`192.168.1.10:13306`(pi 上 autossh 隧道 → doorcome.cn:3306 MariaDB 10.11),用户 `myquant`(密码在服务器 `.env` 的 `NEWS_DB_PASSWORD`)。
---
## 1. 表结构
### 1.1 news_report(日报主表,一行 = 一份日报)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| id | BIGINT UNSIGNED PK | 自增主键 |
| report_date | DATE | 日报日期 |
| report_type | VARCHAR(16) | `finance`=A 股日报 / `intl`=国际财经日报 |
| file_name | VARCHAR(160) | 历史文件源文件名;**新生成日报为空字符串 `""`** |
| generated_at | DATETIME | 生成时间 |
| ai_summary | TEXT | AI 摘要全文(含换行,按条目分行) |
| stats | JSON | 数据总览统计快照(见第 3 节),可为 NULL |
| created_at | DATETIME | 入库时间 |
唯一键:`(report_date, report_type, file_name)` —— 历史同一天多次生成(intl 一日 3 次)保留多行;新生成日报 `file_name=''` 每天每类型仅一行,重复生成覆盖。
### 1.2 news_event(日报事件明细,一行 = 一条事件)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| id | BIGINT UNSIGNED PK | 自增主键 |
| report_id | BIGINT UNSIGNED | FK → news_report.id |
| section | VARCHAR(16) | 板块:`xwlb`=新闻联播 / `news`=财经新闻 / `cninfo`=公告调研 / `intl`=国际重要事件 |
| rank | INT | 板块内序号(1 起) |
| importance | INT NULL | 重要度 1-5 |
| event_type | VARCHAR(64) NULL | 事件类型(如 宏观经济/地缘政治/新闻联播/公告) |
| title | VARCHAR(512) | 标题 |
| summary | TEXT NULL | 摘要/正文 |
| sentiment | VARCHAR(8) NULL | `positive` / `negative` / `neutral` |
| source | VARCHAR(64) NULL | 来源(如 `cls`、`investinglive.com`) |
| sources | TEXT NULL | **该新闻全部来源**,JSON 数组字符串(如 `["yicai","stcn"]`);无多源/历史数据可为 `null` |
| url | VARCHAR(512) NULL | 原文链接(新闻联播为空) |
| created_at | DATETIME | 入库时间 |
索引:`idx_report_section (report_id, section)`。
---
## 2. 数据契约
- **幂等语义**:同一 `(report_date, report_type, file_name)` 重复写入会覆盖主表并全量替换事件(DELETE + INSERT),不会产生重复行。
- **取最新**:同一天存在多份时(历史 intl 一日 3 次),前端按 `generated_at` 取最新;新日报 `file_name=''` 每天唯一。
- **板块差异**:finance 日报含 `xwlb`+`news`+`cninfo` 三板块;intl 日报仅 `intl` 板块。前端按 `section` 过滤展示。
- **历史覆盖范围**:2026-06-16 ~ 2026-08-03,共 177 行(finance 49 + intl 128;finance 少 1 因为两个目录存在同名文件被幂等合并)。事件总计 4222 条。
---
## 3. stats JSON 结构
`news_report.stats` 为数据总览快照,前端自行解析。finance 与 intl 的 key 集合不同:
| key | finance | intl | 内容 |
| --- | --- | --- | --- |
| `pipeline` | ✅ | ✅ | 管道各环节数量。**finance 与 intl 内部 key 集不同**:finance 为 `{raw_total, raw_by_source, raw_total_24h, raw_by_source_24h, proc, deduped, dups, emb_count, qdrant_count, cninfo_raw}`;intl 为 `{raw_total, processed, deduped, embedded, qdrant}`(口径见 §3.1) |
| `sources` | ✅ | — | 各新闻源文章数:`{源名: 数量}` |
| `news` | ✅ | — | 新闻统计:`{total, hi_threshold, sentiments, importances, event_types}` |
| `cninfo` | ✅ | — | 公告调研统计:`{total, hi_threshold, by_day, announcement, research, irm}` |
| `xwlb` | ✅ | — | 联播统计:`{total, date}`(有数据时才有) |
| `sentiment` | ✅ | ✅ | 情绪分布(历史文件为图例文本列表;新生成在 `news.sentiments`) |
| `importance` | ✅ | ✅ | 重要度分布:`[{重要度, 数量}, ...]` |
| `event_types` | ✅ | ✅ | 事件类型 TOP:`[{事件类型, 数量}, ...]` |
| `source_dist` | — | ✅ | 文章来源分布:`[{来源, 文章数}, ...]` |
> 历史文件与新生成日报的 stats 结构存在差异(历史为 HTML 解析快照,新生成为结构化组装),前端建议按 key 防御性读取。
### 3.1 口径说明(重要,避免误解)
`stats` 内各数字口径不同,请勿直接互相比较:
| 字段 | 口径 |
| --- | --- |
| `pipeline.raw_total` | **日报日期当天**抓取的文章数(`data/raw/{src}/{date}/index.jsonl` 中 `stage=article 且 success` 的条目)。`raw_by_source` 是各源明细,**其和 = raw_total**;当天未抓取/无文章的源显示 0 |
| `pipeline.raw_total_24h` | 最近 24 小时内**抓取**(按 `fetched_at`)的文章数;`raw_by_source_24h` 为各源明细,和 = raw_total_24h。当天 07:00 抓取的数据其值 ≈ raw_total(并非"24h 内发布的新闻",raw 层无发布时间的可靠字段) |
| `pipeline.proc / deduped / dups / emb_count / qdrant_count` | 抽取 / 去重后 / 重复 / 向量化 / Qdrant 总量(`qdrant_count` 为全量累计,非当天) |
| `news.total` | **过去 30 小时窗口内**经 LLM 抽取的新闻事件数。**≠ raw_total**:raw 是抓取的文章数,news 是抽取后的事件数(会有过滤/合并),两者不可互相验证 |
| `news.importances` | `{重要度等级(1-5): 事件数}`,**各等级之和 = news.total** |
| `news.sentiments` | `{情绪: 事件数}`(positive/negative/neutral),和 = news.total |
| `news.event_types` | `{事件类型: 事件数}`(TOP 10) |
| `pipeline.*(finance)` | finance 日报专用:`proc`=抽取后条数、`deduped`=去重后、`dups`=重复条数、`emb_count`=向量化条数、`qdrant_count`=Qdrant 全量累计(非当天)、`cninfo_raw`=当天 cninfo 抓取数。`raw_by_source` 各源之和 = `raw_total` |
| `pipeline.*(intl)` | intl 日报**结构不同**:`raw_total`=当天抓取文章数、`processed`=处理数(≈ raw_total)、`deduped`=去重后条数、`embedded`=向量化条数、`qdrant`=Qdrant 全量累计。intl 无 `raw_by_source` 明细与 `cninfo_raw` |
---
## 4. 常用查询示例(API 实现参考)
```sql
-- 某类型日报列表(取每天最新一份)
SELECT r.* FROM news_report r
JOIN (
SELECT report_date, report_type, MAX(generated_at) AS g
FROM news_report GROUP BY report_date, report_type
) t ON r.report_date = t.report_date AND r.report_type = t.report_type
AND r.generated_at = t.g
WHERE r.report_type = 'finance' AND r.report_date >= '2026-07-01'
ORDER BY r.report_date DESC;
-- 某日报的全部事件(按板块)
SELECT section, rank, importance, event_type, title, summary, sentiment, source, url
FROM news_event WHERE report_id = ? ORDER BY section, rank;
-- 最近 N 天重要事件聚合(跨日报检索)
SELECT e.* FROM news_event e
JOIN news_report r ON r.id = e.report_id
WHERE r.report_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
AND e.importance >= 4
ORDER BY e.importance DESC, r.report_date DESC;
```
---
## 5. 相关命令(数据生产侧)
```bash
uv run a-share report --date YYYYMMDD # 生成当日日报并入库(finance)
uv run a-share report-import # 历史 HTML 全量解析入库(幂等)
uv run a-share report-import --date YYYYMMDD --type intl
```
代码:`report_db/`(连接/写入)、`report_import/`(历史解析/导入)、`scheduler/reporter.py`(日报生成)。
+274
View File
@@ -0,0 +1,274 @@
# 部署说明
cc-cursor 包含两个运行组件:**finance 量化引擎**(Mac Mini 本地)和 **djapi API 后端**(Linux 服务器)。部署架构如下:
```
┌─ Mac Mini (本地) ─────────────────────────────────────────────┐
│ │
│ finance/ 量化引擎 │
│ ├── 数据获取 (AkShare/Tushare) │
│ ├── 因子计算 / 回测 / ML │
│ └── Agent 日报生成 │
│ │
│ shared/script/autossh.sh │
│ └── SSH 隧道 :13306 ──────────────────────┐ │
│ │ │
└─────────────────────────────────────────────┼──────────────────┘
│
MariaDB 10.11 │
doorcome.cn:3306│
│
┌─ Linux 服务器 (doorcome.cn) ────────────────┼──────────────────┐
│ │ │
│ djapi/ Django API │ │
│ ├── uWSGI :5004 │ │
│ ├── nginx 反向代理 │ │
│ └── 域名: api.doorcome.cn │ │
│ │ │
│ MariaDB myquant 库 │ │
│ ├── mac_* 表 (量化引擎数据) │ │
│ ├── xwlb_* 表 (新闻联播) │ │
│ └── news_* 表 (日报) │ │
│ │ │
└─────────────────────────────────────────────┘ │
```
---
## 1. 本地环境(Mac Mini)
### 1.1 Python 环境
```bash
conda activate quant # Python 3.11.13
```
### 1.2 环境变量
复制并编辑 `finance/.env`(参考 `finance/.env.example`):
```bash
TUSHARE_TOKEN=your_token_here
QWEN_API_KEY=sk-your-key-here
MAC_DB_HOST=127.0.0.1
MAC_DB_PORT=13306
MAC_DB_USER=myquant
MAC_DB_PASSWORD=your_password_here
MAC_DB_NAME=myquant
```
### 1.3 数据库 SSH 隧道
```bash
bash shared/script/autossh.sh
```
脚本内容:
```bash
autossh -M 0 -fN -L 13306:localhost:3306 tunnel@doorcome.cn
```
验证隧道:
```bash
lsof -i :13306 | grep LISTEN
```
连接信息:
```
Host: 127.0.0.1
Port: 13306
User: myquant
Database: myquant
```
---
## 2. 服务器环境(doorcome.cn)
### 2.1 基本信息
| 项目 | 值 |
|------|-----|
| 服务器 | `simon@doorcome.cn` |
| 项目路径 | `/home/simon/myquant/djapi/` |
| Python 环境 | `/opt/miniconda/envs/django` (Python 3.10) |
| uWSGI 端口 | `127.0.0.1:5004` |
| nginx 反向代理 | 域名 → `127.0.0.1:5004` |
| 生产域名 | `api.doorcome.cn`、`echart.doorcome.cn` |
### 2.2 服务器环境变量
服务器端 `djapi/.env`(**不随代码同步**,需在服务器上手动维护):
```bash
DJANGO_SECRET_KEY=...
TUSHARE_TS_TOKEN=...
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=myquant
MYSQL_PASSWORD=...
MYSQL_DATABASE=myquant
NEWS_DB_HOST=127.0.0.1
NEWS_DB_PORT=3306
NEWS_DB_USER=myquant
NEWS_DB_PASSWORD=...
NEWS_DB_NAME=myquant
DEEPSEEK_API_KEY=...
DASHSCOPE_API_KEY=...
```
### 2.3 uWSGI 配置
配置文件:`djapi/uwsgi.ini`
```ini
[uwsgi]
http = 127.0.0.1:5004
chdir = /home/simon/myquant/djapi
module = djapi.wsgi:application
uid = simon
gid = simon
master = true
workers = 5
pidfile = /home/simon/myquant/djapi/uwsgi.pid
vacuum = true
thunder-lock = true
enable-threads = true
harakiri = 30
post-buffering = 4096
daemonize = /home/simon/myquant/djapi/uwsgi.log
log-maxsize = 10240000
py-autoreload = 1
virtualenv = /opt/miniconda/envs/django
env = DJANGO_SETTINGS_MODULE=djapi.settings
```
### 2.4 uWSGI 管理命令
```bash
# 启动
uwsgi --ini uwsgi.ini
# 热重载
uwsgi --reload uwsgi.pid
# 停止
uwsgi --stop uwsgi.pid
# 强制停止
kill $(lsof -ti:5004)
```
---
## 3. 代码部署
### 3.1 全量同步
```bash
rsync -avz --delete \
--exclude='.env' --exclude='db.sqlite3' \
--exclude='*.log' --exclude='uwsgi.pid' \
--exclude='__pycache__/' --exclude='*.pyc' \
--exclude='xwlb_video/' --exclude='audio_processing/' \
/path/to/cc-cursor/djapi/ \
simon@doorcome.cn:/home/simon/myquant/djapi/
```
### 3.2 单文件同步
```bash
# 必须写完整目标路径,否则会展平到根目录
rsync -avz api/views.py simon@doorcome.cn:/home/simon/myquant/djapi/api/views.py
```
### 3.3 重启服务
```bash
ssh simon@doorcome.cn "kill \$(lsof -ti:5004); sleep 2; /opt/miniconda/envs/django/bin/uwsgi --ini /home/simon/myquant/djapi/uwsgi.ini"
```
### 3.4 部署后验证
```bash
# Swagger 文档
curl -s https://api.doorcome.cn/api/docs/ | head -5
# 日报查询
curl -s 'https://api.doorcome.cn/api/news/reports/' | python -m json.tool | head -20
# 健康检查(冒烟)
curl -s -o /dev/null -w "%{http_code}" 'https://api.doorcome.cn/api/news/reports/'
# → 200
```
---
## 4. 数据库
### 4.1 表前缀
| 前缀 | 用途 | 位置 |
|------|------|------|
| `mac_` | 量化引擎数据(股票列表、日线、财务、报告) | finance 引擎写入 |
| `xwlb_` | 新闻联播数据 | djapi video 模块写入 |
| `news_` | 日报数据 | 外部 pipeline 写入,djapi 只读 |
### 4.2 量化引擎表
| 表 | 内容 | 主键 |
|----|------|------|
| `mac_stock_basic` | A 股列表 (5,524 只) | ts_code |
| `mac_stock_daily` | 日线 OHLCV | (ts_code, trade_date) |
| `mac_stock_financial` | 财务指标 | (ts_code, end_date) |
| `mac_report` | 报告持久化 | id |
### 4.3 日报表
| 表 | 内容 |
|----|------|
| `news_report` | 日报主表(一行 = 一份日报) |
| `news_event` | 日报事件明细(一行 = 一条事件) |
详见 [db_schema_v1.1.md](db_schema_v1.1.md)。
---
## 5. 开发环境
### 5.1 本地运行 djapi
```bash
cd djapi
python manage.py runserver 0.0.0.0:8000
```
### 5.2 API 文档
- Swagger UI:`/api/docs/`
- ReDoc:`/api/redoc/`
- OpenAPI Schema:`/api/schema/`
### 5.3 数据库初始化
```bash
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
```
---
## 6. 文档索引
| 文档 | 内容 |
|------|------|
| [使用指南](usage.md) | 各模块使用方法和代码示例 |
| [架构说明](architecture.md) | 项目架构、数据流、设计原则 |
| [开发指南](development.md) | 环境搭建、开发约定 |
| [DJAPI 接口](api.md) | Django API 端点参考 |
| [日报查询 API](news_report_api.md) | news/reports + news/events 接口 |
| [日报数据库](db_schema_v1.1.md) | news_report / news_event 表结构 |
+271
View File
@@ -0,0 +1,271 @@
# cc-cursor 开发指南
## 环境搭建
### 1. 克隆项目
```bash
git clone https://github.com/Simon2046/myquant.git
cd cc-cursor
```
### 2. Python 环境
```bash
conda activate quant # Python 3.11.13
```
### 3. 数据库 SSH 隧道
```bash
bash shared/script/autossh.sh
# host: 127.0.0.1:13306 user: myquant database: myquant
```
### 4. 环境变量配置
复制并编辑 `finance/.env`(参考 `finance/.env.example`):
```bash
TUSHARE_TOKEN=your_token_here
QWEN_API_KEY=sk-your-key-here
MAC_DB_PASSWORD=your_password_here
```
### 5. 验证环境
```bash
python finance/cli/demo_data_manager.py
```
## 项目结构
```
finance/ # 核心量化引擎(代码实际位置)
├── config/ # 全局配置
│ └── settings.py # .env 加载 + 路径配置
├── database/ # 数据库层
│ ├── connection.py # SQLAlchemy 引擎 + SSH 自动恢复
│ ├── models.py # ORM 模型(mac_ 前缀表)
│ └── dao.py # 数据访问对象
├── data/ # 数据层
│ ├── data_manager.py # 统一数据入口
│ └── sources/ # 数据源实现
│ ├── tushare_source.py
│ └── akshare_source.py
├── factors/ # 因子引擎
│ ├── base.py # 因子基类
│ ├── engine.py # 因子计算引擎
│ ├── registry.py # 因子注册表
│ ├── technical/ # 技术因子(10 类)
│ ├── fundamental/ # 基本面因子
│ └── sentiment/ # 情绪因子
│ ├── sentiment_engine.py
│ ├── sentiment_factor.py
│ ├── news_source.py # 三源新闻聚合
│ └── qwen_client.py # Qwen API 客户端
├── backtest/ # 回测引擎
│ ├── base.py # 策略基类
│ ├── report.py # 回测报告
│ ├── signal.py # 信号工具
│ ├── vectorbt/ # VectorBT 引擎
│ └── strategies/ # 内置策略
├── optimizer/ # 参数优化
│ ├── engine.py # Optuna 引擎
│ ├── space.py # 搜索空间
│ ├── objectives.py # 优化目标
│ └── result.py # 优化结果
├── models/ # ML 模型
│ ├── base.py # 模型基类
│ ├── features.py # 特征工程
│ ├── backtest_integration.py # ML 策略
│ ├── lightgbm/
│ └── catboost/
├── agents/ # Agent 系统
│ ├── base.py # Agent 基类
│ ├── orchestrator.py # 编排器
│ ├── research_agent.py # 因子研究
│ ├── selection_agent.py # 股票打分
│ ├── risk_agent.py # 风险评估
│ └── report_agent.py # 日报生成
├── cli/ # 命令行
│ ├── agent_cli.py # Agent CLI 入口
│ └── demo_*.py # 验证脚本
└── reports/ # 日报输出
└── storage.py # 报告持久化
```
## 开发约定
### 代码组织
- 代码内 import 用顶层名 `data.*`/`factors.*` 等 — CLI 自动把 `finance/` 加入 sys.path
- 文件路径为 `finance/data/xxx.py` 等
### 数据流约束
```
Data → Factor → Model → Strategy → Backtest → Report
```
**必须遵守**:
- 策略层禁止直接访问 AkShare/Tushare → 全部通过 `DataManager`
- 模型层禁止直接访问数据库 → 全部通过 `DataManager`
- 指数代码规则:`.SH`=指数, `.SZ` 开头非 399=个股
- 数据源优先级:Tushare → AkShare (fallback)
### 接口规范
#### 因子接口
```python
class BaseFactor:
def calculate(self, df: pd.DataFrame) -> pd.Series:
"""接收 OHLCV 数据,返回因子值序列"""
```
#### 策略接口
```python
class BaseStrategy:
def generate_signals(self, factor_df: pd.DataFrame) -> pd.Series:
"""接收因子数据,返回交易信号: 1=buy, 0=sell, -1=hold"""
```
#### 模型接口
```python
class BaseModel:
def fit(self, X, y): ...
def predict(self, X) -> np.ndarray: ...
def save(self, path): ...
@classmethod
def load(cls, path): ...
```
### 多步任务规则
复杂任务(涉及 3+ 文件或 2+ 模块)执行前:
1. 输出执行计划清单(步骤 + 每步验证方法)
2. 每步完成后验证通过才继续
3. 遇到失败先定位根因,不跳过
### 修改多文件前
先说明:文件清单、原因、影响;优先小范围修改。
## 模块说明
### 数据层 (`finance/data/`)
双数据源架构:Tushare(优先)→ AkShare(fallback),DB 缓存优先。
```python
from data.data_manager import DataManager
dm = DataManager()
dm.init_db() # 首次建表(幂等)
stocks = dm.get_stock_list() # → 5,524 只
daily = dm.get_daily("000001.SZ") # → 日线
fina = dm.get_financial("000001.SZ") # → 财务
n = dm.sync_daily("000001.SZ") # → 增量同步
```
### 因子引擎 (`finance/factors/`)
34 个注册因子,12 个分类:动量、RSI、MACD、量价、布林、ATR、均线、波动率、换手率、振幅、基本面、情绪。
```python
from factors.registry import get_factor, list_factors
from factors.engine import FactorEngine
fe = FactorEngine(dm)
factor_df = fe.compute("000001.SZ", [get_factor("momentum_20"), get_factor("rsi_14")])
```
### 回测引擎 (`finance/backtest/`)
VectorBT 1.0,只做多,10 万/万三。5 个内置策略 + 自定义策略接口。
```python
from backtest.vectorbt.engine import VectorBTEngine
engine_bt = VectorBTEngine(initial_capital=100_000, commission=0.0003)
report = engine_bt.run(strategy, price_df, factor_df)
```
### 参数优化 (`finance/optimizer/`)
Optuna 4.9 + Walk-Forward 滚动验证。
```python
from optimizer.engine import OptunaEngine
opt = OptunaEngine(engine_bt)
result = opt.optimize(StrategyClass, space, price_df, factor_df, metric="sharpe", n_trials=200)
```
### ML 模型 (`finance/models/`)
LightGBM 4.6 + CatBoost 1.2,统一接口,特征工程防前视偏差。
```python
from models.features import FeatureEngine
from models.lightgbm.model import LightGBMModel
fe = FeatureEngine(lookahead=5)
X, y = fe.build(factor_df, price_df, fit=True)
model = LightGBMModel(params={"n_estimators": 200}).fit(X_train, y_train)
```
### 情绪因子 (`finance/factors/sentiment/`)
三数据源聚合(AkShare 个股新闻 + 新闻联播 DB + MCP trendradar-news),Qwen DashScope + Ollama 双后端。
```python
from factors.sentiment.sentiment_engine import SentimentEngine
sent = SentimentEngine(dm)
sent_df = sent.compute("000001.SZ", max_news=20)
```
### Agent 系统 (`finance/agents/`)
4 个 Agent(Research/Selection/Risk/Report)+ 编排器 + CLI。
```bash
python finance/cli/agent_cli.py daily # 完整流程
python finance/cli/agent_cli.py picks 15 # 选股
python finance/cli/agent_cli.py risk # 风险评估
```
## 安全规范
### 禁止提交
- `.env`(含真实 key)
- API Key(`sk-*`、`TUSHARE_TOKEN` 等)
- Cookie / Session / Token / 密钥
- 个人隐私数据(手机号、身份证、密码)
### 必须提供
- `.env.example` — 仅含占位符的示例配置
### 提交前检查
```bash
grep -r "sk-\|token\|password" --include="*.py" --include="*.md" --include="*.yaml" | grep -v ".example\|your_token\|your_password"
```
## 已知 Bug 速查
详见 [data-layer.md](data-layer.md) 和 [agents.md](agents.md) 中的 Bug 列表。
## 文档索引
| 文档 | 内容 |
|------|------|
| [使用指南](usage.md) | 各模块使用方法和代码示例 |
| [架构说明](architecture.md) | 项目架构、数据流、设计原则 |
| [因子与表结构速查](reference.md) | 34 因子注册表、DB 表结构、数据源接口 |
| [数据层详解](data-layer.md) | DataManager、数据库、缓存策略、已知 Bug |
| [因子引擎详解](factors.md) | 因子计算、情绪引擎、新闻源 |
| [回测引擎详解](backtest.md) | VectorBT、策略、信号工具、Optuna |
| [ML 模型详解](ml-models.md) | 特征工程、LightGBM/CatBoost、ML 策略 |
| [Agent 系统详解](agents.md) | Agent 架构、CLI、日报 |
| [部署说明](deployment.md) | 本地环境、服务器、uWSGI、rsync 部署 |
| [DJAPI 接口](api.md) | Django API 端点参考 |
+55
View File
@@ -0,0 +1,55 @@
# 因子引擎 + 情绪因子
## FactorEngine (`finance/factors/engine.py`)
```python
from factors.registry import get_factor, list_factors
from factors.engine import FactorEngine
fe = FactorEngine(dm, sentiment_engine=sent)
factor_df = fe.compute("000001.SZ", [get_factor("momentum_20"), get_factor("rsi_14")])
# → DataFrame: index=trade_date, columns=[momentum_20, rsi_14]
cross = fe.compute_universe(factors, date="20250630", ts_codes=[...])
```
因子路由:`factor.category == "sentiment"` → SentimentEngine;`isinstance(f, FUNDAMENTAL_FACTOR_TYPES)` → 注入财务数据。
## 因子注册表 (`finance/factors/registry.py`)
34 因子 / 12 分类:动量、RSI、MACD、量价、布林、ATR、均线、波动率、换手率、振幅、基本面、情绪。
```python
list_factors("动量") # → ['momentum_5','momentum_10','momentum_20','momentum_60']
get_factor("rsi_14") # → RSIFactor(period=14)
```
## 技术因子 (`finance/factors/technical/`)
10 类。每个继承 `BaseFactor`,实现 `calculate(df) → pd.Series`。停牌跳过,新股不足 N 日返回 NaN。
## 基本面因子 (`finance/factors/fundamental/`)
- `roe.py` — ROEFactor(同花顺 `stock_financial_abstract_ths`)
- `pe_pb.py` — PEFactor/PBFactor/EPFactor(close + 财务 EPS/BVPS map to daily)
## 情绪因子 (`finance/factors/sentiment/`)
### SentimentEngine (`sentiment_engine.py`)
```python
sent = SentimentEngine(dm, qwen_client=client, news_source=news_src)
sent_df = sent.compute("000001.SZ", max_news=30)
# → DataFrame: news_sent_5, news_conf_5, sent_delta_5
```
### 新闻源 (`news_source.py`)
三数据源聚合:AkShare `stock_news_em` + DB `xwlb_daily_ext` + MCP `trendradar-news`。
xwlb 日期处理:DB `news_date +1day`(晚间播出→次日影响)→ `align_news_to_trading_days`。
AkShare 限频 → 自动 fallback。LIMIT 按日期跨度动态计算。
### Qwen 客户端 (`qwen_client.py`)
双后端:DashScope API + 本地 Ollama。金融情绪专家 prompt,返回 `{sentiment_score, confidence, impact_duration, key_topics}`。配置:`.env` 中的 `QWEN_API_KEY` 或 `QWEN_LOCAL_BASE_URL`。
### 情绪因子 (`sentiment_factor.py`)
- `NewsSentimentFactor(window, decay)` — 时间衰减加权情绪
- `SentimentConfidenceFactor(window)` — score × confidence 加权
- `SentimentMomentumFactor(period)` — 情绪变化方向
+45
View File
@@ -0,0 +1,45 @@
# ML 模型层
## FeatureEngine (`finance/models/features.py`)
因子 → 特征矩阵 + 标签。防前视偏差。
```python
from models.features import FeatureEngine
fe = FeatureEngine(lookahead=5, label_type="regression")
X, y = fe.build(factor_df, price_df, fit=True)
# fit=True: Winsorize(1%/99%) → ffill → median fill → RobustScaler.fit → 标签计算
# fit=False: 复用训练时的 scaler + 有效特征
```
## LightGBM (`finance/models/lightgbm/model.py`)
```python
from models.lightgbm.model import LightGBMModel
model = LightGBMModel(params={"n_estimators": 200, "learning_rate": 0.03}, eval_ratio=0.2)
model.fit(X_train, y_train) # val>=50 行才启用早停
pred = model.predict(X_test)
imp = model.get_feature_importance() # → DataFrame
cv = model.cv_evaluate(X, y, n_folds=5) # TimeSeriesSplit
```
## CatBoost (`finance/models/catboost/model.py`)
同接口。`get_feature_importance()` / `cv_evaluate()`。
## ML 策略 (`finance/models/backtest_integration.py`)
```python
from models.backtest_integration import MLStrategy, MLBenchmark
strategy = MLStrategy(model, fe, buy_quantile=0.7, sell_quantile=0.3, rebalance_freq=5)
# 预测值分位 → 动态阈值 → 交易信号
benchmark = MLBenchmark([lgb, cb], fe, price_df, factor_df)
result = benchmark.run() # → DataFrame: model × (IC, return, sharpe, win_rate, trades)
```
## 重要约束
- lookahead 固定,不输入模型(防目标泄露)
- 单股票 IC≈0 是正常现象(噪声主导),多股票截面才是 ML 发挥价值的地方
- 特征工程严禁使用未来数据(RobustScaler fit 在训练集,transform 在测试集)
- 交叉验证用 TimeSeriesSplit(不 shuffle)
+35 -13
View File
@@ -1,7 +1,7 @@
# 日报查询 API 使用手册(news_report / news_event)
> 版本:v1.1 | 2026-08-03(已部署至生产 `api.doorcome.cn`)
> 数据表定义见 `djapi/docs/db_schema.md`,数据生产侧设计见 `djapi/docs/report_db_design.md`。
> 数据表定义见 [db_schema_v1.1.md](db_schema_v1.1.md)。
> 本文档为前端/调用方对接手册:两个只读查询接口的线上地址、参数、curl 用法与返回结构。
---
@@ -61,11 +61,22 @@ Swagger 交互式文档(自动生成,含全部参数说明):`https://api
"generated_at": "2026-08-03T07:00:00",
"ai_summary": "……(AI 摘要全文,按条目分行)",
"stats": {
"pipeline": { "M1": 210, "M2": 205, "M3": 198, "M4": 190, "M5": 188, "M6": 180 },
"sources": { "cls": 95, "eastmoney": 60, "other": 25 },
"news": { "total": 180, "hi_threshold": 18, "sentiments": {...}, "importances": [...], "event_types": [...] },
"cninfo": { "total": 58, "hi_threshold": 4, "by_day": {...}, "announcement": 40, "research": 15, "irm": 3 },
"xwlb": { "total": 28, "date": "2026-08-03" }
"pipeline": {
"raw_total": 116,
"raw_total_24h": 116,
"raw_by_source": { "经济观察网": 31, "新浪财经": 27, "证券时报": 28, "财联社": 15, "东方财富": 7, "中证券网": 2, "第一财经": 3, "中国证券网": 3 },
"raw_by_source_24h": { ... },
"proc": ..., "deduped": ..., "dups": ..., "emb_count": ..., "qdrant_count": ..., "cninfo_raw": ...
},
"news": {
"total": 643,
"hi_threshold": 4,
"sentiments": { "neutral": 470, "positive": 123, "negative": 50 },
"importances": { "1": 115, "2": 275, "3": 185, "4": 48, "5": 20 },
"event_types": { "其他": 287, "国际局势": 107, "宏观政策": 62, "行业政策": 49, "财报披露": 29 }
},
"cninfo": { "total": 48, "hi_threshold": 2, "by_day": { "2026-08-06": 8, "2026-08-01": 9 }, "announcement": 47, "research": 1, "irm": 0 },
"xwlb": { "total": 21, "date": "08月05日" }
},
"created_at": "2026-08-03T07:01:00"
},
@@ -77,18 +88,26 @@ Swagger 交互式文档(自动生成,含全部参数说明):`https://api
"generated_at": "2026-08-03T07:00:00",
"ai_summary": "……",
"stats": {
"pipeline": { "M1": 150, "M2": 145, "M3": 140, "M4": 135, "M5": 130, "M6": 125 },
"sentiment": [...],
"importance": [{ "重要度": 5, "数量": 3 }, { "重要度": 4, "数量": 12 }],
"event_types": [{ "事件类型": "地缘政治", "数量": 8 }],
"source_dist": [{ "来源": "investinglive.com", "文章数": 45 }]
"pipeline": { "raw_total": 442, "processed": 442, "deduped": 111, "embedded": 112, "qdrant": 2952 },
"importance": [
{ "importance": 2, "count": 2 }, { "importance": 3, "count": 31 },
{ "importance": 4, "count": 26 }, { "importance": 5, "count": 5 }
],
"event_types": [
{ "event_type": "宏观经济", "count": 15 }, { "event_type": "地缘政治", "count": 12 },
{ "event_type": "财报披露", "count": 9 }, { "event_type": "央行决议", "count": 7 }
],
"source_dist": [
{ "source": "InvestingLive", "count": 30 }, { "source": "MarketWatch", "count": 3 },
{ "source": "CNBC", "count": 3 }, { "source": "Yahoo Finance", "count": 1 }
]
},
"created_at": "2026-08-03T07:01:00"
}
]
```
> `stats` 为 JSON 快照:finance 含 `pipeline/sources/news/cninfo/xwlb`,intl 含 `pipeline/sentiment/importance/event_types/source_dist`。前端按 key 防御性读取(见 db_schema.md §3)。
> `stats` 为 JSON 快照(示例为 2026-08-06 真实数据):finance 含 `pipeline/news/cninfo/xwlb`,intl 含 `pipeline/importance/event_types/source_dist`(另有 `sentiment`)。finance 与 intl 的 pipeline 内部 key 集**不同**(finance: `raw_total/raw_by_source/proc/dups/...`;intl: `raw_total/processed/deduped/embedded/qdrant`),前端按 key 防御性读取;各数字口径(`raw_total` ≠ `news.total` 等)见 `db_schema_v1.1.md` §3.1。
**传 `id`** → 单份详情(对象),增加 `events` 数组(按 `section, rank` 排序)。真实返回(id=182,58 条事件):
@@ -179,12 +198,15 @@ curl 'https://api.doorcome.cn/api/news/reports/?id=182'
"title": "特朗普称已取消对伊朗的袭击计划,因双方就协议框架达成一致",
"summary": "……",
"sentiment": "neutral",
"source": "investinglive.com",
"source": "InvestingLive",
"sources": ["InvestingLive"],
"url": "https://www.investing.com/..."
}
]
```
> `sources` 为该新闻的**全部来源**(JSON 数组,字符串列表);`source` 为主来源(单值)。多源事件(如同一新闻被多家媒体转载)时 `sources` 含多个元素;历史事件可能为 `null`。
### curl 示例
```bash
+59
View File
@@ -0,0 +1,59 @@
# 因子 + 表结构速查
## 因子速查(34 个,12 分类)
| 注册名 | 类 | 参数 | 文件 |
|--------|-----|------|------|
| `momentum_5/10/20/60` | MomentumFactor | `period=N` | `factors/technical/momentum.py` |
| `rsi_7/14` | RSIFactor | `period=N` | `factors/technical/rsi.py` |
| `macd` | MACDFactor | `fast=12,slow=26,signal=9` | `factors/technical/macd.py` |
| `macd_5_35_5` | MACDFactor | `fast=5,slow=35,signal=5` | `factors/technical/macd.py` |
| `vol_ratio_5/20` | VolumeRatioFactor | `period=N` | `factors/technical/volume.py` |
| `vol_chg_5` | VolumeChangeFactor | `period=5` | `factors/technical/volume.py` |
| `boll` | BollingerPositionFactor | `period=20` | `factors/technical/bollinger.py` |
| `boll_width` | BollingerWidthFactor | `period=20` | `factors/technical/bollinger.py` |
| `atr_14` | ATRFactor | `period=14` | `factors/technical/atr.py` |
| `atr_ratio_14` | ATRRatioFactor | `period=14` | `factors/technical/atr.py` |
| `ma_cross_5_20` | MACrossFactor | `fast=5,slow=20` | `factors/technical/ma_cross.py` |
| `ma_cross_10_60` | MACrossFactor | `fast=10,slow=60` | `factors/technical/ma_cross.py` |
| `ma_dev_20/60` | MADevFactor | `period=N` | `factors/technical/ma_cross.py` |
| `volatility_20/60` | VolatilityFactor | `period=N` | `factors/technical/volatility.py` |
| `down_vol_20` | DownsideVolatilityFactor | `period=20` | `factors/technical/volatility.py` |
| `turnover_5` | TurnoverFactor | `period=5` | `factors/technical/turnover.py` |
| `turnover_chg_5` | TurnoverChangeFactor | `period=5` | `factors/technical/turnover.py` |
| `amplitude_5/20` | AmplitudeFactor | `period=N` | `factors/technical/amplitude.py` |
| `roe` | ROEFactor | — | `factors/fundamental/roe.py` |
| `pe` | PEFactor | — | `factors/fundamental/pe_pb.py` |
| `pb` | PBFactor | — | `factors/fundamental/pe_pb.py` |
| `ep` | EPFactor | — | `factors/fundamental/pe_pb.py` |
| `news_sent_5/20` | NewsSentimentFactor | `window=N,decay=0.3` | `factors/sentiment/sentiment_factor.py` |
| `news_conf_5` | SentimentConfidenceFactor | `window=5` | `factors/sentiment/sentiment_factor.py` |
| `sent_delta_5` | SentimentMomentumFactor | `period=5` | `factors/sentiment/sentiment_factor.py` |
## DB 表速查
| 表 (mac_) | 主键 | 关键列 | 用途 |
|-----------|------|--------|------|
| `stock_basic` | ts_code | ts_code, name, area, industry | 股票列表 (5524行) |
| `stock_daily` | (ts_code, trade_date) | open, high, low, close, vol, amount, turnover_rate | 日线行情 |
| `stock_financial` | (ts_code, end_date) | eps, bvps, roe, net_profit_margin, debt_to_assets | 财务指标 |
| `report` | id | report_date, title, subject_type, subject_code, content, is_active | 报告持久化 |
## 数据源接口速查
| 接口 | AkShareSource | TushareSource |
|------|--------------|---------------|
| 股票列表 | `ak.stock_info_a_code_name()` | `pro.stock_basic()` |
| 每日行情 | `ak.stock_zh_a_hist(symbol, period='daily')` | `pro.daily(ts_code,...)` |
| 指数行情 | `ak.index_zh_a_hist(symbol, period='daily')` | `pro.index_daily(ts_code,...)` |
| 财务指标 | `ak.stock_financial_abstract_ths(symbol)` | `pro.fina_indicator(ts_code,...)` |
| 复权因子 | — | `pro.adj_factor(ts_code,...)` |
## 常用快捷入口
```python
from factors.registry import get_factor, list_factors, list_categories
from database.dao import get_latest_trade_date, query_daily, save_daily
from database.connection import get_engine, test_connection
from reports.storage import save_report, query_reports
```
-1512
View File
File diff suppressed because it is too large Load Diff
+107 -681
View File
File diff suppressed because it is too large Load Diff