fix: migrate from discontinued Xueqiu API to restore functionality - #8
Open
Ramos-dev wants to merge 1 commit into
Open
fix: migrate from discontinued Xueqiu API to restore functionality#8Ramos-dev wants to merge 1 commit into
Ramos-dev wants to merge 1 commit into
Conversation
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.
Ramos-dev
force-pushed
the
fix/migrate-dead-quote-api
branch
from
August 11, 2026 03:40
f054358 to
ed35898
Compare
This was referenced Aug 11, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Xueqiu endpoints this tool depends on have been discontinued. Both
xueqiu.com/stock/search.jsonandxueqiu.com/v4/stock/quote.jsonnow return301 Moved Permanentlyand require authentication, so the response body is HTMLrather than JSON.
Every command currently fails:
JSON.parsereceives the redirect page, sodata.stocksisundefined.Reproducing the upstream call directly confirms it:
Solution
Migrate to public endpoints that need no authentication:
qt.gtimg.cnsuggest3.sinajs.cnBoth cover A-shares, Hong Kong and US markets.
Changes
Data layer
lib/fetch.js(new) — small client over Node's built-inhttp/httpswith GBKdecoding, gzip/deflate, redirect following and a 10s timeout.
lib/parser.js(new) — quote/search parsing and market-prefix inference, so barecodes like
600519or700resolve correctly.requestdependency: 49 packages → 2. The runtime nowhas no third-party network dependency.
Bugs fixed along the way
the table. Now aligned by display width.
percentageaccumulated%on each poll (-1.5%→-1.5%%→-1.5%%%)because
showStockStatusmutated the object in place. Formatting is now idempotent.setInterval(promise, ...)never handledfailure, so one transient network error silently stopped updates. Replaced with a
recursive
setTimeoutplus error reporting.000xxxmarket ambiguity — this range overlaps between exchanges:000001isthe SSE Composite (Shanghai) while
000801is a Shenzhen listing. Naive prefixmatching sent
sz000801— which is in the defaultstock.json— to the wrongexchange. Now disambiguated via a known-index table.
YYYY/MM/DD HH:mm:ss.url.parse()deprecation warning (DEP0169), replaced with the WHATWGURLAPI.Additions
-s/--singleprints once and exits, for scripting instead of the polling default.npm testcovers offline parsing/code-normalisation plus live connectivity acrossall four markets (20 assertions).
index.d.tsupdated.Compatibility
config/stock.jsonis untouched — the original default list still works, andadd/removenow preserve the file's existing compact formatting instead ofrewriting it.
queryStockInfo,queryStockStatus,queryStockListStatus,addStock,removeStock) is unchanged; all existing CLI flags still behave the same.change,volume,amount,time); existingfields keep their names and string types.
engines.nodemoves to>=12.0.0, required byTextDecoderfor GBK decoding.Verification
Verified from a clean clone of this branch:
Also exercised
query(by name and code),show(Shanghai/Shenzhen/HK/US),add/removeroundtrip, and invalid codes — which now return a message instead ofcrashing.
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.