【量化系统从零构建 #04】存储设计:选型·建库·交易日历

【量化系统从零构建 #04】存储设计:选型·建库·交易日历

系列:《量化系统从零构建》|连载项目 · 纯 GET 取数 · 仅依赖 requests

适用:想给取数层配一个本地存储底座的读者;本篇是纯本地存储层,不发起网络请求,直接基于标准库 sqlite3 建库,为 #05 行情落库铺路。

1. 你将得到什么

  • 存储选型结论 :落库用 SQLite (零依赖、单文件、随开随用);分析导出用 Parquet (列式、压得小);即席分析用 DuckDB(直接 SQL 读 Parquet)。新手默认 SQLite 就够。
  • 建库函数 init_db :建好 trade_calendar(交易日历)与 stocks(股票清单)两张骨架表。
  • 交易日历 seed_calendar:用标准库生成工作日(周一至周五)写入日历表,并给出「取某月首个交易日」示例。
  • 本篇交付 :后续 #05--#08 的所有落库表都挂在这个库上,取数层(ZhituClient)拉到的数据落进这里。

2. 本篇取数约定

本篇是纯本地存储层,不发起任何网络请求 ,直接用 Python 标准库 sqlite3,不装任何第三方数据库驱动。下面的代码可在本地直接跑。

3. 存储选型对比

方案 角色 优点 何时用
SQLite 主落库 单文件、零依赖、事务稳 默认;每日增量更新、按代码/日期查询
Parquet 分析导出 列式压缩、读大表快 把日线/财务导出给 pandas 做离线分析
DuckDB 即席分析 直接 SQL 读 Parquet,免加载 多文件聚合、复杂 SQL 透视

本系列默认 SQLite 落库。Parquet / DuckDB 是「分析延伸」,用到时再引入,不影响主线。

4. 核心模板函数

python 复制代码
import sys, sqlite3, datetime

# ── 建库:交易日历 + 股票清单骨架 ──
def init_db(path=":memory:"):
    """建库并返回连接。path 给文件名即落盘,给 :memory: 即内存库。"""
    conn = sqlite3.connect(path)
    cur = conn.cursor()
    cur.execute("""
    CREATE TABLE IF NOT EXISTS trade_calendar (
        date TEXT PRIMARY KEY,
        year INT, month INT, day INT, weekday INT,
        is_trading_day INT
    )""")
    cur.execute("""
    CREATE TABLE IF NOT EXISTS stocks (
        code TEXT PRIMARY KEY,
        name TEXT,
        market TEXT,
        updated_at TEXT
    )""")
    conn.commit()
    return conn


def _weekdays(start, end):
    """生成 [start, end] 内的周一至周五(近似交易日,未剔除法定节假日)。"""
    s = datetime.date.fromisoformat(start)
    e = datetime.date.fromisoformat(end)
    d, out = s, []
    while d <= e:
        if d.weekday() < 5:            # 0=周一 ... 4=周五
            out.append(d)
        d += datetime.timedelta(days=1)
    return out


def seed_calendar(conn, start, end):
    """把 [start, end] 的工作日写入交易日历表,返回写入条数。"""
    cur = conn.cursor()
    rows = [(d.isoformat(), d.year, d.month, d.day, d.weekday(), 1)
            for d in _weekdays(start, end)]
    cur.executemany(
        "INSERT OR IGNORE INTO trade_calendar"
        "(date,year,month,day,weekday,is_trading_day) VALUES (?,?,?,?,?,?)", rows)
    conn.commit()
    return len(rows)


def first_trading_day(conn, year, month):
    """返回某年某月的第一个交易日(ISO 字符串),无则 None。"""
    cur = conn.cursor()
    cur.execute(
        "SELECT date FROM trade_calendar WHERE year=? AND month=? ORDER BY date LIMIT 1",
        (year, month))
    row = cur.fetchone()
    return row[0] if row else None


def run_check():
    # 合成数据仅逻辑校验(标准库,非真实行情)
    conn = init_db(":memory:")
    n = seed_calendar(conn, "2024-01-01", "2024-01-31")
    assert n == 23, n                      # 2024-01 共 23 个工作日
    cur = conn.cursor()
    cur.execute("SELECT COUNT(*) FROM trade_calendar WHERE weekday>=5")
    assert cur.fetchone()[0] == 0          # 周末不入库
    assert first_trading_day(conn, 2024, 1) == "2024-01-01"
    print("校验通过")


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--check":
        run_check()
    else:
        conn = init_db("quant.db")
        print("交易日历已建,2024-01 工作日数:",
              seed_calendar(conn, "2024-01-01", "2024-01-31"))

5. 跑通示例

把上面的代码复制到本地,填入你的 token 即可直接运行:它会请求对应接口、拉取真实数据,并输出归一化后的结构化字典(各字段含义见前文各小节)。

6. 坑与注意事项

  1. 近似交易日历要补节假日_weekdays 只排除周末,未剔除法定节假日(如元旦、春节)。实盘前需补一份交易日集合(可先用历史数据反推或手动维护一张表)。
  2. INSERT OR IGNORE 防重 :日历 date 是主键,重跑 seed_calendar 不会重复插入。
  3. 内存库 :memory: 仅当次会话有效 :run_check 用内存库验证逻辑;正式落库请传真实文件名(如 quant.db)。
  4. SQLite 并发写入弱:本系列是单进程定时更新,够用;多进程并发写要上 WAL 或换引擎。

7. 小结与下篇预告

本篇把存储底座钉死:SQLite 主库 + 交易日历表,并给出选型对比(Parquet / DuckDB 作分析延伸)。这是 #05--#08 落库章节的共同地基。

下一篇计划写 #05《行情落库:日线·K线·逐笔·盘口》 :在 init_db 上加 daily 表,用 #03 的 ZhituClient 拉日线,经字段归一后写入,并演示「拉取即落库」与失败降级不崩。

8. 免责声明

本文仅演示本地存储与接口字段归一的用法,所有代码示例均为演示数据,未含任何真实数据;文中示例数据仅作演示用途,不构成投资建议,亦不承诺收益。

相关推荐
yurenpai(27届找实习中)41 分钟前
Java 10 的 var 为什么不能随便用?从订单汇总看懂类型推断与泛型陷阱
java·开发语言·java18
、如果1 小时前
PDF图片文字提取零依赖方案:pymupdf+AI视觉实战
人工智能·数据分析·pdf·图片提取·文字提取·skills
米小虾1 小时前
不偷权重,只问问题:蒸馏攻击是怎么把前沿模型"问"走的,以及什么真能挡住
人工智能
2601_960554471 小时前
会议录音一键生成纪要:实测告别手工整理
人工智能
米小虾1 小时前
一周 AI 观察(9.5–9.11):AI 从"产品"变成"基础设施"
人工智能
Behaviour1 小时前
iPhone Duo 来了,苹果 Siri AI 接入定制 Gemini
人工智能·ios·语言模型·aigc·iphone
萧鼎1 小时前
Python 高性能Web框架神器 FastAPI:自动生成API文、基于Pydant、异步请求处理全搞定
前端·python·fastapi
2601_962299881 小时前
Linux执行Python脚本方法
linux·python·脚本·解释器·shebang
不要生病了1 小时前
扩散语言模型的“灵活性陷阱”:为什么训练时反而应该按自回归顺序探索?
人工智能·语言模型·回归