+ const fields: { label: string; min: NumKey; max: NumKey; unit: string; step?: string }[] = [
{ label: '现价', min: 'priceMin', max: 'priceMax', unit: '元', step: '0.1' },
{ label: '涨跌幅', min: 'changePctMin', max: 'changePctMax', unit: '%' },
{ label: '5日涨幅', min: 'momentum5dMin', max: 'momentum5dMax', unit: '%' },
@@ -108,12 +140,71 @@ export function FilterPanel({ value, onChange, onClose, onReset }: {
return (
+ {/* 标题栏: 左侧标题 + 激活计数, 右侧重置 + 关闭 */}
-
筛选条件
-
+
+ {/* 板块 + ST 快速筛选 (按钮组) */}
+
+ 市场
+ {BOARDS.map(board => {
+ const active = value.boards.includes(board)
+ return (
+ toggleBoard(board)}
+ className={`px-2 py-0.5 rounded text-[11px] transition-colors ${
+ active
+ ? 'bg-accent/15 text-accent'
+ : 'bg-elevated text-secondary hover:text-foreground hover:bg-elevated/80'
+ }`}
+ >
+ {board}
+
+ )
+ })}
+
+ onChange({ ...value, excludeST: !value.excludeST })}
+ className={`px-2 py-0.5 rounded text-[11px] transition-colors ${
+ value.excludeST
+ ? 'bg-accent/15 text-accent'
+ : 'bg-elevated text-secondary hover:text-foreground hover:bg-elevated/80'
+ }`}
+ >
+ 排除ST
+
{fields.map((f) => {
const isRange = f.min !== f.max
@@ -146,17 +237,7 @@ export function FilterPanel({ value, onChange, onClose, onReset }: {
)
})}
- {filterActive(value) && (
-
-
- 清空全部
-
- 输入即生效 · 支持范围筛选
-
- )}
+
输入即生效 · 点击市场/ST 按钮切换
)
}
diff --git a/frontend/src/components/stock-table/MiniIntraday.tsx b/frontend/src/components/stock-table/MiniIntraday.tsx
new file mode 100644
index 0000000..9849f59
--- /dev/null
+++ b/frontend/src/components/stock-table/MiniIntraday.tsx
@@ -0,0 +1,114 @@
+/** 迷你分时折线图(自选列表共享)。
+
+用当日分钟K的 close 画一条折线 + 昨收水平基准线 + 分时均线。
+风格仿同花顺/东方财富分时图:
+- 价格折线:涨(收盘 ≥ 昨收)红色,跌绿色 — 以昨收价(prevClose)为基准, 不是当日开盘价
+- 昨收基准线:浅灰实线(从开到右),比虚线更明显
+- 分时均线:黄色细线(成交均价,这里用 close 的累计均值近似)
+空数据返回等尺寸占位 SVG,保证加载前后尺寸一致(同 MiniCandlestick 模式)。
+*/
+import type { MinuteKlineRow } from '@/lib/api'
+
+export function MiniIntraday({ rows, prevClose, changePct, width = 100, height = 56 }: {
+ rows: MinuteKlineRow[]
+ /** 昨收价 (前收), 用于基准线。无则用 close/changePct 反算 */
+ prevClose?: number | null
+ /** 涨跌幅 (小数, 如 -0.029 = -2.9%), 用于涨跌着色。优先级最高 */
+ changePct?: number | null
+ width?: number
+ height?: number
+}) {
+ // 空数据:返回等尺寸占位
+ if (!rows || rows.length < 2) {
+ return
+ }
+
+ const BULL = '#C74040'
+ const BEAR = '#2D9B65'
+ const LINE_PREV_CLOSE = '#7A7A85' // 昨收基准线: 深灰实线
+ const LINE_AVG = '#E0B84A' // 均线: 暖黄
+
+ const W = width
+ const H = height
+ const padY = 3
+ const n = rows.length
+
+ // 涨跌着色: 优先用 changePct (后端 enriched 字段, 最可靠);
+ // 其次用 prevClose vs lastClose; 最后回退到第一根 open
+ const lastClose = rows[n - 1].close
+ const firstOpen = rows[0].open
+ const isUp = changePct != null
+ ? changePct >= 0
+ : prevClose != null && prevClose > 0
+ ? lastClose >= prevClose
+ : lastClose >= firstOpen
+ const color = isUp ? BULL : BEAR
+
+ // 昨收基准线: 优先用 prevClose; 其次用 changePct 反算 (close/(1+changePct));
+ // 最后回退到第一根 open
+ const baseline = (prevClose != null && prevClose > 0)
+ ? prevClose
+ : (changePct != null && changePct !== 0)
+ ? lastClose / (1 + changePct)
+ : firstOpen
+
+ // 价格区间: close + 昨收 + 均线 全部纳入, 确保都在可视范围
+ let hi = -Infinity, lo = Infinity
+ // 累计均价 (分时均线的近似: close 的累计平均)
+ const avgLine: number[] = []
+ let cumSum = 0
+ for (let i = 0; i < n; i++) {
+ const c = rows[i].close
+ cumSum += c
+ const avg = cumSum / (i + 1)
+ avgLine.push(avg)
+ if (c > hi) hi = c
+ if (c < lo) lo = c
+ if (avg > hi) hi = avg
+ if (avg < lo) lo = avg
+ }
+ // 把昨收也纳入区间
+ hi = Math.max(hi, baseline)
+ lo = Math.min(lo, baseline)
+ const range = hi - lo || 1
+
+ const yScale = (v: number) => padY + (1 - (v - lo) / range) * (H - padY * 2)
+ const xScale = (i: number) => (i / (n - 1)) * W
+
+ // 价格折线 points
+ const pricePoints = rows.map((r, i) => `${xScale(i).toFixed(1)},${yScale(r.close).toFixed(1)}`).join(' ')
+ // 均线 points
+ const avgPoints = avgLine.map((v, i) => `${xScale(i).toFixed(1)},${yScale(v).toFixed(1)}`).join(' ')
+
+ // 昨收参考线 y 坐标
+ const prevCloseY = yScale(baseline)
+
+ return (
+
+ )
+}
diff --git a/frontend/src/components/stock-table/StockDataTable.tsx b/frontend/src/components/stock-table/StockDataTable.tsx
index f0317e5..2cc1649 100644
--- a/frontend/src/components/stock-table/StockDataTable.tsx
+++ b/frontend/src/components/stock-table/StockDataTable.tsx
@@ -5,7 +5,7 @@
* 不内置任何业务逻辑:单元格内容(含 symbol 列交互、操作列、ext 列)由调用方通过
* renderCell / renderExtraCol 注入。这样两个页面的特有交互得以保留,同时表头能力一致。
*/
-import type { ReactNode } from 'react'
+import { cloneElement, isValidElement, type ReactElement, type ReactNode } from 'react'
import type { ColumnConfig } from '@/lib/list-columns'
import { UNSORTABLE_KEYS } from '@/lib/stock-table'
import type { SortState } from './useTableSort'
@@ -108,7 +108,13 @@ export function StockDataTable({
key={rowKey(r)}
className={`transition-colors duration-150 ease-smooth group ${rowClassName(r)}`}
>
- {visibleColumns.map(col => renderCell(r, col))}
+ {visibleColumns.map(col => {
+ // renderCell 返回的 无 key, 这里补上避免 React key 警告
+ const cell = renderCell(r, col)
+ return isValidElement(cell)
+ ? cloneElement(cell as ReactElement, { key: col.id })
+ : cell
+ })}
{renderExtraCol && renderExtraCol(r)}
)
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index 15e8b5c..1f51a60 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -781,9 +781,8 @@ export interface Preferences {
nav_order: string[]
nav_hidden: string[]
screener_auto_run: boolean
+ minute_intraday_refresh: boolean
}
-
-// ===== Strategy Alert =====
export interface StrategyAlertEvent {
source: 'strategy' | 'depth'
type: string
@@ -937,6 +936,7 @@ export const api = {
strategy_monitor_ids?: string[]
sidebar_index_symbols?: string[]
screener_auto_run?: boolean
+ minute_intraday_refresh?: boolean
}) =>
request<{
sse_refresh_pages: Record
@@ -944,6 +944,7 @@ export const api = {
strategy_monitor_ids: string[]
sidebar_index_symbols: string[]
screener_auto_run: boolean
+ minute_intraday_refresh: boolean
}>('/api/settings/preferences/realtime-monitor', {
method: 'PUT',
body: JSON.stringify(cfg),
@@ -1064,6 +1065,11 @@ export const api = {
method: 'POST',
body: JSON.stringify({ symbols, days }),
}),
+ klineMinuteBatch: (symbols: string[], date?: string) =>
+ request<{ data: Record }>('/api/kline/minute-batch', {
+ method: 'POST',
+ body: JSON.stringify({ symbols, date }),
+ }),
instrumentSearch: (q: string, limit = 20) =>
request<{ results: { symbol: string; name: string; code: string }[] }>(
`/api/kline/instruments/search?q=${encodeURIComponent(q)}&limit=${limit}`,
diff --git a/frontend/src/lib/list-columns.ts b/frontend/src/lib/list-columns.ts
index 20db1b3..97cc9d0 100644
--- a/frontend/src/lib/list-columns.ts
+++ b/frontend/src/lib/list-columns.ts
@@ -49,6 +49,34 @@ export const DEFAULT_CANDLE_CONFIG: Required = {
days: 12,
}
+/** 分时列渲染配置(builtin: intraday 列专用) */
+export interface IntradayColumnConfig {
+ /** 单元格宽度 px */
+ width?: number
+ /** 单元格高度 px */
+ height?: number
+}
+
+/** 分时列配置默认值 */
+export const DEFAULT_INTRADAY_CONFIG: Required = {
+ width: 150,
+ height: 80,
+}
+
+/** 分时列数值边界 */
+const INTRADAY_BOUNDS = {
+ width: { min: 60, max: 300 },
+ height: { min: 32, max: 200 },
+} as const
+
+export function resolveIntradayConfig(cfg: IntradayColumnConfig | undefined): Required {
+ const c = cfg ?? {}
+ return {
+ width: clampNum(c.width, INTRADAY_BOUNDS.width, DEFAULT_INTRADAY_CONFIG.width),
+ height: clampNum(c.height, INTRADAY_BOUNDS.height, DEFAULT_INTRADAY_CONFIG.height),
+ }
+}
+
/** 数值边界(设置过大取上限,过小取最小值) */
const CANDLE_BOUNDS = {
enabledWidth: { min: 40, max: 300 },
@@ -89,6 +117,8 @@ export interface ColumnConfig {
extDisplay?: ExtColumnDisplayConfig
/** 日k列渲染配置(仅 builtin: candle 列生效) */
candleConfig?: CandleColumnConfig
+ /** 分时列渲染配置(仅 builtin: intraday 列生效) */
+ intradayConfig?: IntradayColumnConfig
/** 信息条场景:是否单独占一行显示(仅 StockInfoBar 生效,表格场景忽略) */
standalone?: boolean
}
@@ -135,11 +165,12 @@ export function mergeColumns(
const def = defaultMap.get(col.id)
if (def) {
// 内置列: label/source/align/pinned 以默认定义为准;visible 使用用户配置;
- // 用户自定义的渲染配置(如日k的 candleConfig、策略列的 extDisplay、信息条 standalone)需保留,否则刷新后丢失
+ // 用户自定义的渲染配置(如日k的 candleConfig、分时的 intradayConfig、策略列的 extDisplay、信息条 standalone)需保留,否则刷新后丢失
result.push({
...def,
visible: col.visible,
...(col.candleConfig ? { candleConfig: col.candleConfig } : {}),
+ ...(col.intradayConfig ? { intradayConfig: col.intradayConfig } : {}),
...(col.extDisplay ? { extDisplay: col.extDisplay } : {}),
...(col.standalone ? { standalone: col.standalone } : {}),
})
diff --git a/frontend/src/lib/queryKeys.ts b/frontend/src/lib/queryKeys.ts
index 4dd6fdc..6766ef6 100644
--- a/frontend/src/lib/queryKeys.ts
+++ b/frontend/src/lib/queryKeys.ts
@@ -26,6 +26,9 @@ export const QK = {
watchlistQuotes: ['watchlist-quotes'] as const,
watchlistEnriched: (ext?: string) => ['watchlist-enriched', ext] as const,
watchlistKlineBatch: (symbols: string) => ['watchlist-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) => ['instrument-search', q] as const,
// Screener
diff --git a/frontend/src/lib/stock-table.ts b/frontend/src/lib/stock-table.ts
index 2715f5b..50dfdc7 100644
--- a/frontend/src/lib/stock-table.ts
+++ b/frontend/src/lib/stock-table.ts
@@ -48,7 +48,7 @@ export function signalCls(type: SignalType): string {
// ===== 排序 =====
/** 不可参与数值/文本排序的内置列 key(渲染为标签/图表,无单一标量值) */
-export const UNSORTABLE_KEYS = new Set(['signals', 'candle', 'strategies'])
+export const UNSORTABLE_KEYS = new Set(['signals', 'candle', 'intraday', 'strategies'])
/**
* 取一列在某行上的排序标量值。builtin 列按 key 映射到行字段;ext 列走
diff --git a/frontend/src/lib/storage.ts b/frontend/src/lib/storage.ts
index b9e7295..593caaa 100644
--- a/frontend/src/lib/storage.ts
+++ b/frontend/src/lib/storage.ts
@@ -42,6 +42,9 @@ export const storage = {
/** 自选列表日K蜡烛图显示状态 */
watchlistCandle: kv('watchlist_showCandle'),
+ /** 自选列表分时图显示状态 */
+ watchlistIntraday: kv('watchlist_showIntraday'),
+
/** 策略结果列表日K蜡烛图显示状态 */
screenerCandle: kv('screener_showCandle'),
diff --git a/frontend/src/lib/watchlist-columns.ts b/frontend/src/lib/watchlist-columns.ts
index 3c69cc4..5e01ebf 100644
--- a/frontend/src/lib/watchlist-columns.ts
+++ b/frontend/src/lib/watchlist-columns.ts
@@ -71,6 +71,7 @@ export const BUILTIN_COLUMNS: ColumnConfig[] = [
// 信号 & 图表
{ id: 'builtin:signals', source: { type: 'builtin', key: 'signals' }, label: '信号', visible: true, align: 'center' },
{ id: 'builtin:candle', source: { type: 'builtin', key: 'candle' }, label: '日k', visible: false, align: 'center' },
+ { id: 'builtin:intraday', source: { type: 'builtin', key: 'intraday' }, label: '分时', visible: false, align: 'center' },
// 财务指标 (需 Expert 套餐 financial capability, 列默认隐藏)
{ id: 'builtin:eps', source: { type: 'builtin', key: 'eps' }, label: 'EPS', visible: false, align: 'center' },
{ id: 'builtin:bps', source: { type: 'builtin', key: 'bps' }, label: 'BPS', visible: false, align: 'center' },
@@ -92,7 +93,7 @@ export const COLUMN_GROUPS: ColumnGroup[] = [
{ id: 'tech', label: '技术指标', icon: '🔬', keys: ['rsi6', 'rsi14', 'rsi24', 'macd_dif', 'macd_dea', 'macd_hist', 'kdj_k', 'kdj_d', 'kdj_j', 'boll_upper', 'boll_lower', 'atr14', 'vol_ma5', 'vol_ma10'] },
{ id: 'momentum', label: '动量', icon: '🚀', keys: ['momentum_5d', 'momentum_10d', 'momentum_20d', 'momentum_30d', 'momentum_60d'] },
{ id: 'limit', label: '连板', icon: '🔥', keys: ['limit_ups', 'limit_downs'] },
- { id: 'signal', label: '信号', icon: '📡', keys: ['signals', 'candle'] },
+ { id: 'signal', label: '信号', icon: '📡', keys: ['signals', 'candle', 'intraday'] },
{ id: 'finance', label: '财务', icon: '📋', keys: ['eps', 'bps', 'roe', 'pe_ttm', 'pb', 'gross_margin', 'net_margin', 'revenue_yoy', 'net_income_yoy', 'debt_ratio'] },
]
diff --git a/frontend/src/pages/Screener.tsx b/frontend/src/pages/Screener.tsx
index 3db438b..1bb2be5 100644
--- a/frontend/src/pages/Screener.tsx
+++ b/frontend/src/pages/Screener.tsx
@@ -1,7 +1,7 @@
import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { motion } from 'framer-motion'
-import { ScanSearch, Clock, TrendingUp, Star, Filter, Layers, Network, Sparkles, RefreshCw, Settings2, Store } from 'lucide-react'
+import { ScanSearch, Clock, TrendingUp, Star, Filter, Layers, Network, Sparkles, RefreshCw, Settings2, Store, RotateCcw } from 'lucide-react'
import { api, genRuleId, type ScreenerStrategy, type ScreenerResult } from '@/lib/api'
import { toast } from '@/components/Toast'
import { useDataStatus, usePreferences } from '@/lib/useSharedQueries'
@@ -669,37 +669,43 @@ export function Screener() {
)}
- {displayRows.length > 0 && (
- <>
+ {(showAll ? allRows.length > 0 : !!result?.rows.length) && (
+
setShowFilter(v => !v)}
- className={`inline-flex items-center gap-1.5 h-7 px-2.5 rounded-btn
- border text-xs font-medium transition-colors duration-150 cursor-pointer
+ className={`inline-flex items-center gap-1.5 px-2.5 text-xs font-medium transition-colors duration-150 cursor-pointer
${filterActive(filter)
- ? 'border-accent/50 bg-accent/10 text-accent'
- : 'border-border bg-surface text-secondary hover:border-accent/50'
+ ? 'bg-accent/15 text-accent'
+ : showFilter
+ ? 'bg-accent/8 text-accent'
+ : 'text-secondary hover:bg-elevated hover:text-foreground'
}`}
>
筛选
{filterActive(filter) && (
-
+
{countActiveFilters(filter)}
)}
{filterActive(filter) && (
- {
- setFilter(defaultFilter)
- if (activeStrategy) filterMap.current.delete(activeStrategy)
- }}
- className="text-xs text-muted hover:text-danger transition-colors"
- >
- 重置
-
+ <>
+
+ {
+ setFilter(defaultFilter)
+ if (activeStrategy) filterMap.current.delete(activeStrategy)
+ }}
+ title="清空筛选条件"
+ className="inline-flex items-center gap-1 px-2 text-muted
+ hover:bg-danger/10 hover:text-danger transition-colors duration-150 cursor-pointer"
+ >
+
+
+ >
)}
- >
+
)}
{displayRows.length > 0 && (
+ {/* 筛选面板: 只要原始结果有数据就显示 (哪怕筛完后为空, 用户才能改条件) */}
+ {showFilter && (showAll ? allRows.length > 0 : !!result?.rows.length) && (
+ setShowFilter(false)}
+ onReset={() => {
+ setFilter(defaultFilter)
+ if (activeStrategy) filterMap.current.delete(activeStrategy)
+ }}
+ />
+ )}
+
{displayRows.length === 0 ? (
) : (
<>
- {showFilter && (
- setShowFilter(false)}
- onReset={() => {
- setFilter(defaultFilter)
- if (activeStrategy) filterMap.current.delete(activeStrategy)
- }}
- />
- )}
-
{
return storage.watchlistCandle.get(true)
})
+ const [intradayChartVisible, setIntradayChartVisible] = useState(() => {
+ return storage.watchlistIntraday.get(true)
+ })
// 列配置 — 从后端/localStorage 异步加载
const [columns, setColumns] = useState([...BUILTIN_COLUMNS])
@@ -527,6 +531,18 @@ export function Watchlist() {
const dailyKVisible = candleColumnEnabled && dailyKChartVisible
+ // 分时列检测: 用户开启且列可见时才拉数据
+ const intradayColumn = useMemo(() =>
+ columns.find(c => c.source.type === 'builtin' && c.source.key === 'intraday' && c.visible),
+ [columns],
+ )
+ // 分时列渲染配置(宽高, 来自列定制, 已钳制边界)
+ const intradayResolved = useMemo(() => resolveIntradayConfig(intradayColumn?.intradayConfig), [intradayColumn])
+ // 分时图需 Pro+ (kline.minute.batch), 低档用户开了列也不拉数据
+ const caps = useCapabilities()
+ const hasMinuteBatch = !!caps.data?.capabilities?.['kline.minute.batch']
+ const intradayVisible = !!intradayColumn && hasMinuteBatch && intradayChartVisible
+
// 计算可见列(列是否出现只由自定义列配置决定)
const visibleColumns = useMemo(() => {
return columns.filter(c => c.visible)
@@ -549,6 +565,13 @@ export function Watchlist() {
return next
})
}, [])
+ const toggleIntradayChart = useCallback(() => {
+ setIntradayChartVisible(v => {
+ const next = !v
+ storage.watchlistIntraday.set(next)
+ return next
+ })
+ }, [])
const [previewSymbol, setPreviewSymbol] = useState(null)
const [previewName, setPreviewName] = useState('')
const [expandedCells, setExpandedCells] = useState>(new Set())
@@ -591,6 +614,20 @@ export function Watchlist() {
const klineData = dailyKVisible ? (klineBatch.data?.data ?? {}) : {}
+ // 批量分时数据 (Pro+ 用户, 列可见时才拉)
+ // 刷新策略: 默认关闭(不轮询); 用户在实时监控设置里开启 minute_intraday_refresh 后,
+ // 盘中按 15s 轮询刷新 (不接 SSE 高频, 避免每秒拉 TickFlow 触限流)
+ const { data: prefsData } = usePreferences()
+ const intradayRefreshEnabled = prefsData?.minute_intraday_refresh ?? false
+ const minuteBatch = useQuery({
+ queryKey: QK.minuteBatch(symbolsKey),
+ queryFn: () => api.klineMinuteBatch(symbols),
+ enabled: intradayVisible && symbols.length > 0,
+ staleTime: 10_000,
+ refetchInterval: intradayRefreshEnabled ? 15_000 : false,
+ })
+ const minuteData = intradayVisible ? (minuteBatch.data?.data ?? {}) : {}
+
const addMutation = useMutation({
mutationFn: (sym: string) => api.watchlistAdd(sym),
onSuccess: (data) => {
@@ -973,6 +1010,26 @@ export function Watchlist() {
)
}
+ if (col.source.type === 'builtin' && col.source.key === 'intraday') {
+ return (
+
+ {col.label}
+ { event.stopPropagation(); toggleIntradayChart() }}
+ className={`inline-flex items-center justify-center w-5 h-5 rounded transition-colors ${
+ intradayChartVisible
+ ? 'text-accent bg-accent/10 hover:bg-accent/20'
+ : 'text-muted hover:text-foreground hover:bg-elevated'
+ }`}
+ title={intradayChartVisible ? '隐藏分时图' : '显示分时图'}
+ aria-label={intradayChartVisible ? '隐藏分时图' : '显示分时图'}
+ >
+ {intradayChartVisible ? : }
+
+
+ )
+ }
return undefined
}}
renderCell={(r: any, col: ColumnConfig) => {
@@ -1091,13 +1148,29 @@ export function Watchlist() {
if (key === 'candle') {
return (
|
)
}
+ // 分时列
+ if (key === 'intraday') {
+ const rows: MinuteKlineRow[] = minuteData[r.symbol] ?? []
+ // 眼睛关闭(收起)时用小尺寸 (和日k收起态一致 40x40); 开启时用配置值
+ const iw = intradayChartVisible ? intradayResolved.width : 40
+ const ih = intradayChartVisible ? intradayResolved.height : 40
+ return (
+
+
+ {intradayChartVisible
+ ?
+ : 分时}
+
+ |
+ )
+ }
// 其余纯数据列 → 共享原语
return renderBuiltinDataCell(r, col)
}}
diff --git a/frontend/src/pages/settings/Monitoring.tsx b/frontend/src/pages/settings/Monitoring.tsx
index 339795f..628d3d9 100644
--- a/frontend/src/pages/settings/Monitoring.tsx
+++ b/frontend/src/pages/settings/Monitoring.tsx
@@ -302,6 +302,16 @@ export function SettingsMonitoringPanel({ highlight }: { highlight?: string } =
)}
+ {/* 自选列表分时图实时刷新 (默认关闭, 开启后盘中 15s 轮询刷新分时数据) */}
+
+ save({ minute_intraday_refresh: v })}
+ />
+
+
{!isFreeTier && (
|