Files
ProStock/AGENTS.md
liaozhaorun b461a4940d refactor(factor): 完成因子框架 DSL 化重构
- 重构 FactorEngine 实现完整的 DSL 表达式执行链路
- 新增 DataRouter 数据路由器,支持内存模式和核心宽表组装
- 新增 ExecutionPlanner 执行计划生成器,整合编译器和翻译器
- 新增 ComputeEngine 计算引擎,支持并行运算
- 完善 factors/__init__.py 公开 API 导出
- 新增 test_factor_engine.py 引擎单元测试
- 移除旧引擎实现和废弃的 DSL promotion 测试
- 更新 AGENTS.md 添加 v2.2 架构变更历史和 Factors 框架设计说明
2026-03-01 15:03:56 +08:00

18 KiB
Raw Blame History

ProStock 代理指南

A股量化投资框架 - Python 项目,用于量化股票投资分析。

交流语言要求

⚠️ 强制要求:所有沟通和思考过程必须使用中文。

所有与 AI Agent 的交流必须使用中文 代码中的注释和文档字符串使用中文 禁止使用英文进行思考或沟通

构建/检查/测试命令

⚠️ 重要:本项目强制使用 uv 作为 Python 包管理器和运行工具。禁止直接使用 pythonpip 命令。

# 安装依赖(必须使用 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

禁止的命令

以下命令在本项目中严格禁止

# 禁止直接使用 python
python -c "..."           # 禁止!
python script.py          # 禁止!
python -m pytest          # 禁止!
python -m pip install     # 禁止!

# 禁止直接使用 pip
pip install -e .          # 禁止!
pip install package       # 禁止!
pip list                  # 禁止!

正确的 uv 用法

# 运行 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       # 数据模块配置
│   │   ├── db_inspector.py # 数据库信息查看工具
│   │   ├── db_manager.py   # DuckDB 表管理和同步
│   │   ├── rate_limiter.py # 令牌桶速率限制器
│   │   ├── storage.py      # 数据存储核心
│   │   ├── sync.py         # 数据同步调度中心
│   │   └── utils.py        # 数据模块工具函数
│   │
│   ├── factors/           # 因子计算框架DSL 表达式驱动)
│   │   ├── __init__.py     # 导出所有公开 API
│   │   ├── dsl.py          # DSL 表达式层 - 节点定义和运算符重载
│   │   ├── api.py          # API 层 - 常用符号(close/open等)和函数(ts_mean/cs_rank等)
│   │   ├── compiler.py     # AST 编译器 - 依赖提取
│   │   ├── translator.py   # Polars 表达式翻译器
│   │   └── engine.py       # 因子执行引擎 - 统一入口
│   │
│   ├── pipeline/          # 模型训练管道
│   │   ├── __init__.py
│   │   ├── pipeline.py     # 处理流水线
│   │   ├── registry.py     # 插件注册中心
│   │   ├── core/           # 核心抽象
│   │   │   ├── __init__.py
│   │   │   ├── base.py     # 基类定义
│   │   │   └── splitter.py # 时间序列划分策略
│   │   ├── models/         # 模型实现
│   │   │   ├── __init__.py
│   │   │   └── models.py   # LightGBM、CatBoost 等
│   │   └── processors/     # 数据处理器
│   │       ├── __init__.py
│   │       └── processors.py # 标准化、缩尾、中性化等
│   │
│   └── training/          # 训练入口
│       ├── __init__.py
│       ├── main.py         # 训练主程序
│       ├── pipeline.py     # 训练流程配置
│       └── output/         # 训练输出
│           └── top_stocks.tsv  # 推荐股票结果
│
├── tests/                 # 测试文件
│   ├── test_sync.py
│   └── test_daily.py
├── config/                # 配置文件
│   └── .env.local         # 环境变量(不在 git 中)
├── data/                  # 数据存储DuckDB
├── docs/                  # 文档
├── pyproject.toml         # 项目配置
└── README.md

代码风格指南

Python 版本

  • 需要 Python 3.10+
  • 使用现代 Python 特性match/case、海象运算符、类型提示

导入

# 标准库优先
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 | YPython 3.10+
  • typing 导入类型:OptionalDictCallable
def sync_single_stock(
    self,
    ts_code: str,
    start_date: str,
    end_date: str,
) -> pd.DataFrame:
    ...

文档字符串

  • 使用 Google 风格文档字符串
  • 包含 Args、Returns 部分
  • 第一行保持简短摘要
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 调用使用指数退避重试逻辑
  • 在关键错误时立即停止(设置停止标志)
try:
    data = api.query(...)
except Exception as e:
    print(f"[ERROR] 获取 {ts_code} 失败: {e}")
    raise  # 记录后重新抛出

配置

  • 对所有配置使用 pydantic-settings
  • config/.env.local 文件加载
  • 环境变量自动转换:tushare_tokenTUSHARE_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 - 数据处理
  • numpy>=1.24.0 - 数值计算
  • tushare>=2.0.0 - A股数据 API
  • pydantic>=2.0.0pydantic-settings>=2.0.0 - 配置
  • tqdm>=4.65.0 - 进度条
  • pytest - 测试(开发)

环境变量

创建 config/.env.local

TUSHARE_TOKEN=your_token_here
DATA_PATH=data
RATE_LIMIT=100
THREADS=10

常见任务

# 同步所有股票(增量)
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)"

架构变更历史

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 表达式
  • 重构 engine.py: 统一执行引擎入口,整合 DataRouter、ExecutionPlanner、ComputeEngine
  • 移除: base.pycomposite.pydata_loader.pydata_spec.py
  • 移除: factors/momentum/factors/financial/ 子目录 使用方式对比:
# 旧方式(基类继承)
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")

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): 已移除自动同步,建议手动定期同步
  • 季度数据同步方式:

    # 财务数据单独同步(不在 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
Translator (translator.py) <- 翻译为 Polars 表达式
    |
    v
Engine (engine.py)      <- 执行引擎 (DataRouter/ExecutionPlanner/ComputeEngine)
    |
    v
数据层 (data_router.py + DuckDB) <- 数据获取和存储

使用方式

1. 基础表达式

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. 注册和执行

from src.factors import FactorEngine

engine = FactorEngine()
engine.register("ma20", ma20)
engine.register("price_rank", price_rank)

# 执行计算
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 运算符:

# 算术运算: +, -, *, /, //, %, **
expr1 = (close - open) / open * 100  # 涨跌幅

# 比较运算: ==, !=, <, <=, >, >=
expr2 = close > open  # 是否上涨

# 一元运算: -, +, abs()
expr3 = -change  # 涨跌额取反

# 链式调用
expr4 = ts_mean(cs_rank(close), 20)  # 排名后的20日平滑

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 混入代码