Files
tick-stock-panel/frontend/src/lib/queryKeys.ts
T
shy3130 d9f7bc645d feat(backtest): 回测页支持分钟策略与分钟级成交展示
- 策略下拉改取 'all' 时段, 分钟策略入列表并加 sky 色「分钟」徽章
- 选中分钟策略条件化 UI: 隐藏分钟成交开关与 signal_next_minute,
  建仓口径固定显示「信号分钟收盘」, 展示本地分钟K覆盖提示与起始日越界警告
- 交易明细买入列渲染 HH:MM 分钟徽章 (entry_date 携带 YYYY-MM-DD HH:MM)
- SSE 连接前先 fetch 探测, HTTP 400 直接展示后端 detail,
  不再误入 EventSource 无限重连
2026-08-30 19:05:17 +08:00

136 lines
7.4 KiB
TypeScript

/**
* 集中管理所有 React Query key。
*
* - 新增查询只需在此加一行,所有消费方自动引用。
* - SSE invalidation 基于 SSE_INVALIDATE_PREFIXES 列表,新增 key 无需改 useQuoteStream。
*/
// ===== Query Key 工厂 =====
export const QK = {
// 全局 / 共享 (Layout 预取)
capabilities: ['capabilities'] as const,
settings: ['settings'] as const,
endpoints: ['endpoints'] as const,
version: ['version'] as const,
preferences: ['preferences'] as const,
dataSources: ['data-sources'] as const,
quoteStatus: ['quote-status'] as const,
quoteInterval: ['quote-interval'] as const,
overviewMarket: (asOf?: string) => ['overview-market', asOf ?? 'latest'] as const,
indexQuotes: ['index-quotes'] as const,
indexList: ['index-list'] as const,
// Watchlist
watchlist: ['watchlist'] as const,
watchlistGroups: ['watchlist-groups'] as const,
watchlistQuotes: ['watchlist-quotes'] as const,
watchlistEnriched: (ext?: string) => ['watchlist-enriched', ext] as const,
// 异动边缘总览 (开启监控时才查询, 参数为 min_closeness/limit)
abnormalOverview: (minCloseness: number, limit: number) => ['abnormal-overview', minCloseness, limit] as const,
// 不用 watchlist- 前缀: 日K历史盘中几乎不变, 若被 SSE quotes_updated 高频失效
// (expert 1s) 会导致全自选日K每秒重拉, staleTime 形同虚设。
// 刷新点: staleTime 过期 + Watchlist 增删自选/改蜡烛天数时的手动失效;
// 当日最后一根蜡烛由 Watchlist 用 enriched 实时 OHLC 前端修补 (零额外请求)。
watchlistKlineBatch: (symbols: string) => ['kline-batch', symbols] as const,
// 不用 watchlist- 前缀: 避免被 SSE quotes_updated 高频失效(expert 1s/pro 2s)
// 导致每次都拉 TickFlow 触限流。分时图用固定 refetchInterval 刷新即可。
minuteBatch: (symbols: string) => ['minute-batch', symbols] as const,
instrumentSearch: (q: string, assetTypes?: string) => ['instrument-search', q, assetTypes ?? 'stock'] as const,
// Screener
screener: ['screener'] as const,
screenerStrategies: (assetType: string = 'stock', timeframe: '1d' | '1m' | 'all' = '1d') => ['screener-strategies', assetType, timeframe] as const,
screenerCachedSummary: ['screener-cached', 'summary'] as const,
screenerCachedResult: (strategyId: string, asOf?: string, ext?: string) => ['screener-cached', 'strategy', strategyId, asOf ?? '', ext ?? ''] as const,
screenerCached: (asOf?: string, ext?: string) => ['screener-cached', 'all', asOf ?? '', ext ?? ''] as const,
screenerKlineBatch: (symbols: string) => ['screener-kline-batch', symbols] as const,
marketSnapshot: ['market-snapshot'] as const,
limitLadder: (asOf?: string) => ['limit-ladder', asOf] as const,
// Backtest
backtestStatus: ['backtest-status'] as const,
factorColumns: ['backtest-factor-columns'] as const,
miningRuns: ['backtest-mining-runs'] as const,
miningAvailability: (assetType: string, profile: string, start: string, end: string) =>
['backtest-mining-availability', assetType, profile, start, end] as const,
miningRun: (id: string) => ['backtest-mining-run', id] as const,
miningResult: (id: string) => ['backtest-mining-result', id] as const,
miningConfig: ['backtest-mining-config'] as const,
researchCandidates: ['research-candidates'] as const,
strategyLinkOptions: (assetType?: 'stock' | 'etf') => assetType
? ['strategy-link-options', assetType] as const
: ['strategy-link-options'] as const,
strategyDetail: (id: string) => ['strategy-detail', id] as const,
// Data / Pipeline
dataStatus: ['data-status'] as const,
pipelineJobs: ['pipeline-jobs'] as const,
pipelineJob: (id: string) => ['pipeline-job', id] as const,
extData: ['ext-data'] as const,
extDataRows: (id: string, date?: string, limit?: number, columns?: string) => ['ext-data-rows', id, date, limit, columns] as const,
dimensionMembers: (id: string, field: string, value: string, date?: string) => ['dimension-members', id, field, value, date] as const,
analysisMenus: ['analysis-menus'] as const,
analysisMenu: (id: string) => ['analysis-menu', id] as const,
// Kline
kline: (symbol: string, start: string, end: string, extColumns?: string) =>
['kline', symbol, start, end, extColumns ?? ''] as const,
stockLevels: (symbol: string, days?: number) => ['stock-levels', symbol, days ?? 120] as const,
klineMinute: (symbol: string, date: string) =>
['kline-minute', symbol, date] as const,
klineMinuteRange: (symbol: string, days: number) =>
['kline-minute-range', symbol, days] as const,
indexDaily: (symbol: string, start: string, end: string) =>
['index-daily', symbol, start, end] as const,
indexMinute: (symbol: string, date: string) =>
['index-minute', symbol, date] as const,
// Schema
extDataSchemaAll: ['ext-data-schema-all'] as const,
tableSchema: (table: string) => ['table-schema', table] as const,
// Custom Signals
customSignals: ['custom-signals'] as const,
customSignalsOptions: ['custom-signals-options'] as const,
// Monitor (监控规则 + 触发记录)
monitorRules: ['monitor-rules'] as const,
monitorRuleOptions: ['monitor-rule-options'] as const,
alerts: (source?: string) => ['alerts', source ?? ''] as const,
// AI 大盘复盘
reviewReports: ['review-reports'] as const,
// 概念涨幅轮动矩阵
rpsRotation: (days: number) => ['rps-rotation', days] as const,
// 市场环境(Regime) — 日级离线计算, 不进 SSE 刷新
regimeHistory: (limit?: number) => ['regime-history', limit ?? 0] as const,
regimeLatest: ['regime-latest'] as const,
regimeStates: (days: number) => ['regime-states', days] as const,
regimeCoverage: ['regime-coverage'] as const,
regimePhases: (start?: string, end?: string) => ['regime-phases', start ?? '', end ?? ''] as const,
regimeMainline: (kind: string, start?: string, end?: string) => ['regime-mainline', kind, start ?? '', end ?? ''] as const,
} as const
// ===== SSE 应该 invalidate 的 key 前缀列表 =====
// 新增需要 SSE 推送的查询,只需在此加一行
//
// 注意: 策略页 (screener-cached) 不在此列表 —— 行情刷新时策略结果不变
// (非监控策略读盘后静态缓存, 监控策略由独立的 strategy_results_updated 事件在
// 重算完成后刷新)。若加入 'screener', 会导致每个行情 tick 双重刷新策略页,
// 且在 monitor "重算" 窗口内读到空结果, 造成策略列表闪烁 (变 0 → 空失效 → 又出现)。
export const SSE_INVALIDATE_PREFIXES = [
// 精确前缀: 只命中自选页的实时数据 (quotes/enriched)。不能用宽泛的 'watchlist' ——
// 会误伤 ['watchlist'] (自选列表) 和 ['watchlist-groups'] (分组配置, 只随手动操作变化)。
// 旧设置里的 'watchlist' 单开关由 useQuoteStream 兼容读取。
'watchlist-quotes',
'watchlist-enriched',
'quote-status',
'index-quotes',
'overview-market',
'limit-ladder',
] as const