Files
sanguo_quant_live/pangtong-value/research/task-20260430-data-platform-integration/design.md
T
2026-04-30 20:51:54 +08:00

10 KiB
Raw Blame History

📐 架构设计:整合数据平台与回测环境

任务ID: integrate-data-platform-20260430
撰写人: 庞统
日期: 2026-04-30


一、现状分析

数据资产清单

数据类型 格式 存储位置 文件组织 数量
日线行情 Parquet raw/daily/{year}/{code}_daily.parquet 按年分目录 2013-2025
财务估值 Parquet raw/financial/valuation/{code}_valuation.parquet 按类型分目录 ~5000股
股票信息 CSV raw/stock_info/stock_basic_info_*.csv 单文件 5493股
HS300成分 CSV raw/stock_info/hs300_constituents_latest.csv 单文件 300只
日线(旧) Parquet raw/a_stock_daily/{year}/ 按年分目录,无文件 可能为空

已有组件

组件 语言 接口 依赖
VnpyLocalDataAdapter Python 类方法 akshare
backtest-service Python REST API (FastAPI) vnpy
张飞回测框架 Python 函数调用 numpy, pandas

关键发现

  1. 数据格式已经是 Parquet(不是 CSV),字段:date, open, high, low, close, volume, amount, outstanding_share, turnover, year
  2. 姜维适配器路径硬编码 /Users/chufeng/nas/stock/sanguo_vnpy/zhaoyun-data/data,与实际数据路径不同
  3. a_stock_daily/ 和 daily/ 是两套目录,需确认哪套是主数据

二、架构设计

核心原则:最简方案

不新建独立包/服务。直接在现有项目内加一个 data_platform.py 配置文件 + 一个 catalog.py 数据接口。

架构图

┌─────────────────────────────────────────────────┐
│                 策略开发者                         │
│   (张飞回测 / vnpy回测 / 未来策略)                  │
└───────────────────┬─────────────────────────────┘
                    │ 调用
                    ▼
┌─────────────────────────────────────────────────┐
│            DataCatalog (catalog.py)              │
│                                                  │
│  get_daily(symbol, start, end) → DataFrame       │
│  get_stock_list() → DataFrame                    │
│  get_financial(symbol) → DataFrame               │
│  get_index_constituents(index) → DataFrame       │
└───────────────────┬─────────────────────────────┘
                    │ 读取
                    ▼
┌─────────────────────────────────────────────────┐
│         data_platform_config.yaml                │
│         DATA_ROOT: /path/to/zhaoyun-data/data    │
└───────────────────┬─────────────────────────────┘
                    │ 指向
                    ▼
┌─────────────────────────────────────────────────┐
│        赵云数据目录 (zhaoyun-data/data/)           │
│                                                  │
│  raw/daily/{year}/{code}_daily.parquet           │
│  raw/financial/valuation/{code}_valuation.parquet│
│  raw/stock_info/stock_basic_info_*.csv           │
│  raw/stock_info/hs300_constituents_latest.csv    │
└─────────────────────────────────────────────────┘

模块划分

sanguo_quant_live/
├── data_platform/                    # 🆕 数据平台模块(新建)
│   ├── __init__.py
│   ├── config.py                     # 配置加载(读 yaml + 环境变量)
│   ├── catalog.py                    # DataCatalog 核心接口
│   └── data_platform_config.yaml     # 配置文件(DATA_ROOT 等)
├── zhaoyun-data/                     # 赵云数据(已有)
│   └── data/raw/...
├── zhangfei-technical/               # 张飞回测(已有)
└── jiangwei-platform/                # 姜维基建(已有)

sanguo_vnpy/
└── src/
    └── adapters/
        └── vnpy_local_data_adapter.py  # 姜维适配器(改路径配置)

三、接口定义

data_platform_config.yaml

# 数据平台配置
data_root: "${ZHAOYUN_DATA_ROOT}"   # 优先环境变量
# 默认值(环境变量未设置时)
default_data_root: "/Users/chufeng/.openclaw/sanguo_projects/sanguo_quant_live/zhaoyun-data/data"

# 数据源配置
sources:
  daily:
    format: parquet
    path_pattern: "raw/daily/{year}/{code}_daily.parquet"
    date_range: [2013, 2025]
    columns: [date, open, high, low, close, volume, amount, outstanding_share, turnover]
  
  stock_info:
    format: csv
    path: "raw/stock_info"
    file_pattern: "stock_basic_info_*.csv"
  
  financial:
    format: parquet
    path_pattern: "raw/financial/valuation/{code}_valuation.parquet"
  
  index_constituents:
    format: csv
    path: "raw/stock_info/hs300_constituents_latest.csv"

# fallback(本地无数据时)
fallback:
  enabled: true
  provider: akshare

config.py

"""数据平台配置加载"""
import os
import yaml
from pathlib import Path

def load_config() -> dict:
    """加载配置,环境变量优先"""
    config_path = Path(__file__).parent / "data_platform_config.yaml"
    with open(config_path) as f:
        config = yaml.safe_load(f)
    
    # 环境变量覆盖 data_root
    env_root = os.environ.get("ZHAOYUN_DATA_ROOT")
    if env_root:
        config["data_root"] = env_root
    elif "${" in config.get("data_root", ""):
        config["data_root"] = config["default_data_root"]
    
    return config

catalog.py 核心接口

"""DataCatalog - 统一数据访问接口"""
import pandas as pd
from pathlib import Path
from typing import Optional
from .config import load_config

class DataCatalog:
    def __init__(self, config: dict = None):
        self._config = config or load_config()
        self._data_root = Path(self._config["data_root"])
    
    def get_daily(self, symbol: str, start: str = None, end: str = None) -> pd.DataFrame:
        """获取日线行情
        Args:
            symbol: 股票代码,如 "000001" 或 "sh600519"
            start: 开始日期 "YYYY-MM-DD"
            end: 结束日期 "YYYY-MM-DD"
        """
        ...
    
    def get_stock_list(self) -> pd.DataFrame:
        """获取全部A股列表"""
        ...
    
    def get_financial(self, symbol: str) -> pd.DataFrame:
        """获取财务估值数据"""
        ...
    
    def get_index_constituents(self, index: str = "000300") -> pd.DataFrame:
        """获取指数成分股"""
        ...

stock code 标准化规则

输入格式:
  "000001" / "sz000001" / "000001.SZ" → 标准化为 "sz000001"parquet文件名)
  "600519" / "sh600519" / "600519.SH" → 标准化为 "sh600519"

规则:
  0/3 开头 → sz 前缀
  6 开头 → sh 前缀
  已有 sh/sz 前缀 → 保持

四、数据流设计

日线数据流

赵云采集脚本 → raw/daily/{year}/{code}_daily.parquet
                        ↓
              DataCatalog.get_daily("600519", "2024-01-01", "2024-12-31")
                        ↓
              1. 标准化 code → sh600519
              2. 扫描 2024/ 目录
              3. pd.read_parquet("raw/daily/2024/sh600519_daily.parquet")
              4. 按 start/end 过滤日期
              5. 返回 DataFrame

张飞回测对接

# 改前(张飞直接读CSV,硬编码路径)
df = pd.read_csv("/path/to/data.csv")

# 改后(通过 DataCatalog
from data_platform import DataCatalog
catalog = DataCatalog()
df = catalog.get_daily("000001", "2024-01-01", "2025-12-31")

vnpy适配器对接

# 改前(姜维硬编码路径)
ZHAOYUN_DATA_BASE = "/Users/chufeng/nas/stock/sanguo_vnpy/zhaoyun-data/data"

# 改后(读取 DataCatalog 配置)
from data_platform.config import load_config
config = load_config()
ZHAOYUN_DATA_BASE = config["data_root"]

五、权衡取舍

决策 选择 理由
新建独立包 vs 项目内模块 项目内模块 代码量小(~200行),不值得独立包
YAML配置 vs JSON YAML 支持注释,可读性好
Parquet vs CSV Parquet(保持现状) 已有数据都是Parquet,不改格式
环境变量 vs 配置文件 环境变量优先+配置文件兜底 生产环境用环境变量,开发用配置文件
全量加载 vs 按需加载 按需加载(按symbol+日期范围) 避免内存浪费
fallback akshare 保留但默认关闭 策略开发者应先确认本地数据完备

六、待澄清项

# 问题 影响范围
Q1 raw/a_stock_daily/raw/daily/ 是否有重叠?哪个是主数据? catalog.py 的路径配置
Q2 财务数据除了 valuation 还有其他类型吗(income/balance/cashflow)? get_financial 接口设计
Q3 分钟线数据是否纳入本次 DataCatalog 是否实现 get_minute()
Q4 NAS 路径 /Users/chufeng/nas/stock/ 当前是否可用? vnpy适配器是否能正常工作

七、各角色分工

角色 任务 交付物
赵云 Q1-Q3 澄清 + 数据目录确认 + 更新SOP 数据目录说明 + 更新脚本
张飞 回测框架对接 DataCatalog 修改后的回测代码
关羽 风控数据接口验证 风控相关数据接口测试
姜维 config.py + catalog.py 实现 + vnpy适配器更新 + 回测环境文档 代码 + 文档
司马懿 架构评审 评审意见
庞统 整合交付 最终报告

八、不做的事

  1. 不建数据库
  2. 不建独立 pip 包
  3. 不做实时行情
  4. 不做 Web UI
  5. 不做云端部署
  6. 不改 vnpy 核心代码