← 返回持仓页面

HTZQ 海通证券持仓系统 — 完整技术文档
最后更新: 2026-06-04 · www.wheelfly.cn/htzq

目录

一、系统架构总览 二、本地数据源 三、实时行情源 四、数据上传流程 五、PostgreSQL 数据库 六、Web 前端 七、费用计算逻辑 八、已知 Bug 及修复记录 九、服务器部署 十、运行依赖

一、系统架构总览

┌─────────────────────────────────────────────────────────────────────┐
│  本地 Windows (D:\code_p16v\03_stock_gtht\)                         │
│  ┌─────────────┐  ┌──────────────┐  ┌──────────────────────┐        │
│  │ fytrade.db  │  │ stkinfo.txt  │  │ TdxW.exe 进程内存    │        │
│  │ (SQLite)    │  │ (GBK编码)    │  │ (minidump 提取)      │        │
│  └──────┬──────┘  └──────┬───────┘  └──────────┬───────────┘        │
│         │                │                     │                    │
│  ┌──────┴────────────────┴─────────────────────┴───────────┐        │
│  │               htzq.py (Python 3.10)                      │        │
│  │  · PositionReader    → 解析持仓二进制                     │        │
│  │  · PriceFetcher      → 新浪实时行情                       │        │
│  │  · AccountBalanceCache → 总资产/现金余额                  │        │
│  │  · ChangeDetector    → 对比变化检测                       │        │
│  │  · CostCalculator    → 交易成本计算                       │        │
│  │  · DatabaseUploader  → PostgreSQL 上传                    │        │
│  │  · HTZQApp           → Tkinter UI                         │        │
│  └──────────────────────┬───────────────────────────────────┘        │
│                         │ psycopg2 (SSL)                             │
└─────────────────────────┼───────────────────────────────────────────┘
                          │
          ┌───────────────┴────────────────┐
          │  腾讯云 CVM 150.158.87.41      │
          │  ┌──────────────────────────┐  │
          │  │  PostgreSQL 16 (appdb)   │  │
          │  │  · ht_position            │  │
          │  │  · ht_analysis_log    │  │
          │  │  · ht_mv_history      │  │
          │  └──────────┬───────────────┘  │
          │             │                  │
          │  ┌──────────┴───────────────┐  │
          │  │  Flask :5001             │  │
          │  │  api_server.py           │  │
          │  │  · /api/htzq             │  │
          │  │  · /api/htzq/analysis    │  │
          │  │  · /api/htzq/costs       │  │
          │  │  · /api/quote            │  │
          │  │  · /api/intraday         │  │
          │  └──────────┬───────────────┘  │
          │             │                  │
          │  ┌──────────┴───────────────┐  │
          │  │  Nginx :443              │  │
          │  │  · /htzq/ → index.html   │  │
          │  │  · /api/* → Flask proxy  │  │
          │  └──────────────────────────┘  │
          └────────────────────────────────┘
                          │
              ┌───────────┴───────────┐
              │   外部行情 API         │
              │   · 新浪 hq.sinajs.cn │
              │   · 腾讯 qt.gtimg.cn  │
              └───────────────────────┘

二、本地数据源

2.1 核心文件路径

文件绝对路径说明
主程序D:\code_p16v\03_stock_gtht\HTZQ\htzq.py提取+上传+UI
启动器D:\code_p16v\03_stock_gtht\HTZQ\htzq.batWindows 双击启动(GBK 编码)
交易数据库D:\code_p16v\03_stock_gtht\newVer\RichEZ\Bin\cache\fytrade.db富易客户端 SQLite
股票名称D:\code_p16v\03_stock_gtht\newVer\RichEZ\Bin\data\stkinfo.txtGBK 编码,分号分隔
账户信息D:\code_p16v\03_stock_gtht\newVer\RichEZ\Bin\wt\ff_myyyb.iniBase64 编码
现金缓存D:\code_p16v\03_stock_gtht\HTZQ\htzq_cash_cache.json总资产/现金本地持久化
前端页面D:\code_p16v\03_stock_gtht\HTZQ\index.html部署到服务器 /www/wwwroot/wheelfly.cn/htzq/

2.2 fytrade.db 表结构

表名用途编码
today_position_<客户号>_ENC当前持仓(base64 + 二进制)BASE64
history_trade_<客户号>_ENC历史成交(77+ 笔)BASE64
today_order_<客户号>_ENC当日委托BASE64

2.3 持仓二进制解析(29 字节)

today_position_*_ENCdata 字段 base64 解码后:

Offset  Size  Type       字段              示例
──────────────────────────────────────────────────
0       4     uint32 LE  会话ID            507691
4       4     uint32 LE  股票代码(整型)     767 → "000767"
8       1     byte       市场码(ASCII)      '2'=深圳, '1'=上海
9       2     uint16 LE  当前持仓(股)       1,200
11      1     byte       分隔符            0x00
12      1     byte       分隔符            0x00
13      2     uint16 LE  可用数量(股)       1,200
15      1     byte       分隔符            0x00
16      1     byte       分隔符            0x00
17      2     uint16 LE  冻结数量(股)       1,200
19      1     byte       分隔符            0x00
20      1     byte       标识              0x00
21      8     double LE  缓存价格(过时!)    4.8183
──────────────────────────────────────────────────
29 字节总计
⚠️ offset 21 的价格是客户端缓存价,不可用作成交价。实际成交价从 history_trade_*_ENC 表读取。

2.4 历史成交二进制解析(48+ 字节)

history_trade_*_ENCdata 字段 base64 解码后:

Offset  Size  Type       字段              说明
──────────────────────────────────────────────────
0       4     uint32 LE  date_start        起始日期
4       4     uint32 LE  date_end          结束日期
8       4     uint32 LE  date_trade        成交日期 (YYYYMMDD)
12      4     uint32 LE  field_12          未知
16      4     uint32 LE  field_16          未知
20      4     uint32 LE  field_20          未知
24      4     uint32 LE  stock_code        股票代码(整型)
28      3     ascii      market_marker     "10"=SH, "20"/"30"=SZ
31      8     double LE  price             ★ 实际成交价 ★
39      4     uint32 LE  qty               成交数量
43      4     uint32 LE  qty2              未知数量
47      8     double LE  price2            未知价格
price (offset 31) 是券商记录的实际成交价,唯一可靠的价格来源

2.5 总资产/现金余额获取

fytrade.db 和所有本地文件中不含总资产和可用资金。这些数据通过加密 gRPC 从券商服务器获取,仅存在于 TdxW.exe 进程内存中。

获取方式:通过 Windows dbghelp.MiniDumpWriteDump API 创建 TdxW.exe 进程 minidump(约 875MB,2-3 秒),在其中搜索 double 值匹配。

搜索逻辑

  1. 在 dump 中搜索 股票市值的 IEEE 754 double 编码
  2. 在附近 200 字节范围内扫描所有 double 值(1000 < v < 100000000)
  3. 查找满足 total ≈ stock_mv + cash 的数对
  4. 金额误差 < 2.0 即为匹配

缓存策略

三、实时行情源

3.1 新浪行情 API

GET http://hq.sinajs.cn/list=sz000767,sh603993
Referer: https://finance.sina.com.cn

返回格式(GBK 编码):

var hq_str_sz000767="晋控电力,5.14,5.07,5.58,5.58,5.07,5.58,5.58,371234,200000000,...";

字段索引(逗号分隔):

索引字段说明
0股票名称中文(GBK)
1今开开盘价
2昨收昨日收盘价
3现价★ 当前价格
4最高当日最高
5最低当日最低
30日期YYYY-MM-DD
31时间HH:MM:SS

市场前缀规则:

代码开头新浪前缀
0xxxxx, 3xxxxxsz (深圳)
6xxxxxsh (上海)

3.2 腾讯分时 API

GET http://qt.gtimg.cn/q=sz000767

返回当日 242 个分时数据点(09:30-15:00),用于前端 SVG 分时曲线图表。

3.3 价格优先级

CostCalculator 中的交易价格选取顺序:

优先级来源字段说明
1 (最高)history_trade_*_ENCprice (offset 31)券商实际成交价
2市值变化反推delta_mv / delta_qty估值近似
3新浪实时行情parts[3]当前市价(非成交价)
4持仓均价curr_mv / curr_qty最后手段

四、数据上传流程

4.1 启动方式

# UI 模式(Tkinter 界面 + 自动刷新)
双击 htzq.bat

# 命令行单次上传
python htzq.py --headless

# 自动循环(每3分钟)
python htzq.py --auto

4.2 单次上传流程

1. PositionReader.read_positions()
   ├── 读取 fytrade.db → today_position_*_ENC
   ├── Base64 解码 → 29 字节二进制解析
   ├── 过滤已清仓残留(price=0, avail_qty=0 但 hold_qty>0)
   └── 返回 positions[], cust_id

2. PriceFetcher.get_realtime_prices()
   ├── 调用新浪 API 获取实时价格
   └── 返回 {code: {price, name, prev_close, open, high, low, time}}

3. AccountBalanceCache
   ├── 有持仓 + 有缓存: 总资产 = 缓存现金 + 实时市值
   ├── 无持仓 + 有缓存: 跳过内存读取,使用缓存
   └── 无缓存: minidump 提取(仅一次)

4. DatabaseUploader.upload_positions()
   ├── _get_prev_snapshot()
   │   ├── 从 ht_analysis_log 获取上一批次摘要
   │   └── 从 ht_position 获取上一批次持仓明细
   ├── ChangeDetector.detect()
   │   ├── 对比两次快照的持仓数量和市值
   │   ├── delta_qty > 0 → BUY
   │   ├── delta_qty < 0 → SELL
   │   └── 首次上传不标记为交易
   ├── 三段式(盘前/盘中/盘后)去重覆盖
   │   ├── 盘中无变化 → 每日首访记录1次
   │   └── 同 session 旧记录 → DELETE + INSERT
   ├── CostCalculator.calculate()
   │   ├── 优先使用 history_trade 实际成交价
   │   └── 逐笔计算佣金/印花税/过户费
   ├── CostReconciler.reconcile()
   │   └── 对比预期 vs 实际现金流,反推真实费率
   └── INSERT 三张表
       ├── ht_position (每只股票一行,空仓时无记录)
       ├── ht_analysis_log (每次上传一行)
       └── ht_mv_history (每只股票一行)

4.3 自动刷新

UI 模式下默认每 3 分钟自动执行「刷新 + 上传」。刷新内容包括:

五、PostgreSQL 数据库

5.1 连接信息

conn = psycopg2.connect(
    host="150.158.87.41",
    port=5432,
    database="appdb",
    user="appuser",
    password="AppUser2024!pg",
    sslmode="require"
)

5.2 ht_position — 持仓快照表

每只股票每次上传一行。空仓时此表无新记录(这是导致幻影交易 bug 的根源,已修复)。

CREATE TABLE ht_position (
    id              SERIAL PRIMARY KEY,
    record_time     TIMESTAMP DEFAULT NOW(),
    cust_id         VARCHAR(20),        -- 客户号 (1210839165)
    total_assets    NUMERIC(18,2),      -- 账户总资产
    cash_balance    NUMERIC(18,2),      -- 可用资金
    total_market_value NUMERIC(18,2),   -- 股票总市值
    stock_code      VARCHAR(10),        -- 股票代码
    stock_name      VARCHAR(50),        -- 股票名称
    market          VARCHAR(4),         -- 市场 (SZ/SH)
    current_price   NUMERIC(10,4),      -- 当前价格(实时)
    hold_quantity   INTEGER,            -- 持仓数量
    avail_quantity  INTEGER,            -- 可用数量
    other_quantity  INTEGER,            -- 冻结数量
    market_value    NUMERIC(18,2),      -- 个股市值
    raw_flag        INTEGER,            -- 原始标识
    raw_session     INTEGER,            -- 原始会话ID
    batch_id        VARCHAR(32),        -- 批次ID (MD5前8位)
    price_source    VARCHAR(10)         -- 价格来源 (sina/stale)
);

5.3 ht_analysis_log — 分析日志表

每次上传必定写入一行(即使空仓/无变化)。这是上一批次查询的可靠来源。

CREATE TABLE ht_analysis_log (
    id              SERIAL PRIMARY KEY,
    batch_id        VARCHAR(32) NOT NULL,
    record_time     TIMESTAMP DEFAULT NOW(),
    cust_id         VARCHAR(20),
    total_assets    NUMERIC(18,2),
    cash_balance    NUMERIC(18,2),
    total_market_value NUMERIC(18,2),
    delta_total_assets NUMERIC(18,2),
    delta_cash      NUMERIC(18,2),
    delta_market_value NUMERIC(18,2),
    position_count  INTEGER,            -- 持仓股票数
    position_summary JSONB,             -- {_session, stocks: [{code,name,qty,price,mv,...}]}
    has_trade       BOOLEAN,            -- 是否有交易
    trade_detail    JSONB,              -- [{stock_code, direction, price, qty, amount, commission, ...}]
    estimated_commission NUMERIC(18,2),
    estimated_stamp_tax NUMERIC(18,2),
    estimated_transfer_fee NUMERIC(18,2),
    estimated_total_cost NUMERIC(18,2),
    cost_discrepancy NUMERIC(18,2),     -- 预期vs实际现金流差异
    expected_cash_change NUMERIC(18,2),
    actual_cash_change NUMERIC(18,2),
    reconciled_rate NUMERIC(10,6),      -- 反推真实费率
    rate_match      BOOLEAN,
    raw_snapshot_ids TEXT               -- ht_position 关联ID列表
);

5.4 ht_mv_history — 个股市值历史表

每只股票每次上传一行,用于 API 返回 latest 持仓详情。

CREATE TABLE ht_mv_history (
    id              SERIAL PRIMARY KEY,
    record_time     TIMESTAMP DEFAULT NOW(),
    batch_id        VARCHAR(32),
    cust_id         VARCHAR(20),
    stock_code      VARCHAR(10),
    stock_name      VARCHAR(50),
    current_price   NUMERIC(10,4),
    hold_quantity   INTEGER,
    avail_quantity  INTEGER,
    market_value    NUMERIC(18,2),
    delta_quantity  INTEGER DEFAULT 0,
    delta_mv        NUMERIC(18,2),
    price_source    VARCHAR(10)
);

5.5 三表关系

ht_analysis_log (1) ──batch_id── (N) ht_position
ht_analysis_log (1) ──batch_id── (N) ht_mv_history

关键约束:ht_analysis_log 永远有记录;ht_positionht_mv_history空仓时无记录

六、Web 前端

6.1 页面结构

┌──────────────────────────────────────────┐
│  海通证券 账户持仓                        │
│  ┌─────────┬─────────┬─────────┐         │
│  │ 总资产   │ 股票市值 │ 可用资金 │         │
│  │ ¥37,930  │ ¥0      │ ¥37,930 │         │
│  └─────────┴─────────┴─────────┘         │
│                                          │
│  持仓明细          更新: 2026-06-03...    │
│  ┌──────────────────────────────────┐    │
│  │ 代码 │名称│市场│价格│持仓│可用│市值│    │
│  │ 点击股票 → 实时行情弹窗           │    │
│  └──────────────────────────────────┘    │
│                                          │
│  资金明细日志                             │
│  ┌──────────────────────────────────┐    │
│  │ 时间 │类型│总资产│现金│市值│持仓...│    │
│  │ 每行显示该时间点的历史持仓+交易    │    │
│  └──────────────────────────────────┘    │
│                                          │
│  交易日志                                 │
│  ┌──────────────────────────────────┐    │
│  │ 日期 │代码│名称│方向│数量│价格...│    │
│  │ 佣金 │印花税│过户费 独立列显示     │    │
│  └──────────────────────────────────┘    │
└──────────────────────────────────────────┘

6.2 API 端点

端点方法数据源说明
/api/htzqGETht_analysis_log + ht_position账户总览 + 当前持仓明细
/api/htzq/analysis?days=30GETht_analysis_log + ht_mv_history资金时序 + 每行持仓 + 每行交易
/api/htzq/costs?days=180GETht_analysis_log (has_trade=true)全部历史交易明细
/api/quote?code=000767GET新浪 hq.sinajs.cn实时报价
/api/intraday?code=000767GET腾讯 qt.gtimg.cn分时数据(242点)

6.3 前端 JS 数据流

loadData() 页面加载
├── fetch /api/htzq → data
│   ├── 渲染总览卡片 (total_assets / stock_mv / cash)
│   └── 渲染持仓明细表 (data.positions)
├── fetch /api/htzq/analysis?days=30 → ar
│   ├── 渲染资金明细日志 (ar.fund_flow)
│   │   └── 每行使用 f.positions.stocks(该时间点持仓)
│   │   └── 有交易时追加交易详情 + 费用
│   └── 空仓时正确处理(非"—"覆盖全部历史行)
├── loadTrades()
│   └── fetch /api/htzq/costs?days=180
│       └── 渲染交易日志表(佣金/印花税/过户费分列)
└── 自动刷新: setInterval(loadData, 5分钟)

6.4 行情弹窗

点击持仓表格中的股票行弹出实时行情卡片:

七、费用计算逻辑

7.1 费率假设

费用类型费率说明
佣金0.025% (万2.5)最低 5 元
印花税0.05% (万5)仅卖出收取
过户费0.001% (万0.1)仅上海市场

7.2 成本对账

通过对比预期现金流变化实际现金流变化反推券商真实费率:

预期现金流 = 买入(-) / 卖出(+) 金额 ± 估算费用
实际现金流 = curr_cash - prev_cash
差异 = 实际 - 预期

如果 |差异| >= 0.5 → 怀疑费率不一致
反推真实佣金 = (估算佣金 + 按比例分配的差异) / 成交金额

八、已知 Bug 及修复记录

8.1 空仓后"持仓股票"列全部消失(2026-06-04 修复)

8.2 成交价用新浪市价而非实际成交价(2026-06-04 修复)

8.3 空仓后每次上传产生幻影交易(2026-06-04 修复)

8.4 空仓时内存读取卡死 60+ 秒(2026-06-04 修复)

九、服务器部署

9.1 文件位置

文件服务器路径
Flask API/www/wwwroot/wheelfly.cn/api/api_server.py
前端页面/www/wwwroot/wheelfly.cn/htzq/index.html
技术文档/www/wwwroot/wheelfly.cn/htzq/htzqweb/index.html
systemd 服务/etc/systemd/system/temp-api.service

9.2 服务管理

systemctl restart temp-api   # 重启 Flask(API 代码变更后)
systemctl status temp-api    # 查看状态
# 前端 HTML 变更无需重启,直接刷新浏览器

9.3 上传部署

# SSH 工具 (本地)
cd key-cloud-tencent/ROCKY94_LAUNCH

# 上传 API
scp -i id_ed25519 .../api_server.py root@150.158.87.41:/www/wwwroot/wheelfly.cn/api/

# 上传前端
scp -i id_ed25519 .../index.html root@150.158.87.41:/www/wwwroot/wheelfly.cn/htzq/

# 重启服务
python rocky94_launch.py exec "systemctl restart temp-api"

9.4 Nginx 反向代理

location /api/ {
    proxy_pass http://127.0.0.1:5001/api/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_connect_timeout 10s;
    proxy_read_timeout 10s;
}

CSP: connect-src *(允许前端跨域请求 API)

十、运行依赖

10.1 Python 包

用途
psycopg2-binaryPostgreSQL 连接
ctypesWindows API 调用(minidump)
tkinterUI 界面(Python 内置)
sqlite3fytrade.db 读取(Python 内置)
struct二进制解析(Python 内置)
base64解码(Python 内置)

10.2 外部依赖

依赖说明
Python 3.10D:\Program Files\Python310\python.exe
TdxW.exe国泰海通富易客户端(需运行中才能读内存)
fytrade.db客户端 SQLite 数据库
新浪行情 API免费 HTTP 接口,无需注册
PostgreSQL 16远程 SSL 连接,需 IP 白名单

文档生成: 2026-06-04 · 返回持仓页面