cn-market-data
v1.3.0
Published
A-share and China ETF market data Facade (SH/SZ only): kline, order book, ticks, capital flow, margin financing, dividends. Hong Kong not supported.
Maintainers
Readme
cn-market-data
A-share / China ETF market data adapters for Node.js and Cloudflare Workers.
npm install cn-market-dataArchitecture: see ARCHITECTURE.md.
Maintainer release steps: see PUBLISHING.md.
Changelog: see CHANGELOG.md.
Tests
All Facade tests hit real upstreams (no mocked network):
npm testCoverage: assertMarketCode, getKlineBars (1d / 1w / 15m, stock + ETF), getOrderBook, getTradeTicks, getMinuteCapitalFlow, getDailyCapitalFlow, getMarginFinancingRecords, getDividendRecords (stock + ETF).
Market scope
| In scope | Out of scope |
|---|---|
| Shanghai / Shenzhen A-shares (sh######, sz######) | Hong Kong (hk#####) |
| China-listed ETFs on SH/SZ (e.g. sh510300, sh515080) | US / other overseas markets |
| | TradingView universe screening |
Facade entry points call assertMarketCode() first. Invalid codes throw immediately; they are never sent upstream.
Conventions
| Item | Rule |
|---|---|
| Stock / ETF code | sh###### / sz###### only (case-insensitive; normalized to lowercase) |
| Currency | CNY yuan unless noted |
| Volume | Shares (股) for stocks; fund units (份) for ETF dividends |
| Custom runtime | Pass fetcher (same shape as fetch) for Workers / tests |
Upstream vendors are unofficial public endpoints. Expect rate limits and occasional schema drift.
Price adjustment (复权)
| Path | Adjustment |
|---|---|
| Day / week / month via Sina (primary) | Vendor default bars from CN_MarketData.getKLineData (not an explicit qfq/hfq switch) |
| Day / week / month via Tencent (fallback) | 前复权 (qfq) — URL param ends with ,qfq |
| Minute bars | Intraday raw prints; no historical adjustment |
If you need a guaranteed adjustment mode, prefer documenting which vendor path you hit, or pin behavior in your own layer.
Error semantics
| API | Invalid code | Upstream / empty data |
|---|---|---|
| assertMarketCode | throws | — |
| getKlineBars | throws | Primary fails → fallback; if both fail, throws |
| getOrderBook | throws | Both vendors empty → throws (没有五档行情数据) |
| getTradeTicks | throws | Baidu empty → Tencent; may return [] or throw if Tencent fails |
| getMinuteCapitalFlow | throws | Missing payload → [] |
| getDailyCapitalFlow | throws | Missing payload → [] |
| getMarginFinancingRecords | throws | Missing payload → [] |
| getDividendRecords | throws | No stock/fund rows → [] |
Network / HTTP failures from fetcher generally throw.
Facade API (recommended)
These are the primary entry points. Prefer them over low-level build* / parse* helpers.
getKlineBars(code, options?)
OHLCV bars with vendor fallback.
| Option | Type | Default | Description |
|---|---|---|---|
| frequency | '1d' \| '1w' \| '1M' \| '1m' \| '5m' \| '15m' \| '30m' \| '60m' | '1d' | Bar interval |
| count | number | 10 | How many bars to request (upstream day bars ~1000 max) |
| endDate | string | '' | Used by Tencent day fallback |
| fetcher | Fetcher | fetch | Custom HTTP client |
Source strategy
| Frequency | Primary | Fallback |
|---|---|---|
| 1d / 1w / 1M | Sina (vendor default) | Tencent fqkline (qfq 前复权) |
| 1m | Tencent | — |
| 5m / 15m / 30m / 60m | Sina | Tencent minute |
Returns Promise<KlineBar[]>
type KlineBar = {
time: string; // e.g. "2026-05-13" or "2026-05-13 10:00"
open: number;
high: number;
low: number;
close: number;
volume: number;
};import { getKlineBars } from 'cn-market-data';
const bars = await getKlineBars('sh601398', { frequency: '1d', count: 120 });
console.log(bars.at(-1));getOrderBook(code, options?)
Five-level order book.
| Option | Type | Default |
|---|---|---|
| fetcher | Fetcher | fetch |
Source: Tencent → Baidu fallback. Throws if neither has data.
Returns Promise<StockOrderBook>
type StockOrderBook = {
code: string;
shortName: string;
asks: OrderBookLevel[]; // sell 1 → sell 5
bids: OrderBookLevel[]; // buy 1 → buy 5
};
type OrderBookLevel = {
level: number; // 1 = best
price: number; // yuan
volume: number; // shares
};import { getOrderBook } from 'cn-market-data';
const book = await getOrderBook('sh600519');
console.log(book.bids[0], book.asks[0]);getTradeTicks(code, options?)
Recent trade ticks.
| Option | Type | Default |
|---|---|---|
| limit | number | 60 |
| fetcher | Fetcher | fetch |
Source: Baidu → Tencent fallback.
Returns Promise<StockTradeTick[]> (latest limit ticks)
type StockTradeTick = {
code: string;
tradeTime: string; // Baidu: "YYYY-MM-DD HH:mm:ss"; Tencent fallback may be "HH:mm:ss"
price: number;
volume: number;
bsType: string; // e.g. "B" / "S" or vendor code
};import { getTradeTicks } from 'cn-market-data';
const ticks = await getTradeTicks('sz000001', { limit: 20 });getMinuteCapitalFlow(code, options?)
Intraday capital-flow points (East Money).
| Option | Type | Default |
|---|---|---|
| limit | number | 60 |
| fetcher | Fetcher | fetch |
Returns Promise<CapitalFlowPoint[]>
type CapitalFlowPoint = {
code: string;
tradeTime: string; // usually "YYYY-MM-DD HH:mm"
mainNetInflow: number; // yuan
smallNetInflow: number;
mediumNetInflow: number;
largeNetInflow: number;
maxNetInflow: number; // 超大单
};getDailyCapitalFlow(code, options?)
Daily capital-flow history (East Money).
| Option | Type | Default |
|---|---|---|
| limit | number | 30 |
| startDate | string | — | Inclusive filter YYYY-MM-DD |
| endDate | string | — | Inclusive filter YYYY-MM-DD |
| fetcher | Fetcher | fetch |
Returns Promise<DailyCapitalFlowPoint[]>
type DailyCapitalFlowPoint = {
code: string;
tradeDate: string; // YYYY-MM-DD
mainNetInflow: number;
smallNetInflow: number;
mediumNetInflow: number;
largeNetInflow: number;
maxNetInflow: number;
};import { getDailyCapitalFlow } from 'cn-market-data';
const flow = await getDailyCapitalFlow('sh600519', {
startDate: '2025-01-01',
endDate: '2025-12-31',
limit: 250,
});getMarginFinancingRecords(code, options?)
Margin financing / securities lending daily series (East Money).
| Option | Type | Default |
|---|---|---|
| limit | number | 7 |
| fetcher | Fetcher | fetch |
Returns Promise<StockMarginFinancingRecord[]>
type StockMarginFinancingRecord = {
code: string;
rawCode: string; // e.g. "600519"
name: string;
secuCode: string; // e.g. "600519.SH"
tradeDate: string; // YYYY-MM-DD
financingBalance?: number;
financingBuyAmount?: number;
financingRepayAmount?: number;
financingNetBuyAmount?: number;
financingBalanceRatio?: number; // %
securitiesLendingVolume?: number;
securitiesLendingBalance?: number;
securitiesLendingSellVolume?: number;
securitiesLendingRepayVolume?: number;
securitiesLendingNetSellVolume?: number;
marginBalance?: number;
closePrice?: number;
changePct?: number;
circulatingMarketValue?: number;
financingBalanceGrowthRate?: number;
};import { getMarginFinancingRecords } from 'cn-market-data';
const rows = await getMarginFinancingRecords('sh600519', { limit: 30 });getDividendRecords(code)
Cash dividend history for stocks and ETFs (East Money).
Lookup order
- Stock share-bonus report (
RPT_SHAREBONUS_DET) - If empty → fund/ETF dividend report (
RPT_F10_FUND_DIVIDEND)
Returns Promise<StockDividendRecord[]>
type StockDividendRecord = {
code: string;
reportDate: string; // announcement date YYYY-MM-DD
dividendPlan: string; // vendor text, e.g. "10派1.689元(含税,扣税后1.5201元)"
exDividendDate: string | null; // 除权除息日
cashDividendPerShare: number | null; // 含税,元/股 or 元/份
};Cash math (broker pre-tax)
到账含税现金 ≈ 持仓数量 × cashDividendPerShare- Stocks:
cashDividendPerShare = PRETAX_BONUS_RMB / 10(每 10 股派息 → 每股) - ETFs:
cashDividendPerShare = DIVIDEND(每份) - Personal dividend tax is not applied here (depends on holding period; often settled when selling)
import { getDividendRecords } from 'cn-market-data';
const icbc = await getDividendRecords('sh601398');
// e.g. cashDividendPerShare === 0.1689 → 1000 shares × 0.1689 = 168.9 yuan (pre-tax)
const etf = await getDividendRecords('sh515080');
// e.g. cashDividendPerShare === 0.02 → 10000 units × 0.02 = 200 yuan (pre-tax)API overview
| Function | Data | Source |
|---|---|---|
| getKlineBars | OHLCV | Sina / Tencent |
| getOrderBook | 五档盘口 | Tencent / Baidu |
| getTradeTicks | 分笔成交 | Baidu / Tencent |
| getMinuteCapitalFlow | 分钟资金流 | East Money |
| getDailyCapitalFlow | 日资金流 | East Money |
| getMarginFinancingRecords | 融资融券 | East Money |
| getDividendRecords | 现金分红(股/ETF) | East Money |
Public surface
Only the Facade is published:
| Category | Exports |
|---|---|
| Methods | getKlineBars, getOrderBook, getTradeTicks, getMinuteCapitalFlow, getDailyCapitalFlow, getMarginFinancingRecords, getDividendRecords, assertMarketCode |
| Options | RequestOptions, LimitOptions, GetKlineBarsOptions, GetDailyCapitalFlowOptions |
| Types | Fetcher, Frequency, KlineBar, OrderBookLevel, StockOrderBook, StockTradeTick, CapitalFlowPoint, DailyCapitalFlowPoint, StockMarginFinancingRecord, StockDividendRecord |
Vendor build* / parse* helpers are not part of the public API (repo-internal only).
Custom fetcher (Cloudflare Workers / tests)
import { getKlineBars } from 'cn-market-data';
await getKlineBars('sh600519', {
frequency: '1d',
count: 30,
fetcher: (input, init) => fetch(input, init),
});Notes / limits
- Day/week/month history depth is bounded by upstream APIs (~1000 bars).
- Dividend precision for stocks follows East Money
PRETAX_BONUS_RMB(e.g. ICBC1.689/ 10 =0.1689). - Hong Kong / overseas symbols are rejected by design.
- Not financial advice; data is for engineering / research use.
License
MIT
