Guide

Reading A-share block trades: discount rate & institutional seats

A block trade is a large off-orderbook transfer agreed between two parties. It never shows on the intraday chart, yet it often reveals intent before price moves. This guide covers: how to read the fields, what the discount rate means, and how to scan in bulk with Python (with live 2026-09-29 data).

1. What a block trade is (30 seconds)

A block trade is a large transfer executed through the exchange block-trading system by mutual agreement. Because it bypasses continuous auction: it does not appear on the intraday tape, and the trade is disclosed the next day (T+1).

That is the value: you can see who took how much, at what price, from whom — information the normal order book does not give.

  • Sellers wanting to offload → use blocks (fast exit without crashing the price)
  • Buyers wanting size → take blocks (faster than buying in the market)
  • Discount = seller concedes for speed (common); premium = buyer is eager (rare, stronger signal)

2. One request to fetch it

`/v1/block-trade` is a paid endpoint. Only two parameters: `code` (optional) and `date` (optional; default = latest).

Python: block trades for one stock
import requests

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

r = requests.get(f"{BASE}/block-trade",
                 params={"code": "sh600519"},     # Kweichow Moutai
                 headers=H, timeout=30)
body = r.json()

# Note: data may be an array; do NOT write body["structured"] (KeyError)
rows = body.get("data") if isinstance(body.get("data"), list) else body.get("structured")
if not body.get("ok"):
    print("no block trade that day (not an API failure)")   # see section 4
else:
    for x in rows:
        print(x["TurnoverPrice"], x["CloseDiscountRate"],
              x["BuySalesDepartment"], x["SellSalesDepartment"])
  • `data` may be an array or Markdown — branch on type (or fall back to `body.get("structured")`)
  • `ok:false` with an empty array = no block trade that day (a normal result, not a fault) — see section 4

3. The seven fields

TurnoverPrice
Trade price
CNY
Compare with the close to get premium/discount
TurnoverValue
Trade value
CNY
> 100m = large; judge the scale of impact
CloseDiscountRate
Discount rate
%
+ = premium / - = discount / 0 = flat
BuySalesDepartment
Buy-side brokerage
-
Institutional seat or a named branch
SellSalesDepartment
Sell-side brokerage
-
Who sold
TradingType
Trade type
-
Agreement trade / after-hours fixing, etc.
SerialNumber
Sequence
-
Which trade of the day

Volume caveat: `TurnoverValue` is a single trade. Sum same-day trades for a daily total. The upstream reports "number of trading days with blocks" and "total trade count" as two different measures — state which one you use.

4. The discount rate is the most informative field

`CloseDiscountRate` is relative to the closing price of the day:

Positive (premium)
Buyer paid above market
Buyer is eager (rare) - stronger signal
0 (flat)
Traded at market
Common, neutral
Negative (discount)
Seller conceded for speed
Common - deeper discount, more urgency

Key point: discounts are the norm — large stakes are hard to sell at market, so a discount alone is not a bearish signal. What carries more information is an unusually deep discount (e.g. -8%) combined with who bought (institutional seat vs an ordinary branch).

5. Live data (2026-09-29)

sh600519 Kweichow Moutai
1299.52
90.97m
0.00
GF Securities Beijing Lugu Rd
sh600276 Hengrui Pharma
49.75
252.19m
-8.01
Huatai Securities Shanghai Br.
sh601899 Zijin Mining
29.42
12.06m
0.00
Industrial Securities Shanghai
sz300750 CATL
286.80
3.16m
0.00
Institutional seat
sh601318 Ping An
56.63
3.12m
0.00
CITIC HQ (non-branch)

How to read it: the Hengrui trade — 252m at an 8.01% discount — shows a clearly motivated seller, with the buyer a brokerage branch. That combination (large + deep discount) is worth a closer look (no directional conclusion — it just shows seller urgency).

6. Scanning in bulk (find large / deep-discount trades)

Python: scan a list, pick out large or deep-discount trades
CODES = ["sh600519", "sh600276", "sh601899", "sz300750",
         "sh601318", "sh600036", "sz000858", "sh600030"]

hits = []
for code in CODES:
    b = requests.get(f"{BASE}/block-trade", params={"code": code},
                     headers=H, timeout=30).json()
    if not b.get("ok"):
        continue                                   # no block trade that day
    rows = b.get("data") or b.get("structured") or []
    for x in rows:
        amt = float(x.get("TurnoverValue") or 0)
        dis = float(x.get("CloseDiscountRate") or 0)
        if amt >= 1e8 or dis <= -5:                # large or deep discount
            hits.append((code, amt / 1e8, dis, x.get("BuySalesDepartment", "")))

for code, amt, dis, buyer in sorted(hits, key=lambda z: -z[1]):
    print("%-10s %.2f bn  rate %+.2f%%  %s" % (code, amt, dis, buyer[:24]))
  • Large: value ≥ 100m CNY (big enough to shift the shareholder structure)
  • Deep discount: rate ≤ -5% (clear seller concession)
  • Combined: "large + deep discount + institutional buyer" is a relatively rare and informative mix

7. The date parameter (historical days)

`date=YYYY-MM-DD` queries a specific day — non-trading days fall back to the most recent trading day (measured: `2026-09-26`, a Saturday, returned `2026-09-24`).

Python: query a specific date
b = requests.get(f"{BASE}/block-trade",
                 params={"code": "sh600276", "date": "2026-09-26"},
                 headers=H, timeout=30).json()
print(b["ok"], b.get("data"))
  • Measured: the first table (date / close and other meta) follows the date, falling back to the latest trading day
  • Detail table behaviour: repeated queries returned the same trade details in our tests — either that stock had only one block, or the detail table only returns the latest. Verify with a sample whose days clearly differ before assuming multi-day history

8. Two common misreads (important)

  • 1. `ok:false` does not mean the API is broken — it means no block trade for that stock on that day (a normal result). Most stocks have none on most days. To check the endpoint is fine, try a liquid large cap (e.g. `sh600519`)
  • 2. A discount is not automatically bearish — large disposals need a discount to clear fast. "Has a discount" is normal; "unusually deep discount + institutional buyer" is what deserves attention

FAQ

It returns ok:false with an empty array - is the API broken?

No. It means no block trade for that stock that day. Block trades are not an everyday event for most stocks. Try a liquid large cap (e.g. sh600519) - if it returns data, the endpoint is fine.

Is the data real-time?

Block trades are disclosed T+1. Our tests returned the latest disclosed trading day (the date field in the response tells you which day).

The discount rate is relative to what?

The closing price of the day. Positive = premium (buyer paid above market), negative = discount (seller conceded), 0 = flat. Discounts are the norm and not automatically bearish.

Why is structured null?

structured / tables are conditional fields - only attached when data is Markdown text. For block-trade, data is already an array (structured by nature), so the extra layer is unnecessary. Use body.get("structured") or branch on the data type.

Last updated: 2026-09-29

Field semantics from the endpoint docs and measured responses; sample data are live ashareapi /v1/block-trade responses (2026-09-29: Kweichow Moutai / Hengrui Pharma / Zijin Mining / CATL / Ping An); date behaviour verified across multiple dates including a weekend fallback.