# ProStock 代理指南 A股量化投资框架 - Python 项目,用于量化股票投资分析。 ## 交流语言要求 **⚠️ 强制要求:所有沟通和思考过程必须使用中文。** 所有与 AI Agent 的交流必须使用中文 代码中的注释和文档字符串使用中文 禁止使用英文进行思考或沟通 ## 构建/检查/测试命令 **⚠️ 重要:本项目强制使用 uv 作为 Python 包管理器和运行工具。禁止直接使用 `python` 或 `pip` 命令。** ```bash # 安装依赖(必须使用 uv) uv pip install -e . # 运行所有测试 uv run pytest # 运行单个测试文件 uv run pytest tests/test_sync.py # 运行单个测试类 uv run pytest tests/test_sync.py::TestDataSync # 运行单个测试方法 uv run pytest tests/test_sync.py::TestDataSync::test_get_all_stock_codes_from_daily # 使用详细输出运行 uv run pytest -v # 运行覆盖率测试(如果安装了 pytest-cov) uv run pytest --cov=src --cov-report=term-missing ``` ### 禁止的命令 ❌ 以下命令在本项目中**严格禁止**: ```bash # 禁止直接使用 python python -c "..." # 禁止! python script.py # 禁止! python -m pytest # 禁止! python -m pip install # 禁止! # 禁止直接使用 pip pip install -e . # 禁止! pip install package # 禁止! pip list # 禁止! ``` ### 正确的 uv 用法 ✅ ```bash # 运行 Python 代码 uv run python -c "..." # ✅ 正确 uv run python script.py # ✅ 正确 # 安装依赖 uv pip install -e . # ✅ 正确 uv pip install package # ✅ 正确 # 运行测试 uv run pytest # ✅ 正确 uv run pytest tests/test_sync.py # ✅ 正确 ``` ## 项目结构 ``` ProStock/ ├── src/ # 源代码 │ ├── config/ # 配置管理 │ │ ├── __init__.py │ │ └── settings.py # pydantic-settings 配置 │ │ │ ├── data/ # 数据获取与存储 │ │ ├── api_wrappers/ # Tushare API 封装 │ │ │ ├── base_sync.py # 同步基础抽象类(BaseDataSync/StockBasedSync/DateBasedSync) │ │ │ ├── api_daily.py # 日线数据接口(DailySync) │ │ │ ├── api_pro_bar.py # Pro Bar 数据接口(ProBarSync) │ │ │ ├── api_stock_basic.py # 股票基础信息接口 │ │ │ ├── api_trade_cal.py # 交易日历接口 │ │ │ ├── api_bak_basic.py # 历史股票列表接口(BakBasicSync) │ │ │ ├── api_namechange.py # 股票名称变更接口 │ │ │ ├── financial_data/ # 财务数据接口 │ │ │ │ ├── api_income.py # 利润表接口 │ │ │ │ └── api_financial_sync.py # 财务数据同步 │ │ │ └── __init__.py │ │ ├── __init__.py │ │ ├── client.py # Tushare API 客户端(带速率限制) │ │ ├── config.py # 数据模块配置 │ │ ├── data_router.py # 数据路由器(factors/engine 专用) │ │ ├── db_inspector.py # 数据库信息查看工具 │ │ ├── db_manager.py # DuckDB 表管理和同步 │ │ ├── rate_limiter.py # 令牌桶速率限制器 │ │ ├── storage.py # 数据存储核心 │ │ ├── sync.py # 数据同步调度中心 │ │ └── utils.py # 数据模块工具函数 │ │ │ ├── factors/ # 因子计算框架(DSL 表达式驱动) │ │ ├── engine/ # 执行引擎子模块 │ │ │ ├── __init__.py # 导出引擎组件 │ │ │ ├── data_spec.py # 数据规格定义(DataSpec, ExecutionPlan) │ │ │ ├── data_router.py # 数据路由器 │ │ │ ├── planner.py # 执行计划生成器(ExecutionPlanner) │ │ │ ├── compute_engine.py # 计算引擎(ComputeEngine) │ │ │ └── factor_engine.py # 因子引擎统一入口(FactorEngine) │ │ ├── __init__.py # 导出所有公开 API │ │ ├── dsl.py # DSL 表达式层 - 节点定义和运算符重载 │ │ ├── api.py # API 层 - 常用符号(close/open等)和函数(ts_mean/cs_rank等) │ │ ├── compiler.py # AST 编译器 - 依赖提取 │ │ ├── translator.py # Polars 表达式翻译器 │ │ ├── parser.py # 字符串公式解析器(FormulaParser) │ │ ├── registry.py # 函数注册表(FunctionRegistry) │ │ └── exceptions.py # 异常定义(FormulaParseError等) │ │ │ ├── pipeline/ # 模型训练管道 │ │ ├── __init__.py │ │ ├── pipeline.py # 处理流水线(ProcessingPipeline) │ │ ├── registry.py # 插件注册中心(PluginRegistry) │ │ ├── core/ # 核心抽象 │ │ │ ├── __init__.py │ │ │ ├── base.py # 基类定义(BaseProcessor/BaseModel/BaseSplitter等) │ │ │ └── splitter.py # 时间序列划分策略 │ │ ├── models/ # 模型实现 │ │ │ ├── __init__.py │ │ │ └── models.py # LightGBM、CatBoost 等 │ │ └── processors/ # 数据处理器 │ │ ├── __init__.py │ │ └── processors.py # 标准化、缩尾、中性化等 │ │ │ └── training/ # 训练入口 │ ├── __init__.py │ ├── pipeline.py # 训练流程配置 │ └── output/ # 训练输出 │ └── top_stocks.tsv # 推荐股票结果 │ ├── tests/ # 测试文件 │ ├── test_sync.py │ ├── test_daily.py │ ├── test_factor_engine.py │ ├── test_factor_integration.py │ ├── test_pro_bar.py │ ├── test_601117_factors.py │ ├── test_two_stocks_string_factors.py │ ├── test_db_manager.py │ ├── test_daily_storage.py │ ├── test_tushare_api.py │ └── pipeline/ │ └── test_core.py ├── config/ # 配置文件 │ └── .env.local # 环境变量(不在 git 中) ├── data/ # 数据存储(DuckDB) ├── docs/ # 文档 ├── pyproject.toml # 项目配置 └── README.md ``` ## 代码风格指南 ### Python 版本 - **需要 Python 3.10+** - 使用现代 Python 特性(match/case、海象运算符、类型提示) ### 导入 ```python # 标准库优先 import os import time from datetime import datetime, timedelta from pathlib import Path from typing import Optional, Dict, Callable from concurrent.futures import ThreadPoolExecutor import threading # 第三方包 import pandas as pd import numpy as np from tqdm import tqdm from pydantic_settings import BaseSettings # 本地模块(使用来自 src 的绝对导入) from src.data.client import TushareClient from src.data.storage import Storage from src.config.settings import get_settings ``` ### 类型提示 - **始终使用类型提示** 用于函数参数和返回值 - 对可空类型使用 `Optional[X]` - 当可用时使用现代联合语法 `X | Y`(Python 3.10+) - 从 `typing` 导入类型:`Optional`、`Dict`、`Callable` 等 ```python def sync_single_stock( self, ts_code: str, start_date: str, end_date: str, ) -> pd.DataFrame: ... ``` ### 文档字符串 - 使用 **Google 风格文档字符串** - 包含 Args、Returns 部分 - 第一行保持简短摘要 ```python def get_next_date(date_str: str) -> str: """获取给定日期之后的下一天。 Args: date_str: YYYYMMDD 格式的日期 Returns: YYYYMMDD 格式的下一天日期 """ ... ``` ### 命名约定 - 变量、函数、方法使用 `snake_case` - 类使用 `PascalCase` - 常量使用 `UPPER_CASE` - 私有方法:`_leading_underscore` - 受保护属性:`_single_underscore` ### 错误处理 - 使用特定的异常,不要使用裸 `except:` - 使用上下文记录错误:`print(f"[ERROR] 上下文: {e}")` - 对 API 调用使用指数退避重试逻辑 - 在关键错误时立即停止(设置停止标志) ```python try: data = api.query(...) except Exception as e: print(f"[ERROR] 获取 {ts_code} 失败: {e}") raise # 记录后重新抛出 ``` ### 配置 - 对所有配置使用 **pydantic-settings** - 从 `config/.env.local` 文件加载 - 环境变量自动转换:`tushare_token` → `TUSHARE_TOKEN` - 对配置单例使用 `@lru_cache()` ### 数据存储 - 使用 **DuckDB** 嵌入式 OLAP 数据库进行持久化 - 存储在 `data/` 目录中(通过 `DATA_PATH` 环境变量配置) - 使用 UPSERT 模式(`INSERT OR REPLACE`)处理重复数据 - 多线程场景使用 `ThreadSafeStorage.queue_save()` + `flush()` 模式 ### 线程与并发 - 对 I/O 密集型任务(API 调用)使用 `ThreadPoolExecutor` - 实现停止标志以实现优雅关闭:`threading.Event()` - 数据同步默认工作线程数:10 - 出错时始终使用 `executor.shutdown(wait=False, cancel_futures=True)` ### 日志记录 - 使用带前缀的 print 语句:`[模块名] 消息` - 错误格式:`[ERROR] 上下文: 异常` - 进度:循环中使用 `tqdm` ### 测试 - 使用 **pytest** 框架 - 模拟外部依赖(Tushare API) - 使用 `@pytest.fixture` 进行测试设置 - 在导入位置打补丁:`patch('src.data.sync.Storage')` - 测试成功和错误两种情况 ### 日期格式 - 使用 `YYYYMMDD` 字符串格式表示日期 - 辅助函数:`get_today_date()`、`get_next_date()` - 完全同步的默认开始日期:`20180101` ### 依赖项 关键包: - `pandas>=2.0.0` - 数据处理 - `polars>=0.20.0` - 高性能数据处理(因子计算) - `numpy>=1.24.0` - 数值计算 - `tushare>=2.0.0` - A股数据 API - `pydantic>=2.0.0`、`pydantic-settings>=2.0.0` - 配置 - `tqdm>=4.65.0` - 进度条 - `lightgbm>=4.0.0` - 机器学习模型 - `catboost>=1.2.0` - 机器学习模型 - `pytest` - 测试(开发) ### 环境变量 创建 `config/.env.local`: ```bash TUSHARE_TOKEN=your_token_here DATA_PATH=data RATE_LIMIT=100 THREADS=10 ``` ## 常见任务 ```bash # 同步所有股票(增量) uv run python -c "from src.data.sync import sync_all; sync_all()" # 强制完全同步 uv run python -c "from src.data.sync import sync_all; sync_all(force_full=True)" # 自定义线程数 uv run python -c "from src.data.sync import sync_all; sync_all(max_workers=20)" # 运行因子计算测试 uv run pytest tests/test_factor_engine.py -v ``` ## 架构变更历史 ### v2.2 (2026-03-01) - 因子框架 DSL 化重构 #### 因子计算框架重构 **变更**: 从基类继承方式迁移到 DSL 表达式方式 **原因**: - 提供更直观的数学公式表达方式 - 支持因子表达式的组合和嵌套 - 更好的类型安全和编译期检查 **架构变化**: - 新增 `dsl.py`: 表达式节点基类和运算符重载(Symbol、FunctionNode等) - 新增 `api.py`: 常用符号(close/open/volume等)和函数(ts_mean/cs_rank等) - 新增 `compiler.py`: AST 编译器,提取表达式依赖 - 新增 `translator.py`: 将 DSL 表达式翻译为 Polars 表达式 - 新增 `parser.py`: 字符串公式解析器(FormulaParser),支持从字符串解析 DSL 表达式 - 新增 `registry.py`: 函数注册表(FunctionRegistry),管理字符串函数名到 Python 函数的映射 - 新增 `exceptions.py`: 公式解析异常定义(FormulaParseError、UnknownFunctionError等) - 重构 `engine/` 子模块: - `factor_engine.py`: 因子引擎统一入口(FactorEngine) - `data_spec.py`: 数据规格定义(DataSpec, ExecutionPlan) - `data_router.py`: 数据路由器 - `planner.py`: 执行计划生成器(ExecutionPlanner) - `compute_engine.py`: 计算引擎(ComputeEngine) - 移除: `base.py`、`composite.py`、`data_loader.py`、根目录的 `data_spec.py` - 移除: `factors/momentum/` 和 `factors/financial/` 子目录 **使用方式对比**: ```python # 旧方式(基类继承) class MA20Factor(TimeSeriesFactor): name = "ma20" data_specs = [DataSpec("daily", ["close"], 20)] def compute(self, data): return data.get_column("close").rolling_mean(20) # 新方式(DSL 表达式) from src.factors.api import close, ts_mean ma20 = ts_mean(close, 20) # 直接编写数学表达式 engine = FactorEngine() engine.register("ma20", ma20) result = engine.compute(["ma20"], "20240101", "20240131") # 字符串公式解析(Phase 1 新增) from src.factors import FormulaParser, FunctionRegistry parser = FormulaParser(FunctionRegistry()) expr = parser.parse("ts_mean(close, 20) / close") # 从字符串解析 ``` #### data 模块补充完善 **新增文件**: - `api_wrappers/base_sync.py`: 数据同步基础抽象类(BaseDataSync、StockBasedSync、DateBasedSync) - `data_router.py`: 数据路由器(已集成到 factors/engine.py 中的 DataRouter) - `utils.py`: 日期工具函数(get_today_date、get_next_date、is_quarter_end等) **影响**: 数据同步逻辑更加规范化,支持按股票和按日期两种同步模式 ### v2.1 (2026-02-28) - 同步模块规范更新 **变更**: 明确 `sync.py` 只包含每日更新的数据同步 **原因**: 区分高频(每日)和低频(季度/年度)数据,避免不必要的 API 调用 **规范**: - `sync.py` / `sync_all_data()`: **仅包含每日更新的数据** - 日线数据 (`api_daily`) - Pro Bar 数据 (`api_pro_bar`) - 交易日历 (`api_trade_cal`) - 股票基本信息 (`api_stock_basic`) - 历史股票列表 (`api_bak_basic`) - **不应放入 `sync.py` 的季度/低频数据**: - 财务数据 (`financial_data/` 目录): 利润表、资产负债表、现金流量表等 - 名称变更 (`api_namechange`): 已移除自动同步,建议手动定期同步 - **季度数据同步方式**: ```python # 财务数据单独同步(不在 sync_all_data 中) from src.data.api_wrappers.financial_data.api_financial_sync import sync_financial sync_financial() # 增量同步利润表 # 名称变更手动同步 from src.data.api_wrappers import sync_namechange sync_namechange(force=True) ``` ### v2.0 (2026-02-23) - 重要更新 #### 存储层重构 **变更**: 从 HDF5 迁移到 DuckDB **原因**: DuckDB 提供更好的查询性能、SQL 下推能力、并发支持 **影响**: 所有数据表现在使用 DuckDB 存储,旧 HDF5 文件可手动迁移 #### Sync 类迁移 **变更**: `DataSync` 类从 `sync.py` 迁移到 `api_daily.py` **原因**: 实现代码职责分离,每个 API 文件包含自己的同步逻辑 **影响**: - `sync.py` 保留为调度中心 - `api_daily.py` 包含 `DailySync` 类和 `sync_daily` 函数 #### 新增模块 **pipeline 模块**: 机器学习流水线组件(处理器、模型、划分策略) **training 模块**: 训练入口程序 **factors/momentum**: 动量因子(MA、收益率排名) **factors/financial**: 财务因子框架 **data/utils.py**: 日期工具函数集中管理 #### 新增 API 接口 `api_namechange.py`: 股票曾用名接口(手动同步) `api_bak_basic.py`: 历史股票列表接口 #### 工具函数统一 `get_today_date()`、`get_next_date()`、`DEFAULT_START_DATE` 等函数统一在 `src/data/utils.py` 中管理 其他模块应从 `utils.py` 导入这些函数,避免重复定义 其他模块应从 `utils.py` 导入这些函数,避免重复定义 ## Factors 框架设计说明 ### 架构层次 因子框架采用分层设计,从上到下依次是: ``` API 层 (api.py) | v DSL 层 (dsl.py) <- 因子表达式 (Node) | v Compiler (compiler.py) <- AST 依赖提取 | v Parser (parser.py) <- 字符串公式解析器 | v Registry (registry.py) <- 函数注册表 | v Translator (translator.py) <- 翻译为 Polars 表达式 | v Engine (engine/) <- 执行引擎 | - FactorEngine: 统一入口 | - DataRouter: 数据路由 | - ExecutionPlanner: 执行计划 | - ComputeEngine: 计算引擎 | v 数据层 (data_router.py + DuckDB) <- 数据获取和存储 ``` ### 使用方式 #### 1. 基础表达式(DSL 方式) ```python from src.factors.api import close, open, ts_mean, cs_rank # 定义因子表达式(惰性计算) ma20 = ts_mean(close, 20) # 20日移动平均 price_rank = cs_rank(close) # 收盘价截面排名 # 组合运算 alpha = ma20 * 0.6 + price_rank * 0.4 ``` #### 2. 字符串公式解析(Phase 1 新增) ```python from src.factors import FormulaParser, FunctionRegistry # 创建解析器(自动加载 api.py 中的所有函数) parser = FormulaParser(FunctionRegistry()) # 从字符串解析公式 expr = parser.parse("ts_mean(close, 20) / close") complex_expr = parser.parse("cs_rank(ts_mean(close, 5) - ts_mean(close, 20))") # 支持完整运算符和函数调用 expr2 = parser.parse("(close - open) / open * 100") # 涨跌幅 expr3 = parser.parse("ts_corr(close, volume, 20)") # 量价相关性 ``` #### 3. 注册和执行 ```python from src.factors import FactorEngine engine = FactorEngine() # 注册 DSL 表达式 engine.register("ma20", ma20) engine.register("price_rank", price_rank) # 或注册字符串解析的表达式 engine.register("alpha", parser.parse("ma20 * 0.6 + price_rank * 0.4")) # 执行计算 result = engine.compute( factor_names=["ma20", "price_rank"], start_date="20240101", end_date="20240131", ) ``` ### 支持的函数 **时间序列函数 (ts_*)**: - `ts_mean(x, window)` - 滚动均值 - `ts_std(x, window)` - 滚动标准差 - `ts_max(x, window)` - 滚动最大值 - `ts_min(x, window)` - 滚动最小值 - `ts_sum(x, window)` - 滚动求和 - `ts_delay(x, periods)` - 滞后 N 期 - `ts_delta(x, periods)` - 差分 N 期 - `ts_corr(x, y, window)` - 滚动相关系数 - `ts_cov(x, y, window)` - 滚动协方差 - `ts_rank(x, window)` - 滚动排名 **截面函数 (cs_*)**: - `cs_rank(x)` - 截面排名(分位数) - `cs_zscore(x)` - Z-Score 标准化 - `cs_neutralize(x, group)` - 行业/市值中性化 - `cs_winsorize(x, lower, upper)` - 缩尾处理 - `cs_demean(x)` - 去均值 **数学函数**: - `log(x)` - 自然对数 - `exp(x)` - 指数函数 - `sqrt(x)` - 平方根 - `sign(x)` - 符号函数 - `abs(x)` - 绝对值 - `max_(x, y)` / `min_(x, y)` - 逐元素最值 - `clip(x, lower, upper)` - 数值裁剪 **条件函数**: - `if_(condition, true_val, false_val)` - 条件选择 - `where(condition, true_val, false_val)` - if_ 的别名 ### 运算符支持 DSL 表达式支持完整的 Python 运算符: ```python # 算术运算: +, -, *, /, //, %, ** expr1 = (close - open) / open * 100 # 涨跌幅 # 比较运算: ==, !=, <, <=, >, >= expr2 = close > open # 是否上涨 # 一元运算: -, +, abs() expr3 = -change # 涨跌额取反 # 链式调用 expr4 = ts_mean(cs_rank(close), 20) # 排名后的20日平滑 ``` ### 异常处理 框架提供清晰的异常类型帮助定位问题: - `FormulaParseError` - 公式解析错误基类 - `UnknownFunctionError` - 未知函数错误(提供模糊匹配建议) - `InvalidSyntaxError` - 语法错误 - `EmptyExpressionError` - 空表达式错误 - `DuplicateFunctionError` - 函数重复注册错误 示例: ```python from src.factors import FormulaParser, FunctionRegistry, UnknownFunctionError parser = FormulaParser(FunctionRegistry()) try: expr = parser.parse("unknown_func(close)") except UnknownFunctionError as e: print(e) # 显示错误位置和可用函数建议 ``` ## AI 行为准则 ## AI 行为准则 ### LSP 检测报错处理 **⚠️ 强制要求:当进行 LSP 检测时报错,必定是代码格式问题。** 如果 LSP 检测报错,必须按照以下流程处理: 1. **问题定位** - 报错必定是由基础格式错误引起:缩进错误、引号括号不匹配、代码格式错误等 - 必须读取对应的代码行,精确定位错误 2. **修复方式** - ✅ **必须**:读取报错文件,检查具体代码行 - ✅ **必须**:修复格式错误(缩进、括号匹配、引号闭合等) - ❌ **禁止**:删除文件重新修改 - ❌ **禁止**:自行 rollback 文件 - ❌ **禁止**:新建文件重新修改 - ❌ **禁止**:忽略错误继续执行 3. **验证要求** - 修复后必须重新运行 LSP 检测确认无错误 - 确保修改仅针对格式问题,不改变代码逻辑 **示例场景**: ``` LSP 报错:Syntax error on line 45 ✅ 正确做法:读取文件第 45 行,发现少了一个右括号,添加后重新检测 ❌ 错误做法:删除文件重新写、或者忽略错误继续 ``` ### Emoji 表情禁用规则 **⚠️ 强制要求:代码和测试文件中禁止出现 emoji 表情。** 1. **禁止范围** - 所有 `.py` 源代码文件 - 所有测试文件 (`tests/` 目录) - 配置文件、脚本文件 2. **替代方案** - ❌ 禁止使用:`print("✅ 成功")`、`print("❌ 失败")`、`# 📝 注释` - ✅ 应使用:`print("[成功]")`、`print("[失败]")`、`# 注释` - 使用方括号 `[成功]`、`[警告]`、`[错误]` 等文字标记代替 emoji 3. **唯一例外** - AGENTS.md 文件本身可以使用 emoji 进行文档强调(如本文件中的 ⚠️) - 项目文档、README 等对外展示文件可以酌情使用 4. **检查方法** - 使用正则表达式搜索 emoji:`[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U0001F1E0-\U0001F1FF\u2600-\u26FF\u2700-\u27BF]` - 提交前自查,确保无 emoji 混入代码