Sequoia-X 项目研究站 · 数据层

Sequoia-X 数据层:baostock 后复权存进本地 SQLite

整套选股系统的地基是一张本地表:stock_daily。数据来自 baostock 的日 K(按后复权口径拉取),日常模式用多进程把当日增量补进来,首次用回填模式把历史一次性灌满。数据库就是一个文件,路径由 DB_PATH 控制,默认落在项目的 data/sequoia_v2.db,可以直接拷贝到另一台机器继续用。

数据源:baostock 日 K(免费、无需注册)存储:本地 SQLite 单文件依据 data/engine.py(2026-09 核验)
baostock 日 K后复权 adjustflag=1,免费免注册
stock_daily 表UNIQUE(symbol, date) + 索引
策略读取get_ohlcv 逐只 或 直连 sqlite3
数据层分层示意(依据 data/engine.py 整理,非官方架构图)。
依据来源官方 README · pyproject · .env.example
本页引用sequoia_x/data/engine.py · core/config.py
核验方式源码静态核验 + 本站实测(2026-10-10 在 Windows / Python 3.11.9 真机跑通安装、回填、日常运行与推送)
核验基准commit 444c0db(2026-05-09)· Python ≥3.10 · 2026-10-12 质检复核(含 2026-10-10 本站实测)
Why

Sequoia-X 为什么用 baostock + 后复权 + 本地库

这三个选择决定了它与其他取数方式的不同。

免费数据源,无需注册

README 说明数据层使用 baostock 拉取历史与增量日 K,并称其免费、无需注册、不限流。项目不需要你申请 API Key,也不产生数据费用;代价是数据频率只有日线,且依赖该服务的可用性。

后复权,适合增量存储

拉取时使用 adjustflag="1"(后复权)。README 的解释是后复权保持历史价格不变,因此增量写入不会因为后续除权导致既有数据错乱——这是「本地增量存库」这条路能成立的前提。

本地库,可拷贝可复盘

数据落在单个 SQLite 文件里,意味着同一天的形态判断在本地是确定的、可重复的,也能整库搬迁或备份。README 用「彻底规避东方财富反爬」来描述动机,这属于项目自述的表述,不是技术保证。

Schema

Sequoia-X 的 stock_daily 表有哪些字段?

建表语句在 data/engine.py 里,字段与 baostock 返回列一一对应。

字段类型与约束来源 / 含义注意点
idINTEGER PRIMARY KEY AUTOINCREMENTSQLite 自增主键仅用于行标识,策略不使用该列
symbolTEXT NOT NULL纯数字股票代码(如 600519、000001)入库前会从 baostock 的 sh.600000 形式里去掉市场前缀
dateTEXT NOT NULL交易日,文本格式日常模式按「最新日期 + 1 天」推算增量起点,格式必须是可解析的日期字符串
open / high / low / closeREAL后复权的开高低收后复权价与行情软件显示的真实成交价不同,不要直接与看盘软件比数字
volumeREAL成交量落库前过滤 volume <= 0,停牌与异常行不会进入策略计算
turnoverREAL成交额;回填路径由 baostock 的 amount 改名而来海龟策略的「成交额过亿」判断用的就是这一列
约束与索引UNIQUE(symbol, date) + 索引 idx_symbol_date(symbol, date)保证同股同日不重复,加速按股票查询两条写入路径都依赖它:日常先删后写、回填把冲突静默跳过
建表方式CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS每次启动引擎时执行首次运行会自动创建目录与库文件,不需要手工建表
Writes

Sequoia-X 的两条写入路径有什么区别?

同一个库被两种方式写入,容错策略完全不同。

对比项日常增量 sync_today_bulk历史回填 backfill适用场景
并发多进程(最多 8)单线程顺序拉取日常求快、回填求稳
起点每只股票「最新日期 + 1 天」到今天库内为空的用 START_DATE,已有的从断点续拉首次建库用回填,之后用日常
写入前动作对涉及的每个日期先 DELETE FROM stock_daily WHERE date = ?无删除,直接追加日常可安全重跑同一天
列名映射拉取时直接请求 amount 并命名为 turnover先取 amount 再 rename 成 turnover两条路最终落到同一列
冲突处理先删后写,一般不触发约束冲突捕获 sqlite3.IntegrityError 后静默跳过回填重跑不会因重复行中断
失败重试子进程内单只失败即跳过(记录在返回结果里)单只重试 3 次(2s / 4s / 8s),每次重试前重连回填更适合应对长连接超时
进度可见性汇总后打印写入条数每 500 只打印成功 / 跳过 / 失败计数,结束再汇总回填耗时长,需要进度反馈
重连策略每个子进程各自 login / logout每处理 200 只强制 logout 后重新 login规避单连接长时间存活被断开
别混用:日常模式会按日期整体删除再写入,如果用它去补一段历史区间,只会把「今天」这一天重写一遍(其余日期根本没被拉取);补历史要用回填模式。选择依据见排障续传页的决策表。
Symbols

Sequoia-X 的股票代码是怎么转换的?

清单来自 baostock 的基础信息接口;展示与链接时会再转一次格式。

环节规则适用场景注意点
取全市场清单bs.query_stock_basic(code_name="", code="")回填前确定标的范围接口一次性返回基础信息,需要自己按字段过滤
过滤条件仅保留 status == "1"(上市)且 stock_type == "1"(股票)排除退市与指数等非股票品种这也是「约 5200 只」这个口径的由来
入库代码取 "sh.600000".split(".")[1] 的纯数字部分库内统一为 6 位数字策略与推送都基于纯数字代码,不带市场前缀
取数时代码6 / 9 开头 → sh.,其余 → sz.把库内代码转回 baostock 格式北交所标的的代码段不在这条规则的显式分支里,实际以你能拉到的数据为准
推送链接代码6 开头 → SH,4 / 8 开头 → BJ,其余 → SZ生成雪球链接(xueqiu.com/S/<code>)两套前缀规则不同,读源码时注意区分
股票名称推送前用 bs.query_stock_basic(code=...) 逐只查名称卡片上显示中文名查不到名称时回退用代码本身,不会中断推送
Storage

Sequoia-X 数据库文件怎么备份与迁移?

单文件存储带来的便利与要留意的地方。

项目说明适用场景注意点
默认路径data/sequoia_v2.db(相对项目根目录)本地开发与定时任务相对路径意味着必须在项目目录下执行,cron 里要显式 cd
改路径改 .env 的 DB_PATH想放到数据盘或共享目录父目录会被自动创建,不需要手工建文件夹
起始日期START_DATE 默认 2024-01-01决定回填的历史深度它只影响「库里还没有的股票」的起点;要改历史深度需要清库重填
备份直接复制该文件(建议在程序未运行时进行)迁移到新机器或留档写入过程中复制可能拿到不一致的快照
体积未给出官方数字;随标的数与历史长度增长容量规划第三方页面给出的「2GB 磁盘」等数字非官方口径,本站不采用
并发写多进程只并行「拉取」,写库在父进程串行完成避免 SQLite 写锁竞争不要在任务运行时另开进程写同一文件
Boundary

Sequoia-X 数据边界:能给你什么、不能给什么

先确认口径,再决定要不要用它做研究。

维度实际情况适用场景注意点
频率只有日线(frequency="d")日频形态扫描没有分钟线与分时数据,日内策略不适用
复权方式后复权(回填与增量一致)跨除权日的区间比较价格数值不等于真实成交价,展示时不要与行情软件混用
基本面没有任何财务字段纯技术面研究估值、业绩类筛选需要另找数据源
品种覆盖按基础信息过滤后的 A 股股票个股形态扫描不含 ETF / 基金 / 指数成分(指数只用于对照)
停牌处理过滤 volume <= 0 的行避免停牌日污染形态窗口窗口按实际入库的交易日滚动,与自然日不等长
数据质量依赖上游数据源;项目未做完整性校验研究用途可接受本站不宣称数据「完全没有缺失」,重要决策前请自行交叉核对
声明:本站整理的是数据层的实现方式与字段口径,不对数据准确性、完整性或任何基于该数据的判断结果作保证。所有内容仅供研究参考,不构成投资建议。
Adjustment

前复权、后复权、不复权有什么区别?

这是 A 股数据里最容易踩的一脚:同一个策略、同一段行情,换一种复权口径,信号数就能变。项目在日 K 上固定用后复权,但它在另一个地方用了不复权——两处都要知道。

口径定义与序列形态对「每日增量存库」的影响对形态判定的影响本项目用在哪
后复权
(adjustflag=1)
以上市价为基准向后累乘复权因子,历史价格一旦确定就不再变化,序列整体大于现价合适:新数据直接追加,旧数据不用重算涨跌幅连续、除权日不跳空,用于形态与涨跌幅判断更干净全部日 K 数据(回填与日常增量都用它,见 data/engine.py)
前复权
(adjustflag=2)
以最新价为基准向前复权,每次除权都要重算整段历史不合适:除权后历史序列变化,本地库会新旧口径混存最新价与现价一致,看图形更直观,但历史值随日期漂移项目未使用
不复权
(adjustflag=3)
原始成交价,除权日出现价格跳空可增量,但缺口需要自己在判定里处理除权日的涨跌幅会出现假暴跌,直接算突破会误判海龟突破计算流通市值时用(_get_market_caps(),因为要用真实价格 × 流通股本)
成交额 turnoverbaostock 的 amount 字段,是当日真实成交金额与复权无关,直接入库本身就与复权无关,所以「成交额过亿」门槛不受口径影响海龟突破的 1 亿门槛
成交量 volume股数,不受价格复权影响;但送转后单位会变直接入库放量倍数(1.5 倍 / 2 倍)跨除权日比较时会被送股放大均线放量、涨停洗盘、跌停策略的放量判定
流通市值(派生)不复权收盘价 × 流通股本,股本由 volume ÷ (turn × 100) 反推不入库,每次命中后即时查询用于命中结果的排序,不参与选股条件海龟突破命中后的排序(按流通市值从大到小)
一个容易误解的点:同一个项目里两种复权口径并存不是矛盾——日 K 用后复权是为了「增量存库不重算」,流通市值必须用不复权价才是真实市值。你在写自己的策略时,如果拿后复权价去乘股本算市值,得到的会是一个无意义的数字。以官方源码为准。
Data audit

除权、停牌、缺失数据怎么审计?

「今天没结果」有两种可能:市场没有符合条件的票,或者你的库本身有问题。下面五条查询按顺序跑一遍,五分钟内能分清是哪种。

  1. 库内最新日期对不对

    SELECT MAX(date) AS latest, COUNT(DISTINCT date) AS days,
           COUNT(DISTINCT symbol) AS symbols, COUNT(*) AS rows
    FROM stock_daily;

    四个数字里,latest 必须等于你要回测/核查的那个交易日。非交易日运行不会新增行,所以它通常停在上一个交易日——这是预期行为,不是故障。

  2. 有没有重复行(UNIQUE 约束的兜底情况)

    SELECT symbol, date, COUNT(*) c
    FROM stock_daily GROUP BY symbol, date HAVING c > 1 LIMIT 20;

    正常应当返回空。表上建的是 UNIQUE (symbol, date),回填路径的重复写入会被 sqlite3.IntegrityError 捕获后静默跳过(except sqlite3.IntegrityError: pass)——也就是说重复不会入库,但也不会留下日志。真出现重复行说明表结构被手工改过。

  3. 找停牌与断档造成的时间断层

    -- 每只股票的起止日期与 K 线根数(根数明显少于日历天数的就是长期停牌或样本不足)
    SELECT symbol, MIN(date) AS first, MAX(date) AS last, COUNT(*) AS bars
    FROM stock_daily GROUP BY symbol
    ORDER BY bars ASC LIMIT 20;

    日增量在入库前会过滤 volume = 0 的行,停牌日因此不会写进库。后果是「最后一根 K 线」不一定是「今天」,策略读到的「今日」可能是一个月前——排查零命中时这是第一顺位嫌疑。

  4. 确认策略没被样本长度卡掉

    -- 统计各阈值下的可参与标的数
    SELECT
      SUM(bars >= 3)  AS ge3,    -- 涨停洗盘
      SUM(bars >= 21) AS ge21,   -- 海龟突破
      SUM(bars >= 40) AS ge40,   -- 高窄旗形
      SUM(bars >= 60) AS ge60    -- 上升趋势跌停
    FROM (SELECT symbol, COUNT(*) bars FROM stock_daily GROUP BY symbol);

    四个数字差得越多,说明回填起始日期(默认 2024-01-01)对新股覆盖越不均匀。K 线不足的标的会被静默跳过,不会报错。

  5. 抽样对价格——确认复权口径没被搞混

    SELECT date, open, close, volume, turnover FROM stock_daily
    WHERE symbol = '600519' ORDER BY date DESC LIMIT 5;

    把 close 与行情软件的前复权/不复权价对照:后复权价通常更高(含历史分红送股)。对不上不代表数据错,先确认你比较的是哪一种口径。

这五条不是官方工具:项目本身没有提供数据体检命令,以上查询是本站按表结构与源码写入逻辑整理的教程扩展(④级)。表结构以 data/engine.py 为准,任何口径冲突以官方源码为最终依据。
Universe

股票池边界:哪些股票根本不会进库

库里的「全市场」并不是你想象中的全市场。下面这些标的从一开始就不会出现在 stock_daily 里,而它们恰好是最容易让回测结论偏乐观的一批。

标的类型源码依据后果你怎么处理
已退市 / 已暂停上市股票get_all_symbols() 只保留 status == "1"(上市)且 stock_type == "1"(股票)的记录幸存者偏差:历史上退市的票永远不在库里,「一直持有」类结论天然偏乐观若结论涉及长期持有或胜率,必须显式声明这一点,或另外补入已退市样本
北交所早期代码(4xxxxx / 8xxxxx)_to_baostock_code() 只判「6 或 9 开头 → sh,其余 → sz」,没有北交所分支;而推送模块 _to_xueqiu_code() 却把 4/8 开头识别为 BJ取数侧会把 4/8 开头的代码拼成 sz.xxxxxx,baostock 查不到 → 该股票长期无数据、被跳过要覆盖北交所需自行改 _to_baostock_code() 的前缀判定并重跑回填;改动前先确认目标代码段的真实前缀
ST / *ST 股票基础信息筛选只看上市状态与证券类型,不排除 STST 股会进库并参与选股,但涨跌幅上限是 5%,而策略用固定 9.5% 系数判断涨停/跌停 → 会被系统性漏掉知道自己拿到的是「含 ST 的股票池」,且 ST 天然不满足 1.095 / 0.905 判定
指数、ETF、基金、可转债stock_type == "1" 只保留股票类,其他证券类型被过滤库里没有指数数据 —— 这意味着无法直接算「相对沪深 300 的超额」基准数据要另外取;用本项目库做回测时,「相对收益」只能自己补
上市不足 N 个交易日的次新股各策略有 _MIN_BARS(3 / 21 / 40 / 60)硬门槛,不足直接 continue样本不足的标的被静默跳过,不会出现在日志里把回填起始日期提前,让更多标的攒够 K 线;或接受「新股不参与」这一事实
B 股(900xxx / 200xxx)同样受「6 或 9 开头 → sh,其余 → sz」规则影响,且基础信息筛选口径未专门区分代码前缀可能与真实市场不符,导致取数失败或无数据与北交所同因:先确认代码段,再决定是否改代码映射
为什么这张表很重要:站内其他页面讲的都是「怎么跑」;这张表讲的是「跑出来的结果代表谁」。股票池决定结论的下限——用一个只含当前上市股票的库去验证「长期持有形态股」的策略,会系统性高估。这也是本站把验证方法放在数据审计之后的原因:先确认数据代表谁,再谈结论。
Measured: adjust

本站实测:后复权价能不能当成交价?

「后复权」三个字最容易被误用。本站用同一只票、同一区间,分别以 adjustflag="1"(后复权)和 adjustflag="3"(不复权)各拉一次真实数据,读数如下。

对照项(600519,2025-09-05 ~ 2026-10-09)后复权 adjustflag=1不复权 adjustflag=3差异
区间行数261261行数一致,说明两条口径只改价格、不改交易日
区间首日 close约 7.37 倍于不复权基准比值 7.3665
最近一日(2026-10-09)close9686.271263.00比值 7.6693
比值是否恒定不恒定:区间首日 7.3665 → 最近一日 7.6693说明复权因子随时间累积,不能用一个固定倍数换算回去
成交额(amount)4453223189.014453223189.01金额字段不受复权影响,两条口径完全一致
  1. 自己复现这两行数字

    import baostock as bs
    bs.login()
    for flag, label in [("1", "hfq"), ("3", "raw")]:
        rs = bs.query_history_k_data_plus("sh.600519", "date,close,amount",
            start_date="2025-09-05", end_date="2026-10-09", frequency="d", adjustflag=flag)
        rows = []
        while rs.next():
            rows.append(rs.get_row_data())
        print(label, rows[0], rows[-1])
    bs.logout()

    预期输出:两个口径的 amount 相同、close 差 7 倍以上,且比值在两个端点上不一样。

  2. 结论:什么能用、什么不能用

    用途能不能用库里的价原因
    算形态、算均线、算突破(策略内部)可以同一标的内部自洽,复权因子在序列里被吸收掉,形态比例正确
    算区间收益率可以分子分母同口径,除权跳空被还原
    当「我应该出多少钱买」的成交价不行9686.27 不是市场价,真实盘口在 1263 附近;按它下单、算持仓、算费用全错
    判断涨跌停、算一手多少钱不行涨跌停按不复权价与前收比,后复权序列里的「涨停」比例不是 10%
    算手续费、印花税、滑点不行这些成本按真实成交金额计,后复权价会把成本放大到 7 倍以上
    和行情软件截图对照对不上是正常的软件默认显示不复权(或前复权),口径不同不必怀疑数据错
回测必须补的一句话:项目自己在两处用了不同口径——日 K 拉取用 adjustflag="1",而海龟策略算流通市值时用 adjustflag="3" 取真实价。也就是说库里的价只适合做形态与收益,任何涉及「成交」的计算都要另取不复权价。这是源码事实,不是本站推断。
Measured: write path

本站实测:为什么一次增量写入会删掉全库 9.97%?

这一节是本轮最重的一条。第 3 张表(两条写入路径)已经写了「日常模式按日期先删后写」,但它到底有多危险,只有跑出来才知道。下面是受控复现的读数。

  1. 复现设计(用项目自己的代码,网络层是真实 baostock)

    复制一份真实库 → 把其中一只标的(600000)的日期截断到 2026-06-30(模拟「长期停牌 / 连续抓取失败导致落后」)→ 调用项目原生的 engine.sync_today_bulk()。被截断的那只标的因此有了 3 个月的追赶窗口,其余 264 只都是最新状态,窗口只有 1 天且取不到数据。

    # 复现脚本:seo_reports/sequoia-x/_run/verify_delete_scope.py
    DELETE FROM stock_daily WHERE symbol='600000' AND date > '2026-06-30';  -- 67 行
    written = DataEngine(settings).sync_today_bulk()   # 返回 67
  2. 前后读数

    度量项调用前调用后变化
    全库行数176,646159,036少 17,610 行(9.97%)
    受影响日期数(2026-07-01 起)67 个交易日67 个交易日日期还在,但内容被换掉了
    每个受影响日期的覆盖标的数264 只1 只只剩被追赶的那一只
    sync_today_bulk() 返回值—67返回值只报告「我写入了 67 行」,不报告删掉了 17,610 行
    耗时—17.4 秒整个过程没有任何一处报错或告警日志
  3. 为什么会长这样(源码逐行)

    with sqlite3.connect(self.db_path) as conn:
        for d in df["date"].unique().tolist():
            conn.execute("DELETE FROM stock_daily WHERE date = ?", (d,))   # ← 只带日期,不带 symbol
        df.to_sql("stock_daily", conn, if_exists="append", ...)            # ← 只写回本次取到的那些标的
        conn.commit()

    删除的粒度是「日期」,写回的粒度是「本次真正取到新数据的标的」。只要这两者的集合不相等,差值就是被删掉且没有补回来的历史。触发条件不止「标的落后」一种:

    触发场景为什么会出现后果量级
    某只标的长期落后(停牌、退市边缘、连续抓取失败)它的追赶窗口 = 从最后一次入库到今天,可能长达数月窗口内所有标的、所有日期的行被删
    同一天重复执行日常模式先跑成功的标的会被跳过(last_date >= today),只有失败的那些还留在队列里第二次运行把当天已写好的全市场行删掉,只写回少数几只
    节假日/周末连续触发非交易日取不到数据,通常直接返回(安全);但只要有任意一只标的能取到数据,就会进入删除分支删除范围等于那只标的的追赶窗口
  4. 怎么办(不用改代码也能做四件事)

    动作具体做法效果
    每天跑之前先备份库把 data/sequoia_v2.db 复制一份带日期的副本(68 MB 级别,几秒钟)出事能整库回滚,这是成本最低的一条
    别重复触发任务计划程序设「已在运行时不再启动新实例」;cron 不要写成每分钟掐掉「同一天第二次运行」这个高频触发点
    让标的别落后日常运行后查 SELECT symbol, MAX(date) m FROM stock_daily GROUP BY symbol HAVING m < (SELECT MAX(date) FROM stock_daily);,落后就单独补把追赶窗口压回 1 天,删除范围就只剩 1 天
    改代码(治本)把 DELETE ... WHERE date = ? 改成同时带 symbol 条件(或按 (symbol,date) 做 upsert)写入范围与删除范围一致,问题根除;属于改动项目源码,需自行评估
    发现掉数据后怎么恢复先停掉定时任务 → 用备份库覆盖 → 重跑回填(已入库会跳过)与日常模式不要直接对着被删过的库继续跑,否则会把残缺状态当成基线
这条实测在回答什么:竞品文章反复说「增量更新避免重复、只补当天」,本站跑完发现真正的风险不是重复,而是删除范围远大于写入范围。「全量回填与增量更新会不会漏数、重复或断档」这个问题,答案是:回填不漏(UNIQUE 约束兜住重复,已入库会跳过);增量在特定条件下会把历史「连片删空」,而且不报错。以官方源码为最终依据,本站只负责把实测现象与复现脚本摆出来。
Measured: calendar

本站实测:怎么确认「库里的最新日期」是哪个交易日

「最后一根 K 线」到底是不是今天、是不是交易日,是排查零命中时最先要确认的事。下面这套检查来自本机 789 只标的、524,104 行、668 个交易日的真实库。

  1. 先数出三个数

    SELECT MAX(date) AS latest, COUNT(DISTINCT date) AS days,
           COUNT(DISTINCT symbol) AS symbols, COUNT(*) AS rows
    FROM stock_daily;
    -- 本站读数:latest=2026-10-09, days=668, symbols=789, rows=524104

    判定规则很硬:如果今天是交易日、而且你已经跑过日常模式,latest 必须等于今天;否则不是数据源没出数,就是你的更新没成功。反过来说,latest 停在上一个交易日恰恰是非交易日的正常表现。

  2. 再看「相邻交易日之间隔了几天」(这才是 T+1 的真实代价)

    SELECT date, CAST(julianday(date) - julianday(LAG(date) OVER (ORDER BY date)) AS INT) AS gap_days
    FROM (SELECT DISTINCT date FROM stock_daily) ORDER BY date DESC LIMIT 12;

    本站 668 个交易日、667 个间隔的真实分布:

    间隔(自然日)出现次数含义
    1 天524正常周中交易日
    2 天1极少见(临时休市等)
    3 天124周五 → 周一,信号日与可成交日天然差 3 个自然日
    4~9 天16长假(清明/五一/端午/中秋等)
    11 天2国庆长假

    合计 142 / 667(21.3%)的相邻交易日间隔 ≥ 3 天。所以「收盘后出信号、第二天开盘买」这句话,在长假前后实际是「等 4~11 个自然日」,任何回测都必须按交易日而不是自然日推进。

  3. 想跟官方日历逐日对账?本机被限流了,如实记录

    >>> bs.query_trade_dates(start_date="2026-09-28", end_date="2026-10-10")
    error_code: 10001011  error_msg: 黑名单用户,请与管理员联系  rows: 0

    本站回填过程中触发了 baostock 的访问限制(详见安装页的速率实测),连交易日历接口一起被拒。所以本轮没有完成「官方交易日历 vs 库内日期」的逐日差集核对——这一项标注为未实测,不编造结论。可用的替代口径有两个:①用 SELECT DISTINCT date FROM stock_daily 作为「实际发生的交易日」基准(多标的取交集更稳);②把交易日历文件另行落盘后与库内日期做集合差。等数据源恢复后,本项应重跑并回填结论。

顺带一个有用的性质:因为回填写入的是「最近 N 年的完整历史」,库里的日期集合天然就是这个市场的交易日集合(本项目不写非交易日)。所以 SELECT DISTINCT date FROM stock_daily 可以直接当交易日历用——前提是所有标的都回填过、且没有标的落后。本轮 789 只全部从 2024-01-02 连续到 2026-10-09,668 个日期可以放心使用;判断依据以官方源码与 baostock 返回为准。
FAQ

数据层常见问题

Sequoia-X 为什么用后复权而不是前复权?

源码里拉取参数是 adjustflag="1"(后复权)。README 给出的理由是后复权下历史价格不变,增量写入时旧的记录不需要重算,适合这套「每天只补增量」的存储方式。口径细节以官方 README 与 baostock 文档为准。

Sequoia-X 库里的价格为什么和行情软件对不上?

因为库里存的是后复权价,行情软件默认多显示不复权价。两者在除权除息后会分叉,这是设计使然而不是数据错误。要做展示或对比时,请注明复权口径。

能自己往 Sequoia-X 库里加数据吗?

表结构是公开的,理论上可以手工插入或另写脚本导入,但要注意两点:UNIQUE(symbol, date) 约束会拒绝重复行;以及策略假定 date 是可排序解析的日期字符串。自行写入导致的结果差异由你自己承担,本站不做保证。

Sequoia-X 两种模式写同一张表会打架吗?

写库动作都在单个进程里串行执行,并发只用在「拉取」阶段;日常模式按日期先删后写,回填模式靠 UNIQUE 约束把重复行静默跳过。因此不要在同一时间同时跑两个模式,正常使用下二者是互补的。

Sequoia-X 数据不够 40 或 60 根怎么办?

高窄旗形与上升趋势跌停这两个策略会直接跳过 K 线不足的标的。解决办法是把 START_DATE 往前提(例如更早的年份)并重跑回填;默认值是 2024-01-01,历史深度越深,能参与计算的标的越多,但回填耗时也越长。

Sequoia-X 数据库能直接搬到别的机器吗?

可以。它是单个 SQLite 文件,迁移后把 DB_PATH 指向新位置即可继续日常更新。注意目录相对路径的基准是「执行命令时所在的目录」,在定时任务里要显式切到项目目录。

为什么库里找不到我要找的那只股票?

按四个可能依次排查:①它是否已退市或暂停上市(get_all_symbols() 只取 status=1 的记录,退市股不进库);②代码前缀是否被正确映射(源码只判 6/9 开头为 sh,北交所 4/8 开头会被误拼成 sz 而取不到数据);③证券类型是否为股票(指数、ETF、基金被 stock_type=1 过滤);④回填是否在它上市之前就结束了。判断依据以官方源码与 baostock 返回为准。

怎么确认某只股票的时间序列没有断层?

用本页第 3 条查询列出每只股票的 MIN(date)、MAX(date) 与 K 线根数,把根数与区间内的交易日数量对比即可。停牌日因为 volume = 0 在入库前被过滤,所以不会留下记录——断层是「静默」的,只能靠比对发现。任何策略在取值前都应先断言库内最新日期等于目标交易日,以官方源码口径为准。

数据就位后,看结果怎么推出去

每个策略有独立的 webhook 标识,可以推给不同的飞书群。