Skip to content

数据服务说明文档(服务器端采集 + 客户端同步)

适用版本:Futures.Server.Api(api.bywl.top)+ Futures.Desktop(客户端) 关联文档:server/数据库文档.md(25 张表结构 + JSONB 通用表)、docs/行情数据源接入协议.md

一、系统架构

【服务器端】api.bywl.top(独立 VPS / 容器)
┌──────────────────────────────────────────────────────────┐
│  Futures.Server.Api(C# ASP.NET Core WebAPI, net9.0)     │
│  ├─ TushareCollector      ← 复用 Futures.Core.TushareApiClient(token/代理/重试)│
│  ├─ DataCollectorHostedService ← 每日盘后定时采集          │
│  ├─ ServerDataStore       ← 服务器 PG(srv_* 表 + JSONB)  │
│  └─ DataController        ← GET /data/* REST              │
│  PostgreSQL(futures_srv 库)                              │
└──────────────────────┬───────────────────────────────────┘
                       │ HTTPS(客户端 DataServerUrl)

【客户端】客户机器
┌──────────────────────────────────────────────────────────┐
│  DataSyncService(启动时 + 每 6 小时定时同步)              │
│  ├─ 拉取 JSON → 反序列化为 Futures.Core 模型               │
│  └─ 复用持久化服务写本地 PG(futures 库,原表不变)          │
│  研判页 / 持仓排名 / 仓单 / 合约参数 / 股票(后续)读本地库   │
└──────────────────────────────────────────────────────────┘

设计要点

  1. 服务器是唯一数据持有方(Tushare token / 天勤账号只存在于服务器),客户端零第三方账号依赖;
  2. 客户端本地库是数据仓库——分析页全部读本地 PG,断网可用、查询快;
  3. REST 是管道,字段名与 Tushare 一致,两端共用 Futures.Core 模型,零映射成本。

二、服务器端部署

2.1 前置要求

  • 一台 Linux/Windows 服务器(建议 Linux + Docker 或 systemd)
  • PostgreSQL(建议 14+),建库:CREATE DATABASE futures_srv;
  • Tushare 账号(token,积分 ≥2000 覆盖大部分接口;5000 覆盖全部)

2.2 构建发布

bash
# 在项目根目录(有 Futures.Server.slnx)
dotnet publish server/Futures.Server.Api -c Release -r linux-x64 --self-contained false -o publish/server
# 将 publish/server 上传到服务器,配置守护进程(systemd/supervisor/Docker)

2.3 配置(appsettings.json 或环境变量)

json
{
  "PgHost": "localhost", "PgPort": 5432, "PgDatabase": "futures_srv",
  "PgUser": "postgres", "PgPassword": "******",
  "TushareToken": "服务器持有",
  "TushareProxyEnabled": false, "TushareProxyUrl": "",
  "TushareBypassSsl": false, "TushareUseHostHeader": false
}

环境变量可覆盖:PG_HOST / PG_PORT / PG_DATABASE / PG_USER / PG_PASSWORD / TUSHARE_TOKEN(容器部署友好)。

2.4 启动

  • 首次启动自动建表(EnsureTablesAsync,25 张专用表 + 1 张 JSONB 通用表);
  • 启动后 30 秒自动采集一次(部署当天即有数据);
  • 之后每 5 分钟检查:工作日 15:35 / 17:05 后进入采集窗口执行全部接口。

2.5 鉴权建议

公网部署时在反向代理(nginx)加 X-Api-Key 校验或 IP 白名单;客户端配置 DataServerApiKey 后请求带 ?key=注意/admin 管理界面无内置鉴权,公网部署务必在反向代理加访问控制或仅内网开放。

2.6 管理界面

浏览器访问 http://api.bywl.top/admin 打开数据采集管理页:

  • 显示 26 张表的数据行数(/admin/status);
  • 列出全部专用接口 + 通用接口清单(/admin/interfaces);
  • 支持填日期后逐接口触发采集 / 一键采集全部(POST /collect/*)。

2.7 数据库初始化 SQL

服务器预建库可执行 server/sql/init_database.sql(psql):

bash
psql -U postgres -h localhost -c "CREATE DATABASE futures_srv;"
psql -U postgres -h localhost -d futures_srv -f server/sql/init_database.sql

包含 26 张表 DDL + 日期索引,幂等可重复执行。程序启动时也会自动建表(二选一即可)。

2.8 宝塔(bt.cn)部署注意事项 ⭐

服务器是宝塔面板时,按以下核对,否则会遇到"表数据量不变/看不到数据":

检查项说明
PostgreSQL 是否装/启动宝塔 → 软件商店 → 安装 PostgreSQL(14+)。确认 5432 端口监听:ss -ltn | grep 5432
PG 连接信息宝塔里创建的数据库名/用户名/密码 → 填进 appsettings.jsonPgHost/PgPort/PgDatabase/PgUser/PgPassword(注意宝塔 PG 默认端口可能就是 5432,用户名不一定 postgres)
TushareToken 必须填服务器采集数据靠 Tushare API;token 为空 = 所有采集失败 = 表数据永远不变。填你自己的 token(客户端 appsettings 里的 aa344fe8... 或服务器专用)
API 进程是否常驻/admin 能打开 ≠ 定时任务在跑。确认进程活着:systemctl status futures-serverps aux | grep Futures.Server;日志看采集是否报错
时间窗口采集只在工作日 15:35 后触发。工作日白天看 /admin 表数据不变是正常的
手动触发验证/admin 页面(或 POST /collect/xxx)手动触发一次采集 → 看 /admin/status 行数是否增长 → 若仍 0,查服务器日志的采集错误(多为 token/网络/积分)

2.8.1 宝塔 PG 的连接与查询(三种方式)

方式 A:宝塔 Web 管理(推荐,无需 SSH)

  1. 宝塔面板 → 数据库 → 找到 futures_srv
  2. 若宝塔装了 phpMyAdmin/pgAdmin 插件 → 点「管理」入口进入 Web 控制台
  3. 执行查询,例如:
    sql
    SELECT count(*) FROM srv_fut_daily;
    SELECT max(trade_date) FROM srv_fut_daily;   -- 看最新数据日期

方式 B:服务器 SSH 用 psql

bash
# 方式 B1:以 postgres 系统用户执行
su - postgres -c "psql -d futures_srv -c 'SELECT count(*) FROM srv_fut_daily;'"

# 方式 B2:直接连接(需知道宝塔创建的 PG 密码)
psql -h 127.0.0.1 -p 5432 -U 宝塔用户 -d futures_srv \
  -c "SELECT count(*) FROM srv_fut_daily;"
# 输入密码即可

# 方式 B3:免密(.pgpass)——宝塔 PG 目录通常在 /www/server/pgsql/
echo "127.0.0.1:5432:futures_srv:用户名:密码" > ~/.pgpass && chmod 600 ~/.pgpass
psql -h 127.0.0.1 -d futures_srv -c "SELECT count(*) FROM srv_fut_daily;"

方式 C:通过 API 间接验证(最稳,不碰数据库)

bash
# 直接调数据接口看有无数据返回
curl "https://api.bywl.top/data/fut_daily?start=20260801&end=20260820"
curl "https://api.bywl.top/data/fut_holding?start=20260801&end=20260820"
# 返回 JSON 数组有内容 = 数据库有数据;空数组 = 确实没采到

宝塔 PG 路径参考:二进制 /www/server/pgsql/bin/psql,数据目录 /www/server/pgsql/data/。 常见排查:宝塔 PG 默认只监听 127.0.0.1,若 appsettings.jsonPgHost 写了别的地址会连不上。

2.8.2 手动触发采集验证(确认数据链路通)

方式 A:/admin 管理页(图形界面)

  1. 浏览器打开 https://api.bywl.top/admin
  2. 页面列出全部接口 + 每张表行数(/admin/status
  3. 点某接口的「采集」按钮(或「一键采集全部」)→ 填日期(默认当天)
  4. 等 10-60 秒(Tushare 拉取 + 落库)→ 刷新页面看行数是否增长

方式 B:POST /collect/ 直接触发*

bash
# 各接口采集(返回 {success, rows, message})
curl -X POST "https://api.bywl.top/collect/fut_daily?trade_date=20260820"
curl -X POST "https://api.bywl.top/collect/fut_holding?trade_date=20260820"
curl -X POST "https://api.bywl.top/collect/fut_wsr?trade_date=20260820"
# 一键全部
curl -X POST "https://api.bywl.top/collect/all"

验证判断

  • rows > 0 → 采集成功,数据已落库 → 问题在客户端同步环节
  • rows = 0 且 message 含"无数据/失败" → 看 message 具体错误(token/积分/网络/日期非交易日)
  • 采集后 /admin 行数仍 0 → 查服务器日志(journalctl -u futures-server 或宝塔网站运行日志)里的 Collect* 错误

⚠️ 注意:手动触发不受时间窗口限制(随时可跑);但非交易日拉当日数据返回空是正常的。

三、数据接口总览(46 类)

3.1 期货(13 个,专用表)

REST 端点Tushare 接口积分
/data/fut_basicfut_basic2000srv_fut_basic
/data/fut_trade_calfut_trade_cal2000srv_fut_trade_cal
/data/fut_dailyfut_daily2000srv_fut_daily
/data/fut_holdingfut_holding2000srv_fut_holding
/data/fut_wsrfut_wsr2000srv_fut_wsr
/data/fut_settlefut_settle2000srv_fut_settle
/data/fut_mappingfut_mapping2000srv_fut_mapping
/data/opt_basicopt_basic2000srv_opt_basic
/data/opt_dailyopt_daily2000srv_opt_daily
/data/fut_weekly_monthlyfut_weekly_monthly2000srv_fut_weekly_monthly
/data/fut_index_dailyfut_index_daily2000srv_fut_index_daily
/data/fut_weekly_detailfut_weekly_detail600srv_fut_weekly_detail
/data/ft_limitft_limit5000srv_ft_limit

3.2 股票核心(12 个,专用表)

REST 端点Tushare 接口积分
/data/stock_dailydaily2000srv_stock_daily
/data/stock_adj_factoradj_factor2000srv_stock_adj_factor
/data/stock_daily_basicdaily_basic2000srv_stock_daily_basic
/data/stock_trade_caltrade_cal2000srv_stock_trade_cal
/data/stock_namechangenamechange120srv_stock_namechange
/data/stock_companystock_company120srv_stock_company
/data/stock_new_sharenew_share120srv_stock_new_share
/data/stock_limitstk_limit2000srv_stock_limit
/data/stock_suspendsuspend_d120srv_stock_suspend
/data/stock_moneyflowmoneyflow2000srv_stock_moneyflow
/data/stock_top_listtop_list2000srv_stock_top_list
/data/stock_top_insttop_inst5000srv_stock_top_inst

3.3 通用 JSONB(21 个,srv_api_data 表)

REST 端点覆盖接口
/data/api/财务(10):income/balancesheet/cashflow/forecast/express/dividend/fina_indicator/fina_audit/fina_mainbz/disclosure_date;基础(3):stock_st/hs_const/stk_managers;行情(2):stk_weekly_monthly/hsgt_top10;资金(4):moneyflow_ths/moneyflow_dc/moneyflow_ind_ths/moneyflow_ind_dc;打板(1):limit_list_d;特色(4):ths_index/ths_member/concept/concept_detail

通用接口按行内 trade_date/end_date/ann_date 分组存 JSONB,查询 GET /data/api/{name}?start=&end=。 个别接口名/权限未逐一验证(如 ths_member/concept),采集失败记日志,可在 TushareCollector.GenericApis 清单修正。

四、手动触发采集(运维)

bash
# 单个接口
POST /collect/holding?date=20260815
POST /collect/stock_daily?date=20260815
POST /collect/api/income?date=20260815     # 通用接口

# 全部接口
POST /collect/all?date=20260815

支持类型见 CollectController;采集失败不影响其他接口。

五、客户端配置与使用

5.1 设置页填写指南(系统设置 → 数据服务)

字段填写说明
数据服务地址https://api.bywl.top服务器 API 基址(也可内网直连 http://服务器IP:端口
访问密钥留空 或 服务器约定 key当前服务器端未校验,留空即可;公网建议 nginx 加访问控制

留空 = 不启用数据同步(客户端直连 Tushare/天勤);填写后客户端从服务器拉盘后数据。 首启向导步骤 5 也有同名字段,新客户首次安装时可直接填写。

填写后的行为

  • 客户端启动时 → SyncAllAsync(7天) 立即同步一次
  • 之后每 6 小时自动同步(幂等 UPSERT,重复无副作用)
  • 同步数据:持仓排名 / 仓单 / 结算参数 / 主力映射 / 期货日线 / 期权日线 / IV 快照

5.2 客户端同步机制(7 类数据 + 验证位置)

SyncAllAsync 每次同步 7 类数据(启动时一次 + 每 6 小时定时 + 立即同步):

#数据服务器接口写入本地表验证位置(对应页面)
1持仓排名/data/fut_holdingfut_holding_records持仓龙虎榜、席位趋势、期货研判"席位"因子
2仓单日报/data/fut_wsrfut_wsr仓单分析、期货研判"库存仓单"因子
3结算参数/data/fut_settlecontract_params(保护 manual 行)合约参数页(保证金/手续费)
4主力映射/data/fut_mappingfut_mappings主力合约映射页、各分析"主力合约"解析
5期货日线/data/fut_dailyquotes_daily回测/信号/基差/价差/板块/K线
6期权日线/data/opt_dailyquotes_option_daily期权回测/期权列表/期权研判
7IV 快照本地聚合(非服务器拉取)option_iv_daily波动率百分位(IV 位置)

逐项核对:持仓龙虎榜=08-20 · 仓单分析=08-19 · 合约参数=最近交易日费率 · 主力映射=08-20 · 期货K线最新=08-20 · 期权列表最新=08-20。 股票数据(个股日线/财务/资金流)不走本同步——由客户端股票模块自己的调度任务采集。

5.3 同步状态查看

系统监控页 → 盘后数据同步

  • 未配置 → "未配置数据服务地址(设置页 → 数据服务)"
  • 已同步 → "已同步 X/7 类,共 N 条 | 最近 HH:mm"
  • 可点击「立即同步」手动触发

六、数据库说明

  • 服务器库(futures_srv):25 张专用表(srv_*)+ 1 张通用表(srv_api_data),结构见 server/数据库文档.md
  • 客户端库(futures,原有):表结构与改造前一致,DataSyncService 复用原持久化服务写入;
  • 索引:所有按日期查询的表已建 trade_date 相关索引(DDL 内含),大数据量表(股票日线等)务必保留。

实时行情数据保留策略(2026-08-20)

  • 结论quotes_realtime(期货/外盘 tick)与 quotes_option_realtime(期权 tick)隔天即无分析价值 (分析走 quotes_daily / quotes_option_daily 日线),已配置 7 天自动保留
  • 配置:TimescaleDB add_retention_policy('quotes_realtime', INTERVAL '7 days')(期权表同理), 后台任务每天自动删除 7 天前的 chunk。
  • 首次清理SELECT drop_chunks('quotes_realtime', (now() - interval '7 days')::timestamp); 两表共释放约 1.4 亿行(8453万→2321万 / 9453万→1635万)。
  • 监控页卡顿修复GetDatabaseStatusAsync 对两张实时表改用 7 天窗口 COUNT(原全表 COUNT 扫 8000万+ 行需 ~6s), 9 表加载总耗时从 ~6.1s 降至 ~2s;UI 表格"统计口径"列标注"近7天"。
  • 监控页卡顿根因(第二次,2026-08-20):剩余卡顿来自 GetScriptStatusAsyncWMI 逐进程查询 (每进程一次 ManagementObjectSearcher,单次 ~1s,5 脚本 × 十几个 python 进程 = 5s+)。 修复:① PID 缓存(桌面端启动进程)与启动时间戳匹配提前到最优先(零 WMI);② 剩余脚本合并为 一次 WMI 批量查询SELECT ProcessId, CommandLine FROM Win32_Process WHERE ...)+ 3s 快照缓存。

七、常见问题

Q1:为什么模块管理页显示 0 个模块? A:客户端 UpdateServerUrl 未配置(设置页填 https://u.bywl.top)。模块 dll 需在服务器 modules/ 目录。

Q2:数据同步失败? A:检查 ① DataServerUrl 是否配置正确;② 服务器 API 是否可达(浏览器访问 /data/health);③ ApiKey 是否匹配;④ 服务器 PG/Tushare 配置。

Q2.5:访问 /admin 发现表数据量一直不变? A:优先排查(最常见 3 个原因):

  1. TushareToken 为空 → 采集器拿不到数据,/admin 表永远 0 增长。检查服务器 appsettings.jsonTushareToken
  2. 非工作时间 → 采集只在工作日 15:35 后触发;周末/工作日白天看不变是正常的;
  3. 采集报错 → 看服务器日志(journalctl / nohup 输出)里的 Collect* 错误信息(token 无效/积分不足/网络不通)。 另:若 PG 是宝塔装的,连接信息(库名/用户/密码)要与宝塔里实际创建的一致(见 2.8 节)。

Q3:服务器重启后数据丢失? A:不会。数据持久化在服务器 PG(futures_srv);重复采集 UPSERT 幂等,安全。

Q4:新增一个 Tushare 接口怎么加? A:专用表类接口:加 Collector 方法 + Store 方法 + 表 + 端点;长尾接口:只需在 TushareCollector.GenericApis 清单加一行(JSONB 通用表),零建表成本。

Q5:软著/授权合规? A:客户端不携带任何 Tushare/天勤 token 与采集脚本(期货侧),数据源授权集中在服务器运营方;行情数据源也已是"用户/服务器提供"模式(见 docs/行情数据源接入协议.md)。

Q6:同步数据条数有、但数值全 0/NULL(仓单/龙虎榜空白)? ⭐ A:这是 JsonElement 解析 bug(2026-08 修复):

  • GetRowsAsyncSystem.Text.Json 反序列化到 Dictionary<string, object?>,数字会变成 JsonElement 而非 long/int
  • ParseLong/ParseDecimal 不认识 JsonElement → 返回 null → 数值字段全 0/NULL;
  • 修复ParseLong/ParseDecimal 增加 JsonElement 分支(TryGetInt64/TryGetDecimal)。
  • 表现:数据条数正常(如 fut_wsr 1027 条)但 vol/long_hld 等全 0;仓单分析显示"08-18/08-19 为 0"、持仓龙虎榜空白。

Q7:修复后重新同步,仓单好了但持仓龙虎榜仍空白? ⭐ A:这是 ON CONFLICT DO NOTHING bug(2026-08 修复):

  • 仓单 fut_wsrON CONFLICT DO UPDATE(修复后重新同步覆盖旧 0 值);
  • 持仓 fut_holding_recordsFutHoldingPersistenceService.SaveAsyncON CONFLICT DO NOTHING——主键已存在的旧 NULL 行被跳过,新值写不进 → 龙虎榜仍空白;
  • 修复:改为 ON CONFLICT (trade_date, symbol, broker, exchange) DO UPDATE SET ...(UPSERT 全量覆盖)。
  • 教训:数据同步的 UPSERT 一律用 DO UPDATE,不要用 DO NOTHING(旧脏数据永远无法自愈)。

Q8:IV 快照(option_iv_daily)永远 0 条,即使期权实时采集在跑? ⭐ A:这是 trade_date() 函数 bug(2026-08 修复):

  • OptionIvSnapshotService.SnapshotFromRealtimeAsyncWHERE trade_date(datetime) = @d
  • trade_date()TimescaleDB schema 函数timescaledb.trade_date()),客户端 Npgsql 连接 search_path=public 找不到该函数 → 每次聚合抛异常被 catch 静默 → 快照 0 条;
  • 修复:改用标准写法 WHERE datetime::date = @d
  • 排查SELECT count(*) FROM option_iv_daily 为 0 且实时表有数据时,检查同步日志的 IV 快照聚合失败 warning。

Q9:如何验证 7 类同步是否都正常? A:见 §5.2 表格——系统监控页「立即同步」后逐项核对:持仓龙虎榜=最新交易日、仓单分析=最新日期、期货/期权 K 线=最新日期、合约参数=最近费率、主力映射=最新日期、IV 位置有数据。

瓯衍期货分析系统 · 温州博益网络科技有限公司