数据流与处理流程
统一入口
所有市场数据请求通过 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,函数名为公开名、无前导下划线):
| 数据类型 | 组装函数 | 输出结构 |
|---|---|---|
| sentiment | build_sentiment(indices, breadth) | {indices, breadth, sentiment_score, sentiment_label, sample_based, timestamp} |
| distribution | build_distribution(breadth) | {up, down, flat, limit_up, limit_down, total, bins[], sample_based, timestamp} |
| fund_flow | build_fund_flow(flow) | {total_volume, main_net_inflow, limit_up_success_rate, prev_total_volume, timestamp} |
| industry_rank | build_industry_rank(sectors) | {top_gainers[](≤5), top_losers[](≤5), timestamp} |
| hot_stocks | build_hot_stocks(stocks) | {stocks[], total, source, source_label, as_of, timestamp} |
| sector_treemap | build_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,也不影响手动触发)。
判据四分支与新增依赖的硬约束见任务中心 · 依赖门控。
::::
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_cleanup | 04:30 | 每日 | — | 每周数据过期清理(默认保留 3 年,SysConfig data.cleanup.retention_days 可调、下限 30 天);runner 内仅周一实际执行,其余日返回跳过。受保护历史表另带「已有数据下限」:保护线只用于阻止清理、不作删除目标,实际删除线取 min(保留窗口, 保护线, 库内最早行)——库内已有的历史(kline 1995 起 / 两融 2012 起 / 公告 1992 起)不因保留窗口被删,数据起点前移到 2010 也不会被裁掉;非保护表(当日快照类)照常按窗口裁剪 |
concept_block_sync | 05:20 | 每日 | em_concept_block | 全市场板块归属刷新(逐股 HTTP,按 7 天新鲜窗口的覆盖度缺口决定是否动手,缺口小则整批跳过) |
morning_note | 06:30 | 交易日 | app_morning_notes | 晨报自动生成(AI 凭据失效时走本地规则口径降级稿并如实标注) |
us_kline_sync | 06:30 | 每日 | fox_us_kline_daily | 美股日K刷新(~548 只美股各一次 Yahoo Chart API 请求即得全历史,整段重拉 UPSERT 幂等;NO_ODS 直写豁免),async_task |
premarket_sync | 08:30 | 交易日 | em_margin_trading | 盘前两融同步(交易所 T+1 盘前公布上一交易日数据) |
wholemarket_margin_sync | 08:40 | 交易日 | em_margin_trading | 全市场两融 RAW 增量刷新(按日期批量,20 天窗口),async_task |
futures_delivery_reminder | 09:00 | 每日 | — | 期货交割日提醒(股指每月第三个周五 + 商品期货按交易所规则推算;当日及未来 3 天内有交割日则推送站内通知 + 可选邮件,纯本地零 HTTP) |
regulation_sync | 09:00 | 每日 | — | 法规源同步(深交所法规 + 证监会政策) |
user_recall | 09:10 | 每日 | — | 不活跃用户召回邮件(SMTP 未配置时安全跳过) |
intraday_minute_kline | 09:30(每 5 分钟) | 交易日 | tdx_kline_minute | 盘中分钟K线同步,失败不重试 |
market_breadth_intraday | 09:30(每 5 分钟) | 交易日 | app_market_breadth_intraday | 盘中市场广度时序采样(支撑 /market/breadth-intraday);同一时点捎带一次腾讯批量取上证/深证成指点位与沪深成交额(深市成交额取深证综指 399106,与 fox_market_turnover_daily 同口径),脉搏取不到只空自家列、不阻断家数落库 |
portfolio_mon | 09:30 | 交易日 | — | 持仓异动监控(交易时段每半小时检测 + 站内通知) |
market_signal_sync | 14:55 | 交易日 | em_popularity_rank | 东财市场信号快照(人气榜/当日异动),临近收盘取近终值 |
daily_report | 15:30 | 交易日 | — | 每日投资报告 + 邮件推送(指数/持仓走 TDX 实时) |
tdx_market_stat_sync | 15:32 | 交易日 | tdx_market_stat_snapshot | TDX 市场统计快照(涨跌/涨跌停家数),是 fox_market_daily 家数列的首选源,必须早于 16:00 |
eod_core | 15:35 | 交易日 | fox_kline_daily | 日终核心同步(自选股:日K线/行情/财报/资金流/估值) |
market_turnover_sync | 15:35 | 交易日 | fox_market_turnover_daily | 两市成交额多源降级采集,逐日 UPSERT 累积 |
tdx_finance_sync | 15:38 | 每日 | tdx_finance_snapshot | 全市场 TDX 财务快照(0x0010 批量接口,64 批 ≈3.2s,故每日全量而非增量);三条 STG 链路的 TDX 补充分支依赖本表 |
fox_kline_sync | 15:40 | 交易日 | fox_kline_daily | fox K线域融合(全市场日K + 周/月/季/年),async_task;quant_calc 依赖其当日就绪;后置 purge_period_residue 清周期期内残行 |
fund_flow_sync | 15:45 | 交易日 | fox_fund_flow_daily | 全市场资金流刷新(fox_wide_sync 的资金面列读本表「每 code 最新一行」,故必须早于 16:20)。股票池 = 目标日 ∪ 前一交易日 K 线并集(15:45 时当日 K 线常只落几百只);「已有」只认非空行(零值占位不挡缺口) |
market_phase_sync | 15:45 | 交易日 | app_market_phase | 情绪阶段快照落库(趋势×情绪九阶段) |
tencent_market_sync | 15:50 | 交易日 | tencent_stock_quote_daily | 腾讯全市场行情时序落库(榜单 26 字段 + QC 投影),早于 fox_wide_sync 以便其零自愈命中 |
fox_daily_sync | 16:00 | 交易日 | fox_market_daily 等 | fox 事件+市场+估值域融合(14 类,含解禁 lockup),async_task;事件类次日补融、RAW 过期自愈 |
daily_review | 16:05 | 交易日 | app_daily_reviews | 收盘复盘自动生成(同晨报的降级与标注规则) |
sw_industry_sync | 16:10 | 交易日 | sw_index_constituent | 申万行业分类同步(一级 31 + 二级 124 + 成分股),须早于 fox_wide_sync |
macro_sync | 16:15 | 每日 | fox_macro_indicator | 宏观指标融合(国内多源 + 东财海外),async_task |
fox_wide_sync | 16:20 | 交易日 | fox_stock_wide | fox 快照宽表域融合(master/industry/wide/行业指数),async_task;后置对账与清残留 |
macro_calendar_sync | 16:25 | 每日 | fox_macro_release | 宏观发布日历(回看 60 天/前瞻 90 天),必须晚于 macro_sync(国内 actual/previous 零 HTTP 读指标表) |
macro_history_sync | 16:30 | 每日 | fox_macro_history | 宏观指标历史回补(国内月/季频按 _INDICATOR_DEFS 全深度取数,日频与不足 3 期的指标不落;夜间增量、超 7 天自动全深度),async_task;必须晚于 macro_sync(末尾与快照最新一期对账,同期次不同值即列名映射被改坏) |
policy_rate_sync | 16: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_sync | 16:35 | 交易日 | tencent_industry_block_snapshot | 行业轮动快照落库(腾讯 getRank) |
commodity_spot_sync | 16: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_sync | 16:37 | 每日 | fox_futures_kline_daily | 期货主连全历史日K刷新(新浪 InnerFuturesNewService 一次请求返回全历史,54 品种整段重拉 + 逐行变更检测、值未变不触库;收盘后补当日一根)。NO_ODS 直写豁免,走融合器(进度/血缘与其它域一致)。async_task;非交易日也跑(重拉幂等) |
ext_market_sync | 16:40 | 每日 | sina_cross_market_daily | 全球市场参考数据(指数/期货/外汇/美股,休市日也刷新);捎带 A50 期货 + 离岸人民币(腾讯全球目录实测无这两个标的,一次新浪请求落两只日频快照,行情日取源自报日期不归一 A 股日历) |
etf_daily_sync | 16:40 | 交易日 | fox_etf_daily | ETF 份额/行情留痕(交易所份额日报 T+1 早间披露、当日行情 15:30 定型后拉齐,对齐 etf-main 采集节奏) |
skill_cache_warmup | 16: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_sync | 16:45 | 交易日 | tencent_block_stock_snapshot | 板块成分与板块K线(成分取自当日 16:35 快照,须在其后) |
sector_snapshot_sync | 16:46 | 交易日 | em_sector_snapshot | 东财行业/概念板块列表快照(逐侧强制打上游,不查缓存)。此前该表无任何定时写入者,只有板块中心页按需访问才落库 → 没人点过的那侧当天就没有行(实测 09-24 industry 整侧缺失),故独立成任务;空侧计入 failed,不再静默 return |
macro_regime_track | 16:50 | 交易日 | app_macro_regime_daily | 宏观判读留痕 + 前向收益复盘(须晚于 macro_sync;落当日判读为 pending,另按 as_of 截断回放补最近 30 个交易日(不放未来数据,深度受日频 36 期窗口限制),过窗口读本地 fox_index_daily 回填上证 T+5,口径共用 services/forward_return.py) |
market_liquidity_sync | 16:55 | 交易日 | fox_market_liquidity_daily | 资金面与拥挤度采集(拥挤度/成交额/总市值比本地聚合零 HTTP;两融与指数估值 PE 走东财独有报表,FR/FDR 取中国货币网官方 CSV),每次重采最近 10 个交易日自愈 T+1 缺口,另增量刷新 fox_index_valuation_daily |
news_sync | 17:00 | 每日 | em_research_report | 自选股新闻/研报同步(新闻滚动快照落 RAW em_stock_news、研报落 em_research_report,供融合层「优先读当日落库」消费) |
erp_sync | 17:00 | 交易日 | fox_erp_daily | 股权风险溢价(PE 走中指数 + 本地宽表兜底、10Y 收益率走中债 + 东财兜底,覆盖沪深 300 / 中证 500 / 上证 50 三大基准;首次跑回填最近 5 年历史,后续只追加当日) |
northbound_sync | 17:05 | 交易日 | em_northbound_daily | 北向资金同步 |
ah_premium_sync | 17:05 | 交易日 | fox_ah_premium | AH 溢价(遍历全部 AH 配对约 183 只,逐只计算溢价率并落库;首次跑填满全表,后续每日刷新) |
cb_detail_sync | 17:10 | 交易日 | fox_cb_detail | 可转债明细(东财 RPT_BOND_CB_LIST 全市场翻页,覆盖约 500 只可转债,单次约 2~5s) |
futures_inventory_sync | 17:15 | 交易日 | fox_futures_inventory | 期货持仓排名(东财 RPT_FUTU_STOCKDATA 全市场最新披露日,覆盖约 200 只期货合约) |
pledge_risk_sync | 17:20 | 交易日 | fox_pledge_risk | 股权质押风险(东财 RPT_CSDC_LIST 全市场最新交易日,覆盖约 3000 只股票) |
goodwill_risk_sync | 17:25 | 交易日 | fox_goodwill_risk | 商誉风险(东财 RPT_GOODWILL_STOCKDETAILS 全市场最新报告期,覆盖约 4000 只股票) |
quant_calc | 17: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_sync | 17:30 | 交易日 | fox_index_valuation_csindex | 中证指数估值(中证指数官方 .xls 文件,覆盖 8 只核心指数:沪深 300 / 中证 500 / 上证 50 等) |
selection_sync | 17:30 | 交易日 | em_stock_selection_daily | 东财选股器全市场宽表 |
unified_selection_sync | 17:35 | 交易日 | fox_unified_selection | 统一选股宽表融合(多源融合,替代 em_stock_selection_daily);须晚于 quant_calc(17:30),确保 fox_stock_indicators_daily 当日就绪 |
national_team_etf_sync | 17:35 | 交易日 | fox_national_team_etf | 国家队 ETF 份额(上交所 + 深交所 ETF 份额,覆盖约 25 只国家队重仓 ETF) |
dragon_score_sync | 17:40 | 交易日 | app_dragon_score | 龙头评分落库(涨停池五维评分),builtin_track 的候选池来源 |
social_sentiment_sync | 17:45 | 交易日 | social_sentiment_daily | 社交情绪融合(股吧/新闻/雪球) |
strategy_auto_run | 18:00 | 交易日 | — | 策略自动执行(宽表/指标就绪后跑用户策略,未就绪整批跳过) |
decision_review | 18:00 | 交易日 | app_decision_card | AI 决策卡复盘(回填 HIT/MISS + 收益,纯读本地K线,零 HTTP) |
limitup_reason_sync | 18:05 | 交易日 | ths_limitup_reason | 同花顺涨停原因(含逐条详因,约 25s):与涨停池互补,给「为什么涨停」的文本归因;晚于 selection_sync(榜单收盘后才更新完整) |
chip_race_sync | 18:06 | 交易日 | tdx_chip_race_daily | 通达信早盘/尾盘抢筹榜(两榜共 200 条,约 1s),紧随涨停原因 |
exchange_sync | 18:10 | 交易日 | sse_stock_list 等 | 沪深交易所权威数据同步 + 末尾 fuse_recent(10) 融合 fox_exchange_* |
builtin_track | 18:15 | 交易日 | app_builtin_board_pick | 内置三榜 top 20 留痕与前向收益复盘;须晚于 dragon_score_sync |
tactic_signals | 18:20 | 交易日 | app_tactic_signal_snapshot | 今日信号战法扫描(6 战法全市场) |
preset_track | 18:25 | 交易日 | app_preset_pick_daily | 量化 14 预设每日留痕 + 前向收益复盘(与 builtin_track 同构) |
evening_sync | 18:30 | 交易日 | em_dragon_tiger 等 | 晚间披露同步(自选股:龙虎榜/大宗/公告/分红/股东人数) |
strategy_lifecycle_review | 18:35 | 交易日 | — | 策略生命周期审查(读 tactic 源近 30 天前向收益,按胜率阈值自动升降级 active/under_review/demoted/retired,状态存 SysConfig);须晚于 preset_track(18:25)让 scorecard 数据最新 |
kline_mirror_reconcile | 18:40 | 每日 | — | K线 Parquet 镜像对账/重建(只重建短日 + 清幻影分区与孤儿代码),async_task |
news_signal_track | 18:40 | 交易日 | app_news_signal_daily | 个股消息面判读留痕 + 前向收益复盘(纯本地 fox_news + fox_kline_daily 零 HTTP;须晚于 news_sync 17:00 与 social_sentiment_sync 17:45,语料按 pub_time 上界截断可传历史日期回放,无未来函数) |
wholemarket_events_sync | 18:50 | 交易日 | cninfo_announcement 等 | 全市场事件 RAW 增量刷新(大宗/龙虎榜+席位明细/公告/分红/解禁/涨停池四类/股东户数/业绩预告/业绩快报/研报(2026-10-07 补齐:此前只有自选股逐股路径,全市场从未被定时覆盖);日级事件 30 天、涨停池 5 天、股东户数 200 天窗口),async_task |
cninfo_market_sync | 19:00 | 交易日 | cninfo_disclosure_schedule | 巨潮市场级数据(大宗/两融/互联互通/披露预约/债券) |
limit_count_backfill | 19:15 | 交易日 | app_limit_count_daily | 涨停/跌停家数历史回算(与供应商权威口径并排) |
market_signal_track | 19:20 | 交易日 | app_market_signal_daily | 市场级信号(跨市场风险温度 + 情绪周期)留痕与上证 T+1/T+3/T+5 前向收益复盘(零 HTTP;须晚于 limit_count_backfill 19:15) |
freshness_snapshot | 19:30 | 每日 | — | 数据新鲜度快照(全部任务完成后检查) |
market_temperature_track | 19:40 | 交易日 | app_market_temperature_daily | 市场温度读数冻结落日账(零 HTTP,只写当日不批量回填历史;须晚于 limit_count_backfill 19:15——跌停家数占权重 0.35) |
cross_source_check | 19:40 | 每日 | fox_data_quality | 跨源一致性抽检(宽表 PE/PB ↔ 融合估值表对齐比对,纯本地 DB 零 HTTP,结论落 fox_data_quality;读已落库历史,非交易日照样能查出口径漂移) |
qfq_check | 19:45 | 每日 | fox_data_quality | 前复权对拍校验(本地前复权序列 ↔ GBBQ 除权事件跳空检测,抽样验非法价/残差跳空/方向反,结论落 fox_data_quality;GBBQ 事件源与行情日历无关) |
history_integrity_check | 19:50 | 每日 | fox_data_quality | 历史数据完整性扫描(按日全市场表查历史中段缺日/残桩/整侧缺失/行数离群;扫描零 HTTP,「一次请求拿一天」型表的缺口当场自愈 ≤31 天区间,逐股型只报告) |
factor_score_track | 19:55 | 交易日 | fox_factor_score_daily | 因子分日度留痕 + 权重校准(每日落全市场 14 个具名因子分与综合分;compute_factor_scores 的输入是快照表 fox_stock_wide,历史因子分无法事后重建,故必须当日留痕——这是滚动 IC 权重校准的唯一样本基座。同任务内做自节流季度校准(≥85 天),只产 proposed 建议权重表 + 站内通知,不自动改运行时权重) |
fin_statement_sync | 20:30 | 每日 | sina_financial_statement | 财报低频全市场同步(新浪三表最旧优先预算轮换 1200 只/晚 + 东财 gjzb 备源补近 2 个报告期),async_task |
ths_f10_events_sync | 21:00 | 每日 | ths_f10_lhb | 同花顺事件择要低频轮换(龙虎榜/两融/大宗三快照;候选池=近 7 天东财主源事件表代码,最陈旧优先),async_task |
executive_holding_sync | 21:20 | 每日 | em_executive_holding | 高管增减持低频轮换(逐股拉取,最陈旧优先 1200 只/晚),async_task |
fscore_calc | 21:30 | 每日 | fox_stock_fscore | Piotroski F-Score 全市场计算(读三表年报期,纯本地零 HTTP,实测 6045 只约 30s;须晚于 fin_statement_sync 20:30,不挂 quant_calc——财报一年才变 4 次) |
bigv_sync | 21:40 | 交易日 | app_bigv_article | 大V文章采集(雪球时间线 / 淘股吧主页,微信无公开接口记 skipped;启用作者最陈旧优先轮换 30 位/轮,逐作者 fail-soft),async_task |
stg_news_rebuild | 21:45 | 每日 | stg_news | STG 新闻层重建(sync_stg_by_types(["news"])),async_task |
gpcw_sync | 22:00 | 每日 | tdx_gpcw_finance | 通达信专业财务全字段同步(每期约 5MB 官方文件装全市场 580 个字段,每期约 140 万行;md5 备忘 + 库内已有行双判据使其平时近乎零成本;另把 6 个已核对科目按最低优先级桥接进 sina_financial_indicator),async_task |
bigv_digest | 22:10 | 交易日 | app_bigv_digest | 大V每日综述两阶段 AI 管道(作者观点卡→共识/分歧/情景+次日预案),并先对前一日综述跑规则核验 → app_bigv_validation;AI 不可用时全程降级规则口径(status=partial),须晚于 bigv_sync,async_task |
sse_exchange_sync | 22:30 | 每日 | sse_stock_snapshot | SSE 官方表低频轮换(快照/公司概况/股本结构/分红/日K 5 表;候选池 sse_stock_list 最陈旧优先 600 只/晚),sse_equity_change 已按 2026-10-07 裁定摘除(源端停供 + 纯插入撞 PK,表只读保留),async_task |
szse_exchange_sync | 22:40 | 每日 | szse_stock_snapshot | SZSE 个股行情快照低频轮换(候选池 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 | 各任务产出表最新日期(任务→产出联动,如「任务成功但产出表没动」一看便知) |