Guide

Reading A-share shareholder counts: chip concentration

Holder count is the most direct public signal of "whose hands are the chips in": a falling count = concentration in fewer hands (often positive), a surging count = dispersion (retail crowding in). This guide covers the three tables, the fields, and a real case.

1. Why look at shareholder counts (30 seconds)

Short-term moves are driven by money; medium-term moves are decided by chip structure. The holder count is the clearest public signal of that structure:

  • Count falling → chips moving from retail to a few hands (big money / institutions) — usually read as accumulation
  • Count rising → chips dispersing (more retail taking over) — usually read as distribution / fading
  • Average shares held = total shares / count, inversely related, also measures concentration

Note: "usually read" is not a hard signal — combine with price level and who is on both sides

2. One request, three tables

`/v1/shareholder` is a paid endpoint. One parameter: `code`. The `data` is an object with `tables` (three tables).

Python: fetch shareholder structure (three tables)
import requests

BASE = "https://api.ashareapi.com/v1"
H = {"Authorization": "Bearer ct-your-key"}

b = requests.get(f"{BASE}/shareholder",
                 params={"code": "sh600667"}, headers=H, timeout=60).json()
d = b["data"]                      # note: data is an object, not an array

for t in d["tables"]:              # three tables, each with title/slug/rows
    print("== [%s] %s ==" % (t["slug"], t["title"]))
    for row in t["rows"][:5]:
        print("  ", row)
  • `data` is an object (with `tables`), not a plain array — iterate with `data["tables"]`
  • Do not use `body["structured"]` — shareholder data is a dict; that key is absent

3. What each of the three tables tells you

`top10_holders`
Top-10 shareholders
Who controls / whether big institutions are there
`top10_float_holders`
Top-10 float holders
Who is in the tradable float (closer to trading)
`holder_count`
Holder-count history
Chip concentration core: count trend over time

`holder_count` is the most valuable — it is reported per quarter and shows the count trend (not a single point).

4. Reading the fields

holdShares
Shares held
shares
How many the holder owns
holdPct
Holding %
%
Share of total shares
holdChange
Holding change
shares
+ = added / - = reduced / 0 = unchanged
totalSHNum
Total holder count
holders
Count at the period
avgHoldShares
Avg shares per holder
shares
Total shares / count (inverse to count)
date
As-of date
-
Report date (important: lags)

Two counts: `totalSHNum` (total) and `aSHNum` (A-share) are usually equal; `avgHoldShares` / `aAvgHoldShares` likewise.

5. Real case: Tai Ji Industry (600667) count triples in a year

Live holder_count table returned 2026-09-29:

  • Trend: 153.6k → 178.2k → 548.9k, a 3.5x jump; avg shares fell from 13,614 to 3,810
  • Meaning: lots of new retail flooded in, chips moved from concentrated to heavily dispersed — a classic "high-level distribution / retailisation" pattern
  • Combine with price: if price rose then stalled, "count surge + price stalling" is a distribution risk; if it happens at a low after volume expansion, it may start a new rotation (needs more context; not a conclusion here)
2025-12-31
153,627
13,614
Baseline
2026-03-31
178,193
11,738
+16%, chips starting to spread
2026-06-30
548,947
3,810
Count tripled — chips heavily dispersed

Why this is a good case: it shows the proper use of the metric — read the trend, not a single point, and pair it with the price level.

6. Bulk scan (find "chips concentrating")

Python: scan a list for holder counts falling notably
CODES = ["sh600667", "sh600519", "sz300750", "sh601318",
         "sh600036", "sz000858", "sh600030", "sh601899"]

for code in CODES:
    b = requests.get(f"{BASE}/shareholder", params={"code": code},
                     headers=H, timeout=60).json()
    d = b.get("data") or {}
    for t in d.get("tables", []):
        if t.get("slug") != "holder_count":
            continue
        rows = t.get("rows", [])           # latest first
        if len(rows) < 2:
            continue
        latest = int(rows[0].get("totalSHNum") or 0)
        prev = int(rows[1].get("totalSHNum") or 0)
        if prev > 0 and latest < prev * 0.9:     # count down >10% = concentrating
            print("%s  %s  count %d → %d  [concentrating]" % (
                code, rows[0].get("date"), prev, latest))
  • Find concentration: `latest < prev * 0.9` (down >10% from prior) — chips moving to fewer hands
  • Find dispersion (reverse): `latest > prev * 1.2` (up >20%) — retail crowding in (can be a avoid signal)
  • Mind the lag: counts are quarterly (a 2026-06-30 count only appears by end of August) — not a "today" signal

7. Two common misreads (important)

  • 1. Count data lags — quarterly, not real-time. Look at the `date` (as-of) field and do not treat it as the latest
  • 2. Falling count is not always bullish, rising not always bearish — it is a mirror of chip structure. Combine with: price level (high-level dispersion is risky / low-level concentration may accumulate), who is on both sides (institution vs retail), and the overall market

FAQ

Is the holder count real-time?

No. Disclosed per quarterly report (with lag). The date field marks the as-of date — use it to know which period the data is, not the latest.

Is a falling count bullish?

Usually a positive "chip concentration" signal, but not absolute. Combine with price level: low-level concentration (may accumulate) differs from high-level concentration (may already have run). It is a structural mirror, not a buy/sell conclusion.

Why not just look at top-10 holders?

Top-10 holders are a single point (who holds now); holder count is a trend (how concentration is changing). They complement: top-10 shows "who is there", count shows "concentrating or dispersing".

Why is data an object not an array?

shareholder returns multiple tables (top-10 / float / count), carried in an object with a tables array. Iterate with data["tables"], not body["structured"].

Last updated: 2026-09-29

Fields and structure from live ashareapi /v1/shareholder responses (2026-09-29 measured: three-table structure, field semantics); case data is the real holder_count series of Tai Ji Industry (600667) across 2025-12-31 / 2026-03-31 / 2026-06-30.