+
setStep(1)} />}
{step === 1 && setStep(2)} onSkip={finish} />}
{step === 2 && (
- setStep(3)} onSkip={() => setStep(3)} onBack={() => setStep(1)} />
+ setStep(3)} onBack={() => setStep(1)} />
)}
{step === 3 && setStep(4)} onBack={() => setStep(2)} />}
{step === 4 && setStep(3)} pending={complete.isPending} />}
@@ -185,23 +183,14 @@ function DisclaimerStep({ onNext }: { onNext: () => void }) {
- 本项目为个人开源项目 ,非
-
- TickFlow
-
-
- 官方项目 。本项目独立维护,当前支持 TickFlow 数据服务。
+ 本项目为个人开源项目 ,由个人独立维护,与任何商业数据服务
+ 无官方关联 。数据能力依赖第三方数据服务提供。
仅供学习研究使用,不构成任何投资建议。股市有风险,使用本项目产生的任何盈亏由使用者自行承担。
- 本项目基于 MIT 协议开源。使用本项目时,请遵守数据源(TickFlow 等)的服务条款;stock-sdk 等第三方接口插件存在版权与反爬风险,使用需自行评估合规责任。
+ 本项目基于 MIT 协议开源。使用本项目时,请遵守所用数据源的服务条款;第三方接口插件存在版权与反爬风险,使用需自行评估合规责任。
@@ -237,7 +226,7 @@ function WelcomeStep({ onNext, onSkip }: { onNext: () => void; onSkip: () => voi
- 欢迎使用 Tick Stock Panel
+ 欢迎使用 TSP
一个本地化的 A 股量化分析面板 —— 行情、选股、回测、监控、财务一体化。
@@ -285,167 +274,220 @@ function WelcomeStep({ onNext, onSkip }: { onNext: () => void; onSkip: () => voi
)
}
-// ===== Step 2: 输入 TickFlow Key =====
+// ===== Step 2: 配置第三方数据源 (默认内置源; 可添加自有数据源) =====
-function KeyStep({ onNext, onSkip, onBack }: { onNext: () => void; onSkip: () => void; onBack: () => void }) {
+/** datasets 标签的中文名 (与设置页数据集口径一致) */
+const DATASET_LABELS: Record = {
+ daily: '日K',
+ adj_factor: '除权',
+ realtime: '实时',
+ minute: '分钟K',
+ financial: '财务',
+}
+
+function DataSourceStep({ onNext, onBack }: { onNext: () => void; onBack: () => void }) {
const qc = useQueryClient()
- const settings = useSettings()
-
- const [keyInput, setKeyInput] = useState('')
- const [revealing, setRevealing] = useState(false)
- const [saved, setSaved] = useState(false)
-
- const save = useMutation({
- mutationFn: () => api.saveTickflowKey(keyInput.trim()),
- onSuccess: (data) => {
- qc.invalidateQueries({ queryKey: QK.settings })
- qc.invalidateQueries({ queryKey: QK.capabilities })
- if (data.ok) {
- // 仅当 key 有效(被存储)时才进入下一步看探测结果
- setSaved(true)
- setTimeout(() => onNext(), 600)
- }
- // ok=false(key 无效):不进入下一步,错误提示由 save.error / save.data 渲染
- },
+ const prefs = usePreferences()
+ const sources = useQuery({
+ queryKey: QK.dataSources,
+ queryFn: api.dataSources,
+ staleTime: 60_000,
})
- // 已配置 key —— 免费档或付费档都算(只要不是 None 档)
- const alreadyHasKey = settings.data?.mode !== 'none' && settings.data?.mode !== undefined
+ // null = 跟随后端当前激活源 (首次使用即默认内置源)
+ const [picked, setPicked] = useState(null)
+ // 是否展开「添加自有数据源」编辑器
+ const [adding, setAdding] = useState(false)
+
+ const builtin = sources.data?.builtin ?? []
+ const plugins = sources.data?.plugins ?? []
+ const custom = sources.data?.custom ?? []
+ const items = [
+ ...builtin.map(s => ({ ...s, kind: 'builtin' as const })),
+ ...plugins.map(s => ({ ...s, kind: 'plugin' as const })),
+ ...custom.map(s => ({ ...s, kind: 'custom' as const })),
+ ]
+ const byName = new Map(items.map(s => [s.name, s]))
+
+ const activeName = prefs.data?.daily_data_provider || 'tickflow'
+ const selected = picked ?? activeName
+
+ // 切换数据源 —— 与设置页同一套偏好接口:
+ // 内置源 5 个数据集全量切换; 其他源仅切换其声明支持的数据集, 其余回落内置源
+ const switchProvider = useMutation({
+ mutationFn: (name: string) => {
+ if (name === 'tickflow') {
+ return api.updateDataProviders({
+ daily_data_provider: 'tickflow',
+ adj_factor_provider: 'same_as_daily',
+ realtime_data_provider: 'tickflow',
+ minute_data_provider: 'tickflow',
+ financial_data_provider: 'tickflow',
+ })
+ }
+ const supported = new Set(byName.get(name)?.datasets ?? [])
+ const pick = (dataset: string) => (supported.has(dataset) ? name : 'tickflow')
+ return api.updateDataProviders({
+ daily_data_provider: pick('daily'),
+ adj_factor_provider: 'same_as_daily',
+ realtime_data_provider: pick('realtime'),
+ minute_data_provider: pick('minute'),
+ financial_data_provider: pick('financial'),
+ })
+ },
+ onSuccess: () => qc.invalidateQueries({ queryKey: QK.preferences }),
+ })
+
+ const choose = (name: string, available: boolean) => {
+ if (!available || name === selected || switchProvider.isPending) return
+ setAdding(false)
+ setPicked(name)
+ switchProvider.mutate(name)
+ }
return (
-
+
-
配置 TickFlow API Key
+
配置数据源
- 本项目基于 TickFlow 这款稳定的数据源为基座进行开发,正在适配其他第三方数据源。
- 如果有任何建议或意见,欢迎发送邮件至{' '}
-
- 415333856@qq.com
-
- 。
+ 所有数据源均为第三方服务,按需选择或添加自有接口;随时可在
+ 设置 → 数据源
+ 中调整。
- {/* 档位对比说明 —— None 档 vs Free 档 */}
-
- {/* None 档 —— 不配置时默认 */}
-
-
- None
- 不配置(默认)
-
-
- · 仅历史日K数据,无实时行情
- · 数据有延迟,盘后约 1-2 小时更新当天
- · 可用于策略回测、盘后分析
-
+ {/* 数据源卡片选择器 */}
+ {sources.isLoading ? (
+
+
+ 正在加载数据源…
- {/* Free 档 —— 免费注册即可获取 */}
-
-
- Free
- 注册免费获取
- 推荐
-
-
- · 无需付费,注册即享
- · 历史日K + 限定范围内的实时数据
- · 可指定个股进行实时监控
-
-
-
+ ) : (
+
+ {items.map(item => {
+ const isSelected = selected === item.name
+ const unavailable = item.kind === 'plugin' && !item.available
+ const plugin = item.kind === 'plugin' ? plugins.find(p => p.name === item.name) : undefined
+ return (
+
choose(item.name, !unavailable)}
+ disabled={unavailable || switchProvider.isPending}
+ title={unavailable ? plugin?.status || '依赖未安装' : item.display_name}
+ className={`relative text-left rounded-card border px-3.5 py-3 transition-all ${
+ unavailable
+ ? 'border-border/40 bg-elevated/10 opacity-60 cursor-not-allowed'
+ : isSelected
+ ? 'border-accent/50 bg-accent/[0.06] ring-1 ring-accent/20 cursor-pointer'
+ : 'border-border/60 bg-elevated/20 hover:bg-elevated/40 cursor-pointer'
+ }`}
+ >
+
+
+
+ {/* 卡片展示去掉声明里的括号备注 (如合规提示), 保持名称干净 */}
+ {item.display_name.replace(/(.*?)|\(.*?\)/g, '').trim() || item.display_name}
+
+ {item.kind === 'custom' ? (
+ 自有
+ ) : (
+ 第三方
+ )}
+
+
+ {unavailable ? (
+ 需安装依赖,见 设置 → 数据源
+ ) : item.datasets.length > 0 ? (
+ item.datasets.slice(0, 4).map(ds => (
+
+ {DATASET_LABELS[ds] ?? ds}
+
+ ))
+ ) : (
+ 未声明数据集
+ )}
+
+
+ )
+ })}
- {/* Key 已配置提示 */}
- {alreadyHasKey && !save.isPending && (
-
-
-
- 已检测到配置好的 Key({settings.data?.tickflow_api_key_masked} )。
- 可直接下一步查看能力,或在下方粘贴新 Key 替换。
-
+ {/* 添加自有数据源卡片 */}
+
setAdding(v => !v)}
+ className={`rounded-card border border-dashed px-3.5 py-3 transition-all flex items-center justify-center gap-1.5 text-sm ${
+ adding
+ ? 'border-accent/50 bg-accent/5 text-accent'
+ : 'border-border/50 text-muted hover:text-foreground hover:border-border hover:bg-elevated/30'
+ }`}
+ >
+
+ 添加自有数据源
+
)}
- {/* 获取 Key 的说明 —— 黄框卡片 */}
-
-
-
- Key 可在{' '}
-
- tickflow.org
-
-
- 获取。
-
- 当前数据源基于 TickFlow 基座,其他第三方数据源正在开发适配中。
-
-
+ {/* 插件化提示: 标识数据源体系已插件化, 并给出两条接入路径与文档指引 */}
+
+
+
+ 数据源已插件化
+ ,接入自有行情有两条路径:用 YAML 描述自有 HTTP 接口,放入
+ data/data_sources/*.yaml
+ (也可用上方表单配置);或开发插件源,放入
+ backend/app/plugins/
+ 目录。接入方法与字段映射详见
+ docs/custom-data-source.md
+ 与
+ docs/plugin-development.md
+ 。
+
- {/* 输入 */}
-