跳到主要内容

数据流与处理流程

统一入口​

所有市场数据请求通过 get_market_data(data_type, **kwargs) 统一入口处理:

数据下载时序​

个股数据下载的完整调用链路(以 K线下载为例):

关键链路节点:

节点实现
Session 管理下载器通过 _require_db()(downloaders/base.py,返回 get_db_session() 上下文管理器)获取会话,统一使用 with _require_db() as db:;DB 不可用时 db is None,由调用方短路返回 failed
重试机制_with_retry(fn, *args, max_retries=2):仅当异常字符串含 timeout/connection/reset/refused/network/dns/temporary/too many 才重试,等待 (attempt + 1) × 2s(即 2s→4s);其余异常直接上抛
降级链主源/备胎顺序由 fallback_registry.FALLBACK_REGISTRY["kline_daily"] 统一注册,下载器不硬编码源顺序
去重保障公告下载以 (code, title, pub_date) 为唯一键走 MySQL 原生批量 UPSERT(ON DUPLICATE KEY UPDATE);新闻维度已移交 fox_news 融合器幂等管理,不再写 ODS 表
数据校验download_quote 校验 price∈[0.01,10000]、change_pct∈±30%(data_sources/validators.py 的 valid_price/valid_change_pct),异常值仅记 warning,不阻塞写入
复权口径日K 由腾讯 fqkline 前复权直出写入 fox_kline_daily;旧 adj_factor=1.0(未复权标记)字段随 kline_daily 表退役一同移除
断档检测写库前用 core.trading_calendar.get_trade_dates() 核对区间内缺失交易日,仅 warn 并计入返回值的 gap_count
数据新鲜度通过 GET /datamgr/stats/freshness 检查 52 类关键维度最新日期是否在合理范围内(响应外层包着统一信封,payload 在 results)

数据组装流程​

每种 data_type 对应独立的组装函数(均在 services/market_data_service/builders.py,函数名为公开名、无前导下划线):

数据类型组装函数输出结构
sentimentbuild_sentiment(indices, breadth){indices, breadth, sentiment_score, sentiment_label, sample_based, timestamp}
distributionbuild_distribution(breadth){up, down, flat, limit_up, limit_down, total, bins[], sample_based, timestamp}
fund_flowbuild_fund_flow(flow){total_volume, main_net_inflow, limit_up_success_rate, prev_total_volume, timestamp}
industry_rankbuild_industry_rank(sectors){top_gainers[](≤5), top_losers[](≤5), timestamp}
hot_stocksbuild_hot_stocks(stocks){stocks[], total, source, source_label, as_of, timestamp}
sector_treemapbuild_sector_treemap(sectors){sectors[], total, up_count, down_count, timestamp}

其中 hot_stocks 的 source/source_label/as_of 用于显性标注降级链所处层级 (fox_stock_wide 全市场盘后 / tencent 全市场实时 / fox_quote_snapshot 自选样本 / eastmoney 东财涨幅榜 / xueqiu 雪球人气榜),前端据此切换标题,避免同一块卡片在不同 降级层级下被误读。distribution/sentiment 的 sample_based 标记广度是否来自抽样估算源。

情绪分数计算​

score = 50 # 基准分
# 指数涨跌贡献(权重 40%):只算 A 股指数(6 位纯数字代码),
# 港美股/外盘指数(HSI/DJI 等)仅展示、不参与评分
a_indices = [i for i in indices if str(i.get("code", "")).isdigit()]
if a_indices:
avg_change = sum(i.get("change_percent", 0) for i in a_indices) / len(a_indices)
score += avg_change * 5 * 0.4
# 涨跌比贡献(权重 40%)
if breadth.get("total", 0) > 0:
up_ratio = breadth["up"] / breadth["total"]
score += (up_ratio - 0.5) * 40
# 限制在 [0, 100]
score = max(0, min(100, round(score)))

indices 与 breadth 同时存在才计算分数,否则 sentiment_score=0、sentiment_label="未知"。

分数区间标签
75-100极度贪婪
60-74贪婪
40-59中性
25-39恐惧
0-24极度恐惧

数据有效性校验​

is_valid_data(data_type, data)(builders.py)是降级链的层级判据:某一层返回的数据 不合格时,上层继续向下一级降级(fetchers.py 的每级 if ... and is_valid_data(...)), 而不是把这份数据写进缓存或返回给前端。

def is_valid_data(data_type: str, data) -> bool:
if not data or not isinstance(data, dict):
return False
if data_type == "sentiment":
# 行级范围校验:只剔单条脏行(|change_pct| > 30 视为单位错误),不整体拒绝;
# price_key=None 跳过价格校验——外盘指数点位远超个股的 PRICE_MAX=10000
data["indices"] = filter_valid_rows(data["indices"], price_key=None,
change_pct_key="change_percent",
source="sentiment.indices", allow_zero_price=True)
return bool(data.get("indices")) or data.get("breadth") is not None
elif data_type == "distribution":
return data.get("total", 0) > 0
elif data_type == "fund_flow":
# main_net_inflow 可能为 None(陈旧数据不输出),按 0 处理
return data.get("total_volume", 0) > 0 or (data.get("main_net_inflow") or 0) != 0
elif data_type == "industry_rank":
return bool(data.get("top_gainers")) or bool(data.get("top_losers"))
elif data_type == "hot_stocks":
...
return len(data.get("stocks") or []) >= 5 # 不足 5 只视为残缺榜,触发降级
elif data_type == "sector_treemap":
return bool(data.get("sectors"))
return True

抽样估算源另有约束:非交易时段 sentiment/distribution 走「腾讯 → TDX」顺序, 但若腾讯返回的是 sample_based=True(约 50 只样本估算),会强制回试 TDX 全市场口径 (fetchers.py:492,样本数据让位);TDX 也拿不到时才回落到这份样本结果,且此时不再继续降级东财。

前端数据管理界面​

前端 DataManager 页面(src/pages/DataManager/index.tsx)提供 14 个功能标签:

标签功能对应API
数据概览各表记录数统计GET /datamgr/stats
个股下载K线/财务/事件等按类型下载与批量任务POST /datamgr/download/{code}、POST /datamgr/download-batch
实时行情盘口/分钟K/分钟资金流(不入库)GET /datamgr/realtime/{code}/quote、/minute-kline、/fund-flow-minute
市场数据行业排名/北向资金/全球快讯/市场级入库GET /datamgr/market/*、GET /datamgr/realtime/northbound
涨停板涨停/炸板/跌停/昨日涨停 四类池 + 涨停情绪GET /datamgr/market/limit-up、/broken-board、/limit-down、/yesterday-limit-up、/limit-sentiment
龙虎榜当日全市场龙虎榜GET /datamgr/market/dragon-tiger
快讯7x24 滚动快讯 + 同花顺快讯GET /datamgr/market/global-news、/cls-telegraph、/ths-kuaixun
自选股导入/管理自选股GET /datamgr/portfolio/list、POST /datamgr/portfolio/import
全A股全市场浏览(数据源 fox_stock_wide 融合宽表;行业下拉为腾讯三级口径)GET /datamgr/stock-base/list
研报生成AI 晨报/复盘GET /morning/note、GET /review/note
新鲜度关键维度新鲜度 + K线断档缺口GET /system/freshness、GET /datamgr/gaps?days=30
系统状态表体量体检 + 数据源探针(互查交叉校验)GET /datamgr/stats、GET /datamgr/probe-all
同步日志数据同步历史记录GET /datamgr/sync-logs
系统配置运行时参数调整GET/PUT /datamgr/config

「定时任务管理」不在本页:任务列表/开关/触发时间/运行历史统一在任务中心 /tasks (src/pages/TaskCenter/,TaskListPanel 消费 GET/PUT /datamgr/schedule 与 GET /datamgr/schedule/status)。

定时任务与数据发布窗口​

后台调度器(backend/app/services/scheduler.py + scheduler_tasks.py,纯标准库线程实现,零外部依赖,主循环 _TICK_INTERVAL = 30s)按 A 股数据实际发布时间执行多窗口 T+1 自动增量更新。SCHEDULED_TASKS 注册表共 87 个任务(59 个限定交易日、28 个每日执行;下表按触发时间排序,async_task 标记的长耗任务提交后台线程执行,不阻塞调度循环)。

时点只是近似顺序,真正的先后由 depends_on 门控保证

下表里的触发时间是近似的:长链路任务的实测耗时会漂(fox_kline_sync 15:40 起跑、 常到 17:45 之后才收尾),只靠"排在后面几分钟"会让下游拿到半截数据。2026-10-07 起 8 个下游任务在注册表里声明了 depends_on,到期时若上游未交付本次应执行日则本轮 跳过、30s 后自然重试(跳过不写 last_run_date,也不影响手动触发)。 判据四分支与新增依赖的硬约束见任务中心 · 依赖门控。 ::::

调度器跑在独立的 worker 进程里

XUANGU_ROLE=worker 的进程抢 MySQL GET_LOCK('xuangu_scheduler') 后才跑调度循环; XUANGU_ROLE=web 只服务 API、不起调度器,缺省 all 为单进程历史行为。 dev 下 backend/run.py 作 supervisor 同时拉起 web 与 worker 两个子进程, worker 不随热重载 —— 改调度器/任务代码须重启 run.py 才生效。 生产环境是 deploy/xuangu.service(web)与 deploy/xuangu-worker.service 成对的两个 unit。 详见任务中心与进程模型。

任务 key触发交易日限定产出表说明
data_cleanup04:30每日—每周数据过期清理(默认保留 3 年,SysConfig data.cleanup.retention_days 可调、下限 30 天);runner 内仅周一实际执行,其余日返回跳过。受保护历史表另带「已有数据下限」:保护线只用于阻止清理、不作删除目标,实际删除线取 min(保留窗口, 保护线, 库内最早行)——库内已有的历史(kline 1995 起 / 两融 2012 起 / 公告 1992 起)不因保留窗口被删,数据起点前移到 2010 也不会被裁掉;非保护表(当日快照类)照常按窗口裁剪
concept_block_sync05:20每日em_concept_block全市场板块归属刷新(逐股 HTTP,按 7 天新鲜窗口的覆盖度缺口决定是否动手,缺口小则整批跳过)
morning_note06:30交易日app_morning_notes晨报自动生成(AI 凭据失效时走本地规则口径降级稿并如实标注)
us_kline_sync06:30每日fox_us_kline_daily美股日K刷新(~548 只美股各一次 Yahoo Chart API 请求即得全历史,整段重拉 UPSERT 幂等;NO_ODS 直写豁免),async_task
premarket_sync08:30交易日em_margin_trading盘前两融同步(交易所 T+1 盘前公布上一交易日数据)
wholemarket_margin_sync08:40交易日em_margin_trading全市场两融 RAW 增量刷新(按日期批量,20 天窗口),async_task
futures_delivery_reminder09:00每日—期货交割日提醒(股指每月第三个周五 + 商品期货按交易所规则推算;当日及未来 3 天内有交割日则推送站内通知 + 可选邮件,纯本地零 HTTP)
regulation_sync09:00每日—法规源同步(深交所法规 + 证监会政策)
user_recall09:10每日—不活跃用户召回邮件(SMTP 未配置时安全跳过)
intraday_minute_kline09:30(每 5 分钟)交易日tdx_kline_minute盘中分钟K线同步,失败不重试
market_breadth_intraday09:30(每 5 分钟)交易日app_market_breadth_intraday盘中市场广度时序采样(支撑 /market/breadth-intraday);同一时点捎带一次腾讯批量取上证/深证成指点位与沪深成交额(深市成交额取深证综指 399106,与 fox_market_turnover_daily 同口径),脉搏取不到只空自家列、不阻断家数落库
portfolio_mon09:30交易日—持仓异动监控(交易时段每半小时检测 + 站内通知)
market_signal_sync14:55交易日em_popularity_rank东财市场信号快照(人气榜/当日异动),临近收盘取近终值
daily_report15:30交易日—每日投资报告 + 邮件推送(指数/持仓走 TDX 实时)
tdx_market_stat_sync15:32交易日tdx_market_stat_snapshotTDX 市场统计快照(涨跌/涨跌停家数),是 fox_market_daily 家数列的首选源,必须早于 16:00
eod_core15:35交易日fox_kline_daily日终核心同步(自选股:日K线/行情/财报/资金流/估值)
market_turnover_sync15:35交易日fox_market_turnover_daily两市成交额多源降级采集,逐日 UPSERT 累积
tdx_finance_sync15:38每日tdx_finance_snapshot全市场 TDX 财务快照(0x0010 批量接口,64 批 ≈3.2s,故每日全量而非增量);三条 STG 链路的 TDX 补充分支依赖本表
fox_kline_sync15:40交易日fox_kline_dailyfox K线域融合(全市场日K + 周/月/季/年),async_task;quant_calc 依赖其当日就绪;后置 purge_period_residue 清周期期内残行
fund_flow_sync15:45交易日fox_fund_flow_daily全市场资金流刷新(fox_wide_sync 的资金面列读本表「每 code 最新一行」,故必须早于 16:20)。股票池 = 目标日 ∪ 前一交易日 K 线并集(15:45 时当日 K 线常只落几百只);「已有」只认非空行(零值占位不挡缺口)
market_phase_sync15:45交易日app_market_phase情绪阶段快照落库(趋势×情绪九阶段)
tencent_market_sync15:50交易日tencent_stock_quote_daily腾讯全市场行情时序落库(榜单 26 字段 + QC 投影),早于 fox_wide_sync 以便其零自愈命中
fox_daily_sync16:00交易日fox_market_daily 等fox 事件+市场+估值域融合(14 类,含解禁 lockup),async_task;事件类次日补融、RAW 过期自愈
daily_review16:05交易日app_daily_reviews收盘复盘自动生成(同晨报的降级与标注规则)
sw_industry_sync16:10交易日sw_index_constituent申万行业分类同步(一级 31 + 二级 124 + 成分股),须早于 fox_wide_sync
macro_sync16:15每日fox_macro_indicator宏观指标融合(国内多源 + 东财海外),async_task
fox_wide_sync16:20交易日fox_stock_widefox 快照宽表域融合(master/industry/wide/行业指数),async_task;后置对账与清残留
macro_calendar_sync16:25每日fox_macro_release宏观发布日历(回看 60 天/前瞻 90 天),必须晚于 macro_sync(国内 actual/previous 零 HTTP 读指标表)
macro_history_sync16:30每日fox_macro_history宏观指标历史回补(国内月/季频按 _INDICATOR_DEFS 全深度取数,日频与不足 3 期的指标不落;夜间增量、超 7 天自动全深度),async_task;必须晚于 macro_sync(末尾与快照最新一期对账,同期次不同值即列名映射被改坏)
policy_rate_sync16:33每日fox_macro_history政策利率长序列回补(scope=policy:LPR/贷款基准月频 250 期 + Shibor 四期限 + 中债 2/10/30Y·美债 10Y·利差日频 2000 条,按列拆开落),async_task;读路径 policy_rates 按「只补该列 live 最早一期之前」合并,页面默认窗口(120 期 / 750 条)之外也有长历史、Shibor 单源限流时仍可读。准备金率/存贷款基准利率/7 天逆回购不落(数据本体在 macro_data 代码常量里,抄进库即同一事实两个来源);指标键加 policy_ 前缀,与 scope=cn 的判读序列不重名
industry_rank_sync16:35交易日tencent_industry_block_snapshot行业轮动快照落库(腾讯 getRank)
commodity_spot_sync16:36每日fox_commodity_spot_daily商品现货-期货基差采集(生意社 100ppi 现期表,54 品种现货价/近月与主力基差率)。分层串接:RAW ppi100_spot_daily(最新表 + 2020-01-01 起有界深回补单轮 ≤400 日页、剩余顺延下轮)→ STG stg_commodity_spot(融合器 auto_stg 自动补)→ DWD gap-driven 融合;采集层只写 RAW。async_task;非交易日也跑(回补幂等)
futures_kline_sync16:37每日fox_futures_kline_daily期货主连全历史日K刷新(新浪 InnerFuturesNewService 一次请求返回全历史,54 品种整段重拉 + 逐行变更检测、值未变不触库;收盘后补当日一根)。NO_ODS 直写豁免,走融合器(进度/血缘与其它域一致)。async_task;非交易日也跑(重拉幂等)
ext_market_sync16:40每日sina_cross_market_daily全球市场参考数据(指数/期货/外汇/美股,休市日也刷新);捎带 A50 期货 + 离岸人民币(腾讯全球目录实测无这两个标的,一次新浪请求落两只日频快照,行情日取源自报日期不归一 A 股日历)
etf_daily_sync16:40交易日fox_etf_dailyETF 份额/行情留痕(交易所份额日报 T+1 早间披露、当日行情 15:30 定型后拉齐,对齐 etf-main 采集节奏)
skill_cache_warmup16:40交易日—技能策略缓存预热(skill-wisdom-trading 等只存在于内存 _SKILL_DSL_DEFS,不在 SmartStrategy 表里,strategy_auto_run 不会自动执行;本任务在 fox_wide_sync 之后以默认参数预执行一遍并写入缓存,用户收盘后秒开),async_task(实测均值 51min,同步执行会把 16:40~19:30 窗口的十几个任务顺延一个身位)+ depends_on: [fox_wide_sync]
block_sync16:45交易日tencent_block_stock_snapshot板块成分与板块K线(成分取自当日 16:35 快照,须在其后)
sector_snapshot_sync16:46交易日em_sector_snapshot东财行业/概念板块列表快照(逐侧强制打上游,不查缓存)。此前该表无任何定时写入者,只有板块中心页按需访问才落库 → 没人点过的那侧当天就没有行(实测 09-24 industry 整侧缺失),故独立成任务;空侧计入 failed,不再静默 return
macro_regime_track16:50交易日app_macro_regime_daily宏观判读留痕 + 前向收益复盘(须晚于 macro_sync;落当日判读为 pending,另按 as_of 截断回放补最近 30 个交易日(不放未来数据,深度受日频 36 期窗口限制),过窗口读本地 fox_index_daily 回填上证 T+5,口径共用 services/forward_return.py)
market_liquidity_sync16:55交易日fox_market_liquidity_daily资金面与拥挤度采集(拥挤度/成交额/总市值比本地聚合零 HTTP;两融与指数估值 PE 走东财独有报表,FR/FDR 取中国货币网官方 CSV),每次重采最近 10 个交易日自愈 T+1 缺口,另增量刷新 fox_index_valuation_daily
news_sync17:00每日em_research_report自选股新闻/研报同步(新闻滚动快照落 RAW em_stock_news、研报落 em_research_report,供融合层「优先读当日落库」消费)
erp_sync17:00交易日fox_erp_daily股权风险溢价(PE 走中指数 + 本地宽表兜底、10Y 收益率走中债 + 东财兜底,覆盖沪深 300 / 中证 500 / 上证 50 三大基准;首次跑回填最近 5 年历史,后续只追加当日)
northbound_sync17:05交易日em_northbound_daily北向资金同步
ah_premium_sync17:05交易日fox_ah_premiumAH 溢价(遍历全部 AH 配对约 183 只,逐只计算溢价率并落库;首次跑填满全表,后续每日刷新)
cb_detail_sync17:10交易日fox_cb_detail可转债明细(东财 RPT_BOND_CB_LIST 全市场翻页,覆盖约 500 只可转债,单次约 2~5s)
futures_inventory_sync17:15交易日fox_futures_inventory期货持仓排名(东财 RPT_FUTU_STOCKDATA 全市场最新披露日,覆盖约 200 只期货合约)
pledge_risk_sync17:20交易日fox_pledge_risk股权质押风险(东财 RPT_CSDC_LIST 全市场最新交易日,覆盖约 3000 只股票)
goodwill_risk_sync17:25交易日fox_goodwill_risk商誉风险(东财 RPT_GOODWILL_STOCKDETAILS 全市场最新报告期,覆盖约 4000 只股票)
quant_calc17:30交易日fox_stock_indicators_daily量化指标(技术指标 / K线形态 / 筹码分布 / 估值历史分位 fox_valuation_percentile,读 fox_kline_daily 当日数据),async_task(实测均值 12min)+ depends_on: [fox_kline_sync] —— K线域实测常跑到 17:45 之后(09-30 那次 17:45:53 收尾),按死时点起跑会撞上"当日 K 线只写了一半"记成 partial,故由依赖门控按住,上游收尾后下个 tick(30s)即起跑
index_valuation_sync17:30交易日fox_index_valuation_csindex中证指数估值(中证指数官方 .xls 文件,覆盖 8 只核心指数:沪深 300 / 中证 500 / 上证 50 等)
selection_sync17:30交易日em_stock_selection_daily东财选股器全市场宽表
unified_selection_sync17:35交易日fox_unified_selection统一选股宽表融合(多源融合,替代 em_stock_selection_daily);须晚于 quant_calc(17:30),确保 fox_stock_indicators_daily 当日就绪
national_team_etf_sync17:35交易日fox_national_team_etf国家队 ETF 份额(上交所 + 深交所 ETF 份额,覆盖约 25 只国家队重仓 ETF)
dragon_score_sync17:40交易日app_dragon_score龙头评分落库(涨停池五维评分),builtin_track 的候选池来源
social_sentiment_sync17:45交易日social_sentiment_daily社交情绪融合(股吧/新闻/雪球)
strategy_auto_run18:00交易日—策略自动执行(宽表/指标就绪后跑用户策略,未就绪整批跳过)
decision_review18:00交易日app_decision_cardAI 决策卡复盘(回填 HIT/MISS + 收益,纯读本地K线,零 HTTP)
limitup_reason_sync18:05交易日ths_limitup_reason同花顺涨停原因(含逐条详因,约 25s):与涨停池互补,给「为什么涨停」的文本归因;晚于 selection_sync(榜单收盘后才更新完整)
chip_race_sync18:06交易日tdx_chip_race_daily通达信早盘/尾盘抢筹榜(两榜共 200 条,约 1s),紧随涨停原因
exchange_sync18:10交易日sse_stock_list 等沪深交易所权威数据同步 + 末尾 fuse_recent(10) 融合 fox_exchange_*
builtin_track18:15交易日app_builtin_board_pick内置三榜 top 20 留痕与前向收益复盘;须晚于 dragon_score_sync
tactic_signals18:20交易日app_tactic_signal_snapshot今日信号战法扫描(6 战法全市场)
preset_track18:25交易日app_preset_pick_daily量化 14 预设每日留痕 + 前向收益复盘(与 builtin_track 同构)
evening_sync18:30交易日em_dragon_tiger 等晚间披露同步(自选股:龙虎榜/大宗/公告/分红/股东人数)
strategy_lifecycle_review18:35交易日—策略生命周期审查(读 tactic 源近 30 天前向收益,按胜率阈值自动升降级 active/under_review/demoted/retired,状态存 SysConfig);须晚于 preset_track(18:25)让 scorecard 数据最新
kline_mirror_reconcile18:40每日—K线 Parquet 镜像对账/重建(只重建短日 + 清幻影分区与孤儿代码),async_task
news_signal_track18:40交易日app_news_signal_daily个股消息面判读留痕 + 前向收益复盘(纯本地 fox_news + fox_kline_daily 零 HTTP;须晚于 news_sync 17:00 与 social_sentiment_sync 17:45,语料按 pub_time 上界截断可传历史日期回放,无未来函数)
wholemarket_events_sync18:50交易日cninfo_announcement 等全市场事件 RAW 增量刷新(大宗/龙虎榜+席位明细/公告/分红/解禁/涨停池四类/股东户数/业绩预告/业绩快报/研报(2026-10-07 补齐:此前只有自选股逐股路径,全市场从未被定时覆盖);日级事件 30 天、涨停池 5 天、股东户数 200 天窗口),async_task
cninfo_market_sync19:00交易日cninfo_disclosure_schedule巨潮市场级数据(大宗/两融/互联互通/披露预约/债券)
limit_count_backfill19:15交易日app_limit_count_daily涨停/跌停家数历史回算(与供应商权威口径并排)
market_signal_track19:20交易日app_market_signal_daily市场级信号(跨市场风险温度 + 情绪周期)留痕与上证 T+1/T+3/T+5 前向收益复盘(零 HTTP;须晚于 limit_count_backfill 19:15)
freshness_snapshot19:30每日—数据新鲜度快照(全部任务完成后检查)
market_temperature_track19:40交易日app_market_temperature_daily市场温度读数冻结落日账(零 HTTP,只写当日不批量回填历史;须晚于 limit_count_backfill 19:15——跌停家数占权重 0.35)
cross_source_check19:40每日fox_data_quality跨源一致性抽检(宽表 PE/PB ↔ 融合估值表对齐比对,纯本地 DB 零 HTTP,结论落 fox_data_quality;读已落库历史,非交易日照样能查出口径漂移)
qfq_check19:45每日fox_data_quality前复权对拍校验(本地前复权序列 ↔ GBBQ 除权事件跳空检测,抽样验非法价/残差跳空/方向反,结论落 fox_data_quality;GBBQ 事件源与行情日历无关)
history_integrity_check19:50每日fox_data_quality历史数据完整性扫描(按日全市场表查历史中段缺日/残桩/整侧缺失/行数离群;扫描零 HTTP,「一次请求拿一天」型表的缺口当场自愈 ≤31 天区间,逐股型只报告)
factor_score_track19:55交易日fox_factor_score_daily因子分日度留痕 + 权重校准(每日落全市场 14 个具名因子分与综合分;compute_factor_scores 的输入是快照表 fox_stock_wide,历史因子分无法事后重建,故必须当日留痕——这是滚动 IC 权重校准的唯一样本基座。同任务内做自节流季度校准(≥85 天),只产 proposed 建议权重表 + 站内通知,不自动改运行时权重)
fin_statement_sync20:30每日sina_financial_statement财报低频全市场同步(新浪三表最旧优先预算轮换 1200 只/晚 + 东财 gjzb 备源补近 2 个报告期),async_task
ths_f10_events_sync21:00每日ths_f10_lhb同花顺事件择要低频轮换(龙虎榜/两融/大宗三快照;候选池=近 7 天东财主源事件表代码,最陈旧优先),async_task
executive_holding_sync21:20每日em_executive_holding高管增减持低频轮换(逐股拉取,最陈旧优先 1200 只/晚),async_task
fscore_calc21:30每日fox_stock_fscorePiotroski F-Score 全市场计算(读三表年报期,纯本地零 HTTP,实测 6045 只约 30s;须晚于 fin_statement_sync 20:30,不挂 quant_calc——财报一年才变 4 次)
bigv_sync21:40交易日app_bigv_article大V文章采集(雪球时间线 / 淘股吧主页,微信无公开接口记 skipped;启用作者最陈旧优先轮换 30 位/轮,逐作者 fail-soft),async_task
stg_news_rebuild21:45每日stg_newsSTG 新闻层重建(sync_stg_by_types(["news"])),async_task
gpcw_sync22:00每日tdx_gpcw_finance通达信专业财务全字段同步(每期约 5MB 官方文件装全市场 580 个字段,每期约 140 万行;md5 备忘 + 库内已有行双判据使其平时近乎零成本;另把 6 个已核对科目按最低优先级桥接进 sina_financial_indicator),async_task
bigv_digest22:10交易日app_bigv_digest大V每日综述两阶段 AI 管道(作者观点卡→共识/分歧/情景+次日预案),并先对前一日综述跑规则核验 → app_bigv_validation;AI 不可用时全程降级规则口径(status=partial),须晚于 bigv_sync,async_task
sse_exchange_sync22:30每日sse_stock_snapshotSSE 官方表低频轮换(快照/公司概况/股本结构/分红/日K 5 表;候选池 sse_stock_list 最陈旧优先 600 只/晚),sse_equity_change 已按 2026-10-07 裁定摘除(源端停供 + 纯插入撞 PK,表只读保留),async_task
szse_exchange_sync22:40每日szse_stock_snapshotSZSE 个股行情快照低频轮换(候选池 szse_stock_list 最陈旧优先 600 只/晚;SZSE 全站阻断时 SzseCoolingDown 优雅退出记 partial),async_task

同步范围:eod_core/evening_sync 等仅覆盖自选股,均复用 data_manager 现有下载器(UPSERT 幂等写入),个股间随机节流避免触发数据源限流;fox_kline_sync/fox_daily_sync/fox_wide_sync 三个域任务不依赖自选股,从 fox_stock_master 获取全量代码传给融合器,覆盖全市场(约 6000 只,注册表上限 _KLINE_BAR_FULL_MARKET_LIMIT = 6000)。

关键机制:

  • 错过补跑:任务成功后将日期持久化到 SysConfig(sched.{key}.last_success_date);到期判断是「最近一个应执行日(_last_expected_date)是否已成功」,桌面应用错过触发点后启动(如周六才开机)会自动补跑最近交易日的数据
  • 失败重试:任务级 max_retries 默认 2 次,退避 retry_delay × 2^attempt(retry_delay 逐任务配置,常见 300s,另有 60/180/600/900s);failed/total > 0.5(或 failed > 0 而 total == 0)即视为本次尝试失败触发重试,重试耗尽后当日不再自动重跑,次日或手动触发
  • 执行记录:每次尝试各写一行 app_task_run(任务中心为准,含 attempt/耗时/明细),同时兼容写 SyncLog(task_type 前缀 sched:,/datamgr/sync-logs 依赖)
本表由绊线看守,改时点必须同步三处

上表的任务清单、触发时点、「交易日限定」列与正文声明的任务总数,全部由 backend/tests/test_schedule_docs_consistency.py(CI 已纳入)拿 SCHEDULED_TASKS 注册表逐行对账;AGENTS.md 的「数据更新时序」同一份事实也在绊线范围内。 所以改一个任务的 trigger_hm,要同时改注册表、本表、AGENTS.md 三处—— 漏改不会悄悄过去,测试会红。该绊线还锁住 9 条时点依赖不变量 (如 fund_flow_sync < fox_wide_sync、dragon_score_sync < builtin_track), 挪时间把下游当日缺口挪出来时,它会直接指出是哪一条依赖断了。

  • 运行时配置:开关与触发时间持久化到 SysConfig(sched.{key}.enabled / sched.{key}.trigger_hm),通过 PUT /datamgr/schedule/{key} 修改后下个调度 tick(30s)即生效;ENABLE_SCHEDULER 为总开关
  • 手动触发:POST /datamgr/schedule/{key}/run 返回 202 并在线程中执行,任务已在运行时返回 409;未知任务 404

相关 API:

端点说明
GET /datamgr/schedule任务列表(定义 + 运行时配置)
GET /datamgr/schedule/status调度器线程状态 + 各任务运行态(上次执行/上次成功/下次预计)
PUT /datamgr/schedule/{key}修改开关/触发时间(需管理员)
POST /datamgr/schedule/{key}/run手动立即触发(需管理员);回填型任务可传 {"trade_date": "YYYY-MM-DD"}
GET /datamgr/schedule/product-freshness各任务产出表最新日期(任务→产出联动,如「任务成功但产出表没动」一看便知)

相关文档​

  • 架构总览
  • 数据源集成
  • 缓存策略
  • 错误处理
  • docs/reports/data-coverage-backfill-2026-10-07.md(数据覆盖与回补报告) — 全量表最新数据覆盖日期与缺口清单;历史基线见 docs/archive/数据库全量表覆盖审计报告-2026-10-01.md(已归档冻结,§2.3 后续验证)