A-share top-trader boards with Python: four approaches
Four options: call an HTTP API directly, Tushare, AkShare, or scrape pages. Start with the first — it returns institution, hot-money and active-seat boards in one call (other routes usually give only flat detail rows). Below: runnable code and when not to use each.
Option 1: call the HTTP API directly (recommended)
This is a paid endpoint (Pro tier and up): one request with your key returns the shared envelope `{ ok, endpoint, tier, elapsed_ms, source, data }`. The `type` parameter picks the board: `institution` / `hotmoney` / `activeseat`.
Inside the response, `data` is a human-readable Markdown table while `structured` is the same data already parsed into an array of objects — use `structured` in code instead of parsing Markdown.
import requests
KEY = "ct-your-key" # the top-trader endpoint needs a key (Pro tier and up)
r = requests.get(
"https://api.ashareapi.com/v1/lhb",
headers={"Authorization": "Bearer " + KEY},
params={"type": "institution"}, # institution / hotmoney / activeseat
timeout=30,
)
body = r.json()
if not body.get("ok"):
raise RuntimeError(body) # upstream failure returns ok=false and is NOT counted
for row in body["structured"][:3]:
net = float(row["netBuyAmt"]) # values are strings, convert before doing math
print(row["name"], row["instBuyBranchCount"], "institutions",
"net buy", round(net / 1e8, 2), "x1e8 CNY")- `structured[].code` carries the market prefix (`sz301234`) · `tdDays` = days on the board · `instBuyBranchCount` = number of buying institutions
- `instBuyAmt` / `netBuyAmt` are in CNY (not 10k CNY) · `instBuyRate` / `netBuyRate` are percentages (`20` means 20%)
- The response also carries `tables` (the Markdown tables parsed into 2D arrays), identical in content to `structured`
What the three boards are
One live sample (2026-09-23): the institution board returned 34 rows and active seats 161. Top institution entry: Wuzhou Medical (4 institutions bought 135M CNY, net 33M). Top active seat: the Shanghai Stock Connect seat, 345M CNY bought.
In active seats, `code` and `stockName` are both semicolon-separated lists with matching order — e.g. `sh600127;sh600664;sh688105` maps to `Jinjian Cereals;Harbin Pharma;Novogene`. Just `split(";")`.
| type | Board | Key fields | Question it answers |
|---|---|---|---|
| institution | Institution board | instBuyBranchCount · instBuyAmt · netBuyAmt | Which stocks institutions bought today |
| activeseat | Active seats | name · code (semicolon-separated) · buyAmt | Which branches/channels are most active (incl. Stock Connect, known hot-money branches) |
| hotmoney | Hot money board | No data upstream right now (see FAQ) | Hot-money moves — currently unavailable, stated honestly |
Option 2: Tushare (top_list / top_inst)
Tushare ships official board endpoints with stable data, but you must sign up, get a token and hold enough credits: daily detail `top_list` needs 2000+ credits, institutional detail `top_inst` needs 5000+ (per Tushare official docs).
It returns flat detail rows (which seat bought how much of which stock) — there is no pre-grouped institution/hot-money board, so you aggregate yourself.
# pip install tushare
import tushare as ts
pro = ts.pro_api("your-token")
df = pro.top_list(trade_date="20260923") # daily detail (needs 2000+ credits)
print(df[["ts_code", "name", "net_amount", "reason"]].head())- When not to use it: you just want "what did institutions buy today" — flat detail still needs aggregation, a board endpoint is faster
- When it fits: you already run historical research on Tushare and need boards joined with financials and quotes
Option 3: AkShare (stock_lhb_detail_em)
AkShare needs no token and scrapes public East Money pages; the board entry is `stock_lhb_detail_em(start_date, end_date)`, with extra Sina-backed interfaces for daily detail, institutional seats and branch statistics.
The cost is that upstream redesigns break it and you maintain the upgrades (most of its issue tracker). Fine for one-off research; a long-running job needs someone watching.
# pip install akshare
import akshare as ak
df = ak.stock_lhb_detail_em(start_date="20260901", end_date="20260923")
print(df.head())Option 4: scrape East Money / exchange pages yourself (not recommended)
Only when there is no API. Scraping boards stacks three problems: page structure changes, anti-bot measures (rate limits, captchas, IP bans), and the compliance call for how you use the data lands back on you.
Exchange disclosure pages (SSE / SZSE) are the most authoritative source, but they are pages for humans, not interfaces for programs — parsing is costly and field semantics are yours to align.
How to choose
| Direct HTTP API | Tushare | AkShare | Scrape pages | |
|---|---|---|---|---|
| Effort to start | Lowest: one GET request | Signup + credits | Install only | Highest: parsing + anti-bot |
| Cost | Paid endpoint (Pro+) | Credit thresholds (2000 / 5000) | Free | Free but labour-heavy |
| Boards | institution / hot-money / active seats | Detail only, aggregate yourself | Detail only, aggregate yourself | Depends on page |
| Stability | Multi-source failover + caching | Official, stable | Breaks on upstream change | Breaks on page change |
| Best for | Daily institution & seat tracking | Historical research + joins | One-off research | Special data with no API |
Gotchas we hit in testing
- Every field value is a string: `"135160431.2"` must be `float()`-ed before math, or `+` concatenates
- Amounts are in CNY: 135160431.2 is 1.35 x 1e8 CNY — do not multiply by 10k again
- `instBuyRate` / `netBuyRate` are percentages (20 means 20%), not fractions
- `code` needs the market prefix (`sz301234` / `sh600664`); active-seat codes are semicolon-separated lists
- `data` is a Markdown string while `structured` is the array — use the latter in code
- The hot-money board (`hotmoney`) has no upstream data right now and returns "market data not found" (verified for 2026-09-17 through 09-22)
Common errors and boundaries
- `401` = missing key: this is a paid endpoint, anonymous calls are rejected
- `429` = rate limited: limits follow your key tier; slow down or upgrade
- `ok:false` = upstream fetch failed: we already failed over and it is not counted; one retry usually works
- Update cadence: after the close on trading days (no same-day board intraday — that is the disclosure schedule, not API latency)
- `date` accepts history (`YYYY-MM-DD`); empty means the latest trading day, and holidays will not turn into "today"
FAQ
The institution board (`institution`) counts institutional dedicated seats: number of institutions, institutional buy amount and net buy — useful to tell whether institutional money is entering. The hot-money board (`hotmoney`) tracks well-known hot-money branches. They are different scopes and are not interchangeable.
Stated plainly: across 2026-09-17 to 09-22 our hot-money board had no data — that upstream feed is currently unavailable. Use institution + active seats instead: active seats list the Stock Connect seat and known hot-money branches (e.g. Kaiyuan Securities Xian West Street) with buy amounts and related stocks, covering most "where did the money come from" questions. It will reconnect automatically once upstream recovers, with no code change.
After the close on trading days. The boards are disclosed by the exchange after the session, so the same-day board is unavailable intraday — that is when the data exists, not API latency. The `date` field/parameter tells you which session the data belongs to.
No. The top-trader board is a paid endpoint (Pro tier and up); the five free endpoints are quote, K-line, hot list, market overview and breadth. To check the response shape first, see the samples on the endpoint page.
Amount fields (`instBuyAmt` / `netBuyAmt` / `totalBuyAmt` / `buyAmt`) are all in CNY; ratio fields (`instBuyRate` / `netBuyRate`) are percentages where `20` means 20%. All numeric values are strings in JSON — `float()` them before computing.
Last updated: 2026-09-23
Code and fields come from live responses (verified 2026-09-23: 34 institution rows / 161 active seats / no hot-money data). Tushare and AkShare descriptions follow their official docs (Tushare top_list doc_id=106, top_inst doc_id=107; AkShare stock_lhb_detail_em); thresholds and interfaces are theirs to change.