1
0
Files
2026-01-27 18:30:41 +08:00

10 KiB
Raw Permalink Blame History

Spec: Data Fetching

ADDED Requirements

Requirement: 数据库连接配置

系统 SHALL 通过硬编码常量管理数据库连接参数(开发环境)。

Scenario: 使用硬编码常量

  • WHEN 系统在 backtest.py 中定义数据库配置
  • THEN 系统定义 DB_HOST, DB_NAME, DB_USER, DB_PASSWORD 常量
  • THEN DB_HOST 值 SHALL 为数据库主机地址(如 '81.71.3.24'
  • THEN DB_NAME 值 SHALL 为数据库名称(如 'leopard_dev'
  • THEN DB_USER 值 SHALL 为数据库用户名
  • THEN DB_PASSWORD 值 SHALL 为数据库密码

Scenario: 构建连接字符串

  • WHEN 系统创建 SQLAlchemy 连接
  • THEN 系统使用硬编码的常量构建连接字符串
  • THEN 连接字符串格式 SHALL 为 postgresql://{user}:{password}@{host}/{database}
  • THEN 不从环境变量读取任何凭证

Scenario: 修改数据库凭证

  • WHEN 开发人员需要更换数据库或凭证
  • THEN 开发人员直接修改 backtest.py 中的常量值
  • THEN 修改后脚本使用新凭证连接数据库

Requirement: 数据库连接建立

系统 SHALL 使用 SQLAlchemy 创建 PostgreSQL 数据库连接。

Scenario: 成功建立连接

  • WHEN 凭证正确且数据库可访问
  • THEN 系统使用 sqlalchemy.create_engine(conn_str) 创建引擎
  • THEN 连接字符串格式 SHALL 为 postgresql://{user}:{password}@{host}/{database}
  • THEN 系统成功创建引擎对象
  • THEN 系统可用于执行查询

Scenario: 连接字符串构建

  • WHEN 系统构建 PostgreSQL 连接字符串
  • THEN 连接字符串 SHALL 正确编码特殊字符(密码中的 @, : 等)
  • THEN 连接字符串 SHALL 使用标准 URI 格式
  • THEN 连接字符串 SHALL 不包含额外选项(仅基础连接参数)

Scenario: 数据库连接失败

  • WHEN 凭证错误或数据库不可达
  • THEN SQLAlchemy 抛出 sqlalchemy.exc.OperationalError
  • THEN 主流程捕获异常
  • THEN 系统输出错误信息:"数据库连接失败: {error}"
  • THEN 系统退出并返回状态码 2

Scenario: 连接池管理

  • WHEN 系统创建引擎对象
  • THEN SQLAlchemy SHALL 自动管理连接池
  • THEN 查询后连接 SHALL 自动返回池中
  • THEN 系统 SHALL 在查询完成后调用 engine.dispose() 清理

Requirement: SQL 查询构建

系统 SHALL 构建参数化的 SQL 查询以获取股票历史数据。

Scenario: 基础查询结构

  • WHEN 系统构建查询
  • THEN 查询 SHALL 选择 trade_date, Open, High, Low, Close, Volume, factor
  • THEN 查询 SHALL 连接 leopard_daily 和 leopard_stock 表
  • THEN 查询 SHALL 按 stock.code 过滤
  • THEN 查询 SHALL 按 trade_date 范围过滤
  • THEN 查询 SHALL 按 trade_date 升序排序

Scenario: 复权价格计算

  • WHEN 系统计算复权价格
  • THEN Open SHALL 计算为 open * factor
  • THEN Close SHALL 计算为 close * factor
  • THEN High SHALL 计算为 high * factor
  • THEN Low SHALL 计算为 low * factor
  • THEN Volume SHALL 直接使用原始值(不复权)
  • THEN factor SHALL 使用 COALESCE(factor, 1.0) 处理 NULL 值

Scenario: 参数化股票代码

  • WHEN 用户指定股票代码(如 '000001.SZ'
  • THEN 查询 WHERE 子句 SHALL 使用 stock.code = '{code}'
  • THEN 代码 SHALL 精确匹配(不使用 LIKE
  • THEN 查询 SHALL 返回匹配股票的所有日线数据

Scenario: 参数化日期范围

  • WHEN 用户指定开始日期 '2024-01-01' 和结束日期 '2025-12-31'
  • THEN 查询 WHERE 子句 SHALL 使用 BETWEEN '{start_date} 00:00:00' AND '{end_date} 23:59:59'
  • THEN 00:00:00 和 23:59:59 SHALL 覆盖全天
  • THEN 日期格式 SHALL 为 YYYY-MM-DD HH:MM:SS

Scenario: 完整 SQL 查询

  • WHEN 系统执行数据加载
  • THEN 查询 SHALL 为:
    SELECT
        trade_date,
        open * factor AS Open,
        close * factor AS Close,
        high * factor AS High,
        low * factor AS Low,
        volume AS Volume,
        COALESCE(factor, 1.0) AS factor
    FROM leopard_daily daily
    LEFT JOIN leopard_stock stock ON stock.id = daily.stock_id
    WHERE stock.code = '{code}'
      AND daily.trade_date BETWEEN '{start_date} 00:00:00'
                              AND '{end_date} 23:59:59'
    ORDER BY daily.trade_date
    

Requirement: 数据查询执行

系统 SHALL 使用 pandas 的 read_sql 函数执行 SQL 查询并返回 DataFrame。

Scenario: 成功执行查询

  • WHEN SQL 查询有效且数据存在
  • THEN 系统调用 pd.read_sql(query, engine)
  • THEN 系统返回 DataFrame 对象
  • THEN DataFrame SHALL 包含查询结果的所有列
  • THEN DataFrame 行数 SHALL 匹配数据库返回的记录数

Scenario: 数据类型处理

  • WHEN pandas 读取 SQL 结果
  • THEN trade_date SHALL 自动转换为 datetime 类型
  • THEN Open, High, Low, Close, Volume SHALL 为 float 类型
  • THEN factor SHALL 为 float 类型
  • THEN 系统不需要手动类型转换(除日期索引设置)

Scenario: 查询返回空结果

  • WHEN 指定股票代码或日期范围无数据
  • THEN read_sql 返回空 DataFrame0 行)
  • THEN 系统检查 len(df) == 0
  • THEN 系统抛出 ValueError: "未找到股票 {code} 在指定时间范围内的数据"

Scenario: SQL 语法错误

  • WHEN SQL 查询包含语法错误
  • THEN SQLAlchemy 抛出 sqlalchemy.exc.ProgrammingError
  • THEN 主流程捕获异常
  • THEN 系统输出错误信息:"SQL 查询错误: {error}"
  • THEN 系统退出并返回状态码 2

Requirement: 数据格式转换

系统 SHALL 将查询结果转换为 backtesting 库要求的格式。

Scenario: 设置日期索引

  • WHEN DataFrame 加载完成
  • THEN 系统调用 df.set_index('trade_date', inplace=True)
  • THEN DataFrame 的索引 SHALL 为 DatetimeIndex
  • THEN 索引 SHALL 不再是数值索引
  • THEN backtesting 库 SHALL 能正确处理日期范围

Scenario: 列名格式化

  • WHEN DataFrame 加载完成
  • THEN 列名 SHALL 为 ['Open', 'High', 'Low', 'Close', 'Volume', 'factor']
  • THEN 列名 SHALL 遵循 backtesting 库要求(首字母大写)
  • THEN 列名 SHALL 与 SQL 查询中的别名一致

Scenario: 数据验证

  • WHEN 系统准备返回 DataFrame
  • THEN 系统验证 DataFrame 包含必需列
  • THEN 系统验证 'Open', 'High', 'Low', 'Close', 'Volume' 列存在
  • THEN 系统验证索引为 DatetimeIndex
  • WHEN 验证失败
  • THEN 系统抛出 ValueError: "数据格式不符合要求"

Requirement: 数据清理

系统 SHALL 清理数据以确保回测质量。

Scenario: 删除 NULL 值行

  • WHEN DataFrame 包含 NULL 或 NaN 值
  • THEN 系统调用 df.dropna() 删除
  • THEN 任何包含 NaN 的行 SHALL 被删除
  • THEN 返回的 DataFrame SHALL 不包含 NULL 值

Scenario: 数据完整性检查

  • WHEN DataFrame 加载完成
  • THEN 系统检查 trade_date 连续性
  • THEN 系统检查无重复日期
  • WHEN 发现异常
  • THEN 系统输出警告:"数据存在异常: {detail}"

Scenario: 最小数据量验证

  • WHEN DataFrame 行数少于 10
  • THEN 系统输出错误:"数据不足,至少需要 10 天数据"
  • THEN 系统抛出 ValueError
  • THEN 主流程捕获并退出

Requirement: 资源管理

系统 SHALL 正确管理数据库连接和内存资源。

Scenario: 引擎创建和清理

  • WHEN 系统开始数据加载
  • THEN 系统创建 SQLAlchemy 引擎对象
  • THEN 系统使用引擎执行查询
  • WHEN 查询完成
  • THEN 系统调用 engine.dispose() 关闭连接池
  • THEN 系统释放所有数据库连接

Scenario: 异常情况下的资源清理

  • WHEN 查询过程中抛出异常
  • THEN 系统在 finally 块中调用 engine.dispose()
  • THEN 所有连接 SHALL 被正确关闭
  • THEN 系统不会泄漏数据库连接

Requirement: 错误处理和日志

系统 SHALL 提供清晰的错误信息和调试支持。

Scenario: 连接错误信息

  • WHEN 数据库连接失败
  • THEN 错误信息 SHALL 包含数据库主机和端口
  • THEN 错误信息 SHALL 区分网络错误和认证错误
  • THEN 系统提示用户检查凭证和网络连接

Scenario: 查询错误信息

  • WHEN SQL 查询失败
  • THEN 错误信息 SHALL 包含失败的 SQL 语句
  • THEN 错误信息 SHALL 包含数据库返回的错误详情
  • THEN 系统提示用户检查表结构和数据

Scenario: 数据格式错误信息

  • WHEN 返回的 DataFrame 不符合要求
  • THEN 错误信息 SHALL 列出缺失的列
  • THEN 错误信息 SHALL 提示期望的格式
  • THEN 系统建议用户检查数据库表结构

Requirement: 函数接口

load_data_from_db 函数 SHALL 提供清晰的调用接口。

Scenario: 函数签名

  • WHEN 主流程调用 load_data_from_db(code, start_date, end_date)
  • THEN 函数接收三个字符串参数
  • THEN code 为股票代码(如 '000001.SZ'
  • THEN start_date 为开始日期(如 '2024-01-01'
  • THEN end_date 为结束日期(如 '2025-12-31'

Scenario: 返回值

  • WHEN 数据加载成功
  • THEN 函数返回 pandas.DataFrame
  • THEN DataFrame 索引为 DatetimeIndextrade_date
  • THEN DataFrame 包含 ['Open', 'High', 'Low', 'Close', 'Volume', 'factor'] 列

Scenario: 异常抛出

  • WHEN 数据加载失败
  • THEN 函数 SHALL 抛出异常(不捕获)
  • THEN 异常类型 SHALL 为 ValueError业务逻辑错误
  • THEN 主流程负责捕获和处理异常

Requirement: 性能考虑

系统 SHALL 优化数据加载性能以支持大数据集。

Scenario: 使用 pandas 向量化操作

  • WHEN 执行复权计算
  • THEN 计算 SHALL 使用 pandas 向量化操作
  • THEN 不使用循环逐行计算
  • THEN 10 年数据(约 2500 行) SHALL 在 1 秒内加载

Scenario: 索引优化

  • WHEN 设置 DataFrame 索引
  • THEN set_index() 操作 SHALL 高效(使用底层数组拷贝)
  • THEN 日期索引 SHALL 支持快速范围查询

Scenario: 内存管理

  • WHEN 加载大数据集
  • THEN 系统 SHALL 及时调用 engine.dispose() 释放连接
  • THEN DataFrame SHALL 使用 pandas 内部优化存储
  • THEN 内存占用 SHALL 合理10 年数据约几 MB