Skip to content

Symbols & Codes

stock-sdk v2 funnels the whole "how do I write a ticker" question through a single entry point: normalizeSymbol. Whether you write sh600519, 600519, 600519.SH, or { code: '600519' }, it all resolves to the same internal representation before being handed to each provider adapter.

This page covers two things:

  1. How to pass symbols (SDK methods primarily take string / string[]; normalizeSymbol supports an optional SymbolRef);
  2. The fault-tolerant parsing rules, the pure-code ambiguities, and when a hint is required.

Input: string first-class

Public SDK methods primarily take string / string[]. SymbolRef is the disambiguation input exposed by stock-sdk/symbols for normalizeSymbol:

ts
type SymbolInput = string | SymbolRef;

interface SymbolRef {
  code: string;
  market?: 'CN' | 'HK' | 'US' | 'GLOBAL';
  assetType?: 'stock' | 'index' | 'fund' | 'bond' | 'futures' | 'option' | 'board';
  exchange?: string; // 'SSE' | 'SZSE' | 'BSE' | 'HKEX' | 'NASDAQ' | ...
}

A bare string is the preferred form and covers the vast majority of cases:

ts
await sdk.quotes.cn(['sh600519', '000001']);
await sdk.quotes.hk(['00700']);        // Hong Kong
await sdk.quotes.us(['AAPL']);         // US

SymbolRef is not a separate system — it's a "string with hints". Its code still goes through normalizeSymbol; it just carries extra market / assetType / exchange hints to disambiguate. When you need object hints, normalize first and then pass the explicit code string to the SDK method.

ts
// Use SymbolRef only when a bare code can't be told apart
normalizeSymbol({ code: '510300', assetType: 'fund' });
await sdk.quotes.fund(['510300']);

normalizeSymbol: unified fault-tolerant parsing

normalizeSymbol is exported from stock-sdk/symbols. It's a pure function — zero network, works in both browser and Node. Providers reuse it where needed, and you can use it directly to normalize symbols yourself.

ts
import { normalizeSymbol } from 'stock-sdk/symbols';

normalizeSymbol('sh600519');
// → { market: 'CN', exchange: 'SSE', assetType: 'stock', code: '600519', input: 'sh600519' }

normalizeSymbol('AAPL');
// → { market: 'US', exchange: 'US', assetType: 'stock', code: 'AAPL', input: 'AAPL' }

The returned NormalizedSymbol is the SDK's single internal representation:

ts
interface NormalizedSymbol {
  market: 'CN' | 'HK' | 'US' | 'GLOBAL';
  exchange: string;  // the strong discriminator
  assetType: 'stock' | 'index' | 'fund' | 'bond' | 'futures' | 'option' | 'board';
  code: string;      // pure code, no prefix
  variety?: string;  // futures variety (e.g. 'IF' / 'RB')
  input: string;     // original input, for errors and debugging
}

market denotes the trading region / system; exchange is the strong discriminator. For example, overseas futures fall under market: 'GLOBAL' and are distinguished by exchange (COMEX / NYMEX / CBOT / LME …).

Resolution priority chain (first match wins)

normalizeSymbol(input, hint?) tries the following rules in order and returns on the first match. When hint conflicts with SymbolRef fields, the explicit input (SymbolRef) wins.

OrderRuleExample
1Dotted form: Eastmoney secid / suffix / futures exchange / board1.600519, 600519.SH, CFFEX.IF2412, 90.BK0475
2Letter prefixsh600519, sz000001, bj430047, hk00700, usAAPL
3Special indices (by code shape / named, see below)930955, H30533, HSHCI, GDAXI
4Pure digits600519, 000001, 00700
5Bare futures contract with hintrb2510 (with assetType:'futures')
6Pure letters → US stockAAPL, MSFT
7Parse failure → throw InvalidSymbolError'', @@@

Per-market cheat sheet

MarketAccepted formsNormalized result
A-share (Shanghai)sh600519, 600519, 600519.SH, 1.600519CN / SSE / 600519
A-share (Shenzhen)sz000001, 000001, 000001.SZ, 0.000001CN / SZSE / 000001
A-share (Beijing)bj430047, 430047, 920819CN / BSE / 430047
Hong Kong00700, hk00700, 700, 00700.HK, 116.00700HK / HKEX / 00700
USAAPL, usAAPL, 105.AAPL, 106.BABAUS / US (or NASDAQ / NYSE / AMEX)
CN futuresrb2510 (with hint), CFFEX.IF2412CN / SHFE, CFFEX
Board90.BK0475CN / SSE / board
Special index930955, H30533, 2.930955, HSHCI, GDAXICN/CSI, HK/HSI, GLOBAL/DAX, assetType always index

Hong Kong codes shorter than 5 digits are zero-padded: 70000700, hk70000700. US tickers are upper-cased: aaplAAPL.

Special indices (CSI / Hang Seng / overseas)

Some indices use dedicated Eastmoney secid market prefixes (2 / 124 / 100) instead of exchange-based inference. The SDK recognizes them via code-shape rules plus a named registry, and assetType is always 'index':

FamilyCode shape / codesNormalized resultEastmoney secid
CSI indices (open family)93xxxx (e.g. 930955, 932000, 931071), H + 5 digits (e.g. H30533, H11136)CN / CSI2.<code>
Hang Seng Healthcare IndexHSHCIHK / HSI124.HSHCI
German DAX indexGDAXIGLOBAL / DAX100.GDAXI
ts
// CSI indices go through the CN pipeline (kline.cn / getHistoryKline just work)
await sdk.kline.cn('930955', { startDate: '20240101', endDate: '20240105' });
await sdk.kline.cn('H30533');

// HSHCI belongs to the HK market, use the HK pipeline
await sdk.kline.hk('HSHCI');

// The secid forms (2.930955 / 2.H30533 / 124.HSHCI / 100.GDAXI) are valid inputs too
normalizeSymbol('2.930955'); // → CN / CSI / index / 930955

Notes:

  • The code shape syntactically determines all three axes (market / exchange / assetType); conflicting hints throw InvalidSymbolError (e.g. kline.cn('HSHCI') — HSHCI belongs to the HK market, use kline.hk instead).
  • Special indices have no Tencent quote mapping — toTencentSymbol throws InvalidArgumentError; individual fund flow (fundFlow.individual) does not support special indices either.
  • The CSI family matches by code shape (an open set): a well-formed but nonexistent code (e.g. mistyping H30533 as H30553) parses successfully and surfaces as empty upstream data rather than a local parse error — consistent with every other code family (e.g. 600520).
  • GDAXI belongs to the GLOBAL market and has no formal kline entry point yet; as an escape hatch you can pass the raw secid via sdk.kline.us('100.GDAXI') (the response is labeled with the US model — currency: 'USD' and US/Eastern timezone — which is not accurate for DAX).

Pure-code inference and ambiguity

When you supply only a pure code (no prefix, no suffix, no hint), inference follows these defaults:

ShapeDefault inferenceExchange refinement
6 pure digitsA-share stockstarts with 6/5/9 → SSE; 0/3 → SZSE; 4/8 or the 92 range → BSE; the 93 range → CSI index (see “Special indices” above)
5 / 4 pure digitsHong Kong stockzero-padded to 5 digits
Pure lettersUS stock

Pure-code inference is "most-common-wins", so some cases simply cannot be told apart from the code alone and require a hint or SymbolRef when calling normalizeSymbol.

Known ambiguities that require a hint

① Fund vs. stock (overlapping code ranges)

On-exchange fund / ETF code ranges overlap with stock code ranges; a bare code can't distinguish them, so declare the asset type explicitly:

ts
// Without a hint this is treated as a stock
normalizeSymbol('510300', { assetType: 'fund' });
await sdk.quotes.fund(['510300']);

② Index vs. stock (both 6 digits)

For example, 000001 is both the Shanghai Composite Index and Ping An Bank. A bare code defaults to stock; to mean the index, add a hint:

ts
normalizeSymbol('000001', { assetType: 'index' });

③ A/B-share five-digit codes instead of Hong Kong

A 5-digit pure code defaults to Hong Kong. If you truly mean a 5-digit A/B-share code, specify the market explicitly:

ts
normalizeSymbol('90001', { market: 'CN' });

④ Overseas futures bare contracts

Futures with market: 'GLOBAL' must provide an explicit exchange; they will not default to a domestic exchange:

ts
normalizeSymbol('GC', { market: 'GLOBAL', exchange: 'COMEX' });
// Missing exchange throws InvalidSymbolError

Rule of thumb: prefer an explicit form (prefix / suffix / secid) whenever it expresses your intent (e.g. sh000001 for the Shanghai Composite). Use a hint only when the system genuinely lacks enough information.

Parse failure: InvalidSymbolError

Unparseable input throws InvalidSymbolError (a subclass of SdkError, with code INVALID_SYMBOL), carrying the original input for diagnosis:

ts
import { normalizeSymbol } from 'stock-sdk/symbols';
import { InvalidSymbolError } from 'stock-sdk/errors';

try {
  normalizeSymbol('');
} catch (e) {
  if (e instanceof InvalidSymbolError) {
    console.log(e.code); // 'INVALID_SYMBOL'
  }
}

See Errors & Retry for the full error system.

The type fields above are subject to the final implementation; the resolution priority and known ambiguities are stable conventions.