Skip to content

fix: migrate from discontinued Xueqiu API to restore functionality - #8

Open
Ramos-dev wants to merge 1 commit into
hellopao:masterfrom
Ramos-dev:fix/migrate-dead-quote-api
Open

fix: migrate from discontinued Xueqiu API to restore functionality#8
Ramos-dev wants to merge 1 commit into
hellopao:masterfrom
Ramos-dev:fix/migrate-dead-quote-api

Conversation

@Ramos-dev

Copy link
Copy Markdown

Problem

The Xueqiu endpoints this tool depends on have been discontinued. Both
xueqiu.com/stock/search.json and xueqiu.com/v4/stock/quote.json now return
301 Moved Permanently and require authentication, so the response body is HTML
rather than JSON.

Every command currently fails:

$ wstock query 茅台
TypeError: Cannot read properties of undefined (reading 'length')
    at index.js:118:21

$ wstock list
(no output)

JSON.parse receives the redirect page, so data.stocks is undefined.

Reproducing the upstream call directly confirms it:

$ curl -s "http://xueqiu.com/v4/stock/quote.json?code=SH000001"
<html>
<head><title>301 Moved Permanently</title>...

Solution

Migrate to public endpoints that need no authentication:

Purpose Endpoint
Quotes Tencent qt.gtimg.cn
Search Sina suggest3.sinajs.cn

Both cover A-shares, Hong Kong and US markets.

Changes

Data layer

  • lib/fetch.js (new) — small client over Node's built-in http/https with GBK
    decoding, gzip/deflate, redirect following and a 10s timeout.
  • lib/parser.js (new) — quote/search parsing and market-prefix inference, so bare
    codes like 600519 or 700 resolve correctly.
  • Removes the deprecated request dependency: 49 packages → 2. The runtime now
    has no third-party network dependency.

Bugs fixed along the way

  • GBK responses rendered Chinese names as mojibake; now decoded properly.
  • CJK column alignment — padding counted code units, so full-width names broke
    the table. Now aligned by display width.
  • percentage accumulated % on each poll (-1.5%-1.5%%-1.5%%%)
    because showStockStatus mutated the object in place. Formatting is now idempotent.
  • Polling swallowed rejectionssetInterval(promise, ...) never handled
    failure, so one transient network error silently stopped updates. Replaced with a
    recursive setTimeout plus error reporting.
  • 000xxx market ambiguity — this range overlaps between exchanges: 000001 is
    the SSE Composite (Shanghai) while 000801 is a Shenzhen listing. Naive prefix
    matching sent sz000801 — which is in the default stock.json — to the wrong
    exchange. Now disambiguated via a known-index table.
  • Inconsistent timestamps across markets, normalised to YYYY/MM/DD HH:mm:ss.
  • url.parse() deprecation warning (DEP0169), replaced with the WHATWG URL API.

Additions

  • -s/--single prints once and exits, for scripting instead of the polling default.
  • npm test covers offline parsing/code-normalisation plus live connectivity across
    all four markets (20 assertions).
  • README and index.d.ts updated.

Compatibility

  • config/stock.json is untouched — the original default list still works, and
    add/remove now preserve the file's existing compact formatting instead of
    rewriting it.
  • Public API (queryStockInfo, queryStockStatus, queryStockListStatus,
    addStock, removeStock) is unchanged; all existing CLI flags still behave the same.
  • Stock objects gain optional fields (change, volume, amount, time); existing
    fields keep their names and string types.
  • engines.node moves to >=12.0.0, required by TextDecoder for GBK decoding.
  • Version intentionally left unbumped, for you to decide on release.

Verification

Verified from a clean clone of this branch:

$ wstock list -s
股票名称    股票代码    今日开盘    昨日收盘    当前价格    今日最高    今日最低    今日涨幅
上证指数    sh000001    3950.71     3966.59     3966.02     3966.02     3942.75     -0.01%
深证成指    sz399001    14266.44    14316.96    14414.32    14447.10    14201.29    0.68%
四川九洲    sz000801    11.52       11.61       11.52       11.62       11.40       -0.78%

更新时间: 2026/08/11 11:28:33

$ npm test
通过 20 项, 失败 0 项

Also exercised query (by name and code), show (Shanghai/Shenzhen/HK/US),
add/remove roundtrip, and invalid codes — which now return a message instead of
crashing.

One note in case it's useful: both search endpoints expect UTF-8 query keys while
returning GBK bodies. Sending GBK-encoded keys returns a generic hot-stock list
rather than an error, which looks like working code returning wrong data.

The Xueqiu endpoints this tool relies on have been discontinued. Both
`xueqiu.com/stock/search.json` and `xueqiu.com/v4/stock/quote.json` now
return `301 Moved Permanently` and require authentication, so the
response body is HTML rather than JSON.

As a result every command fails:

    $ wstock query 茅台
    TypeError: Cannot read properties of undefined (reading 'length')
        at index.js:118:21

`JSON.parse` receives the redirect page, leaving `data.stocks` undefined.

Data sources
------------
* Quotes now use Tencent `qt.gtimg.cn`
* Search now uses Sina `suggest3.sinajs.cn`

Both are public and need no authentication. Covers A-shares, Hong Kong
and US markets.

Changes
-------
* Add `lib/fetch.js` — a small client over Node's built-in `http`/`https`
  handling GBK decoding, gzip/deflate, redirects and a 10s timeout.
* Add `lib/parser.js` — quote/search parsing plus market-prefix
  inference, so bare codes such as `600519` or `700` now work.
* Drop the deprecated `request` dependency (49 packages down to 2); the
  runtime now has no third-party network dependency.
* Decode GBK responses, which previously rendered Chinese names as
  mojibake.
* Align columns by display width, treating full-width characters as two
  cells so CJK names line up.
* Stop re-appending `%` to `percentage` on every poll, which accumulated
  into `-1.5%%%` while watching.
* Handle rejections in the polling loop; a transient network error
  previously stopped updates silently.
* Refresh in place instead of emitting `ESC[2J`, which pushed each frame
  into the scrollback buffer so `list` and `show` appeared to append
  rather than update. Now uses `cursorTo`/`clearScreenDown`, and skips
  control characters entirely when stdout is not a TTY so piped output
  stays parseable.
* Disambiguate the `000xxx` code range, where Shanghai indices overlap
  Shenzhen tickers (`000001` is the SSE Composite, `000801` is a
  Shenzhen listing).
* Normalise timestamps to `YYYY/MM/DD HH:mm:ss` across all markets.
* Replace the deprecated `url.parse()` with the WHATWG `URL` API.
* Add `-s/--single` to print once and exit, for scripting.
* Add tests covering offline parsing, terminal refresh and live
  connectivity, and update the README and type definitions.

`config/stock.json` is left untouched, and `add`/`remove` now preserve
its original formatting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant