diff --git a/.gitignore b/.gitignore index 0c4b6b485..965f5d9df 100644 --- a/.gitignore +++ b/.gitignore @@ -37,11 +37,10 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json old_docs .superpowers +# superpowers plans/specs are local working artifacts — never version them. +# (astro-migration files already tracked on main stay tracked; gitignore does +# not affect already-tracked files.) docs/superpowers/* -!docs/superpowers/specs/ -!docs/superpowers/specs/** -!docs/superpowers/plans/ -!docs/superpowers/plans/** .claude/worktrees .worktrees diff --git a/astro.config.ts b/astro.config.ts index 4deb109e9..58b2ca730 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -18,6 +18,61 @@ import { prebuildSkills } from './src/integrations/prebuild-skills' const REGION = process.env['VITE_REGION'] ?? 'global' const SITE = process.env['VITE_SITE_HOSTNAME'] ?? 'https://open.longportapp.com' +// Dev-only reverse proxy for the TryIt debugger. Vite's built-in http-proxy +// returns 502 for these HTTPS upstreams under bun, so forward with fetch (which +// works). `/api-prod` → production gateway. +function apiDevProxy() { + const TARGETS: Record = { + '/api-prod': 'https://openapi.longbridge.com', + // Legacy default prefix used by when no baseUrl is passed. + '/api': 'https://openapi.longbridge.com', + } + return { + name: 'lb-api-dev-proxy', + apply: 'serve' as const, + configureServer(server: { middlewares: { use: (fn: (req: any, res: any, next: () => void) => void) => void } }) { + // Dev only: corporate networks MITM these HTTPS upstreams with a cert the + // dev process doesn't trust, so outbound fetch fails. Skip verification — + // this never runs in a production build (apply: 'serve'). + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0' + server.middlewares.use(async (req: any, res: any, next: () => void) => { + const url: string = req.url ?? '' + const prefix = Object.keys(TARGETS).find((p) => url === p || url.startsWith(p + '/')) + if (!prefix) return next() + const target = TARGETS[prefix] + const path = url.slice(prefix.length) || '/' + try { + const chunks: Buffer[] = [] + for await (const c of req) chunks.push(c as Buffer) + const method: string = (req.method ?? 'GET').toUpperCase() + const headers: Record = {} + for (const [k, v] of Object.entries(req.headers)) { + if (k === 'host' || k === 'connection' || k === 'content-length') continue + if (typeof v === 'string') headers[k] = v + } + const upstream = await fetch(target + path, { + method, + headers, + body: method === 'GET' || method === 'HEAD' || chunks.length === 0 ? undefined : Buffer.concat(chunks), + }) + res.statusCode = upstream.status + upstream.headers.forEach((value: string, key: string) => { + const k = key.toLowerCase() + // Body is already decoded by fetch — don't forward encoding/length. + if (k === 'content-encoding' || k === 'content-length' || k === 'transfer-encoding') return + res.setHeader(key, value) + }) + res.end(Buffer.from(await upstream.arrayBuffer())) + } catch (err) { + res.statusCode = 502 + res.setHeader('content-type', 'application/json; charset=utf-8') + res.end(JSON.stringify({ code: -1, msg: `dev proxy error: ${err instanceof Error ? err.message : String(err)}`, data: null })) + } + }) + }, + } +} + export default defineConfig({ site: SITE, // `assets: 'assets'` (default is `_astro`) so hashed CSS/JS land in /assets/ — @@ -44,6 +99,11 @@ export default defineConfig({ ], vite: { plugins: [ + // Dev proxy for the API Reference TryIt debugger: one prefix per + // environment so the 生产/测试 toggle can hit either backend without CORS. + // Implemented with fetch (not Vite's http-proxy, which 502s under bun for + // these HTTPS upstreams). Dev only — prod talks to the real domains. + apiDevProxy(), tailwind(), // Rename mdx frontmatter `layout:` → `docs_layout:` so astro-mdx // doesn't try to resolve values like "api-reference" as module diff --git a/bun.lock b/bun.lock index 0b8dfa6bf..74d846c90 100644 --- a/bun.lock +++ b/bun.lock @@ -65,11 +65,15 @@ "name": "@longbridge/openapi-api-reference", "version": "0.0.0", "dependencies": { + "@longbridge/openapi-tryit": "workspace:*", + "@longbridge/openapi-ui": "workspace:*", "js-yaml": "^4", "markdown-it": "^14", + "markdown-it-container": "^4.0.0", }, "devDependencies": { "@types/markdown-it": "^14", + "@types/markdown-it-container": "^4.0.1", }, }, "packages/homepage": { @@ -663,6 +667,8 @@ "@types/markdown-it": ["@types/markdown-it@14.2.0", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ=="], + "@types/markdown-it-container": ["@types/markdown-it-container@4.0.1", "", { "dependencies": { "@types/markdown-it": ">=14" } }, "sha512-lGoaXzFY51sfUiBZ+L9V7n/Ah2q/46G2MGERUXN/4Djq0aCFOEUxysC8xftAdf5EdnvKfb2QkQnGNMoaKwjI1Q=="], + "@types/mdast": ["@types/mdast@4.0.4", "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], @@ -1231,6 +1237,8 @@ "markdown-it": ["markdown-it@14.3.0", "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.3.0.tgz", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.5.0", "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw=="], + "markdown-it-container": ["markdown-it-container@4.0.0", "", {}, "sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw=="], + "markdown-table": ["markdown-table@3.0.4", "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.4.tgz", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], "marked": ["marked@15.0.12", "https://registry.yarnpkg.com/marked/-/marked-15.0.12.tgz", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], diff --git a/docs/en/docs/account/alert/update_alert.mdx b/docs/en/docs/account/alert/update_alert.mdx deleted file mode 100644 index b979309f8..000000000 --- a/docs/en/docs/account/alert/update_alert.mdx +++ /dev/null @@ -1,241 +0,0 @@ ---- -slug: update-alert -title: Update Alert -sidebar_position: 3 -language_tabs: false -toc_footers: [] -includes: [] -search: true -highlight_theme: '' -headingLevel: 2 ---- - -Enable or disable an existing price alert. First call `list` to obtain the full `AlertItem`, set `item.enabled` to `True` or `False`, then call `update(item)`. - - -# Enable an alert -longbridge alert enable 486469 -# Disable an alert -longbridge alert disable 486469 - - - - - -## Parameters - -> **SDK method parameters.** - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| id | int64 | YES | Alert ID (path parameter) | -| enabled | bool | YES | New enabled state: `true` to enable, `false` to disable — set on the `AlertItem` before calling `update` | - -## Request Example - - - - -```python -from longbridge.openapi import AlertContext, Config, OAuthBuilder - -oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) -config = Config.from_oauth(oauth) -ctx = AlertContext(config) - -# Get the alert from list() -alerts = ctx.list() -item = alerts.lists[0].indicators[0] # pick the alert you want -# Enable: set enabled=True then call update -item.enabled = True -ctx.update(item) -# Disable: set enabled=False then call update -item.enabled = False -ctx.update(item) -``` - - - - -```python -import asyncio -from longbridge.openapi import AsyncAlertContext, Config, OAuthBuilder - -async def main() -> None: - oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) - config = Config.from_oauth(oauth) - ctx = AsyncAlertContext.create(config) - - alerts = await ctx.list() - item = alerts.lists[0].indicators[0] - item.enabled = True # or False to disable - await ctx.update(item) - -if __name__ == "__main__": - asyncio.run(main()) -``` - - - - -```javascript -const { Config, AlertContext, OAuth } = require('longbridge') - -async function main() { - const oauth = await OAuth.build('your-client-id', (_, url) => { - console.log('Open this URL to authorize: ' + url) - }) - const config = Config.fromOAuth(oauth) - const ctx = AlertContext.new(config) - const alerts = await ctx.list() - const item = alerts.lists[0].indicators[0] - item.enabled = true // or false to disable - await ctx.update(item) - console.log(resp) -} -main().catch(console.error) -``` - - - - -```java -import com.longbridge.*; -import com.longbridge.alert.*; - -class Main { - public static void main(String[] args) throws Exception { - try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); - Config config = Config.fromOAuth(oauth); - AlertContext ctx = AlertContext.create(config)) { - var alerts = ctx.list().get(); - var item = alerts.getLists().get(0).getIndicators().get(0); - item.setEnabled(true); // or false to disable - ctx.update(item).get(); - System.out.println(resp); - } - } -} -``` - - - - -```rust -use std::sync::Arc; -use longbridge::{oauth::OAuthBuilder, alert::AlertContext, Config}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; - let config = Arc::new(Config::from_oauth(oauth)); - let ctx = AlertContext::new(config); - let mut item = ctx.list().await?.lists.remove(0).indicators.remove(0); - item.enabled = true; // or false to disable - ctx.update(&item).await?; - Ok(()) -} -``` - - - - -```cpp -#include -#include - -using namespace longbridge; -using namespace longbridge::alert; - -int main() { - OAuthBuilder("your-client-id").build( - [](const std::string& url) { std::cout << "Open: " << url << std::endl; }, - [](auto res) { - if (!res) return; - Config config = Config::from_oauth(*res); - AlertContext ctx = AlertContext::create(config); - ctx.list([&ctx](auto list_resp) { - auto& item = (*list_resp).lists[0].indicators[0]; - ctx.enable(item, [](auto resp) { - if (resp) std::cout << "OK" << std::endl; - }); - }); - std::cin.get(); -} -``` - - - - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/longbridge/openapi-go/config" - "github.com/longbridge/openapi-go/oauth" - "github.com/longbridge/openapi-go/alert" -) - -func main() { - o := oauth.New("your-client-id"). - OnOpenURL(func(url string) { fmt.Println("Open this URL to authorize:", url) }) - if err := o.Build(context.Background()); err != nil { - log.Fatal(err) - } - conf, err := config.New(config.WithOAuthClient(o)) - if err != nil { - log.Fatal(err) - } - c, err := alert.NewFromCfg(conf) - if err != nil { - log.Fatal(err) - } - defer c.Close() - list, err := c.List(context.Background()) - if err != nil { log.Fatal(err) } - item := list.Lists[0].Indicators[0] - item.Enabled = true // or false to disable - if err = c.Update(context.Background(), &item); err != nil { - log.Fatal(err) - } - if err != nil { - log.Fatal(err) - } - fmt.Printf("%+v\n", resp) -} -``` - - - - -## Response - - -### Response Example - -```json -{ - "code": 0, - "message": "success", - "data": {} -} -``` - -### Response Status - -| Status | Description | Schema | -| ------ | ----------- | ------ | -| 200 | Success | [UpdateAlertResponse](#UpdateAlertResponse) | -| 400 | Bad request | None | - -## Schemas - -### UpdateAlertResponse - - - -No response body fields. diff --git a/docs/en/docs/account/overview.mdx b/docs/en/docs/account/overview.mdx index de7308637..23d717a61 100644 --- a/docs/en/docs/account/overview.mdx +++ b/docs/en/docs/account/overview.mdx @@ -29,7 +29,6 @@ Create and manage price alerts for securities. |---|---| | [list_alerts](./alert/list-alerts) | List all active price alerts | | [create_alert](./alert/create-alert) | Create a new price alert | -| [update_alert](./alert/update-alert) | Update an existing alert | | [delete_alert](./alert/delete-alert) | Delete a price alert | ## DCAContext diff --git a/docs/en/docs/cli/fundamentals/industry-peers.mdx b/docs/en/docs/cli/fundamentals/industry-peers.mdx index 3d81fc9a0..edd219b59 100644 --- a/docs/en/docs/cli/fundamentals/industry-peers.mdx +++ b/docs/en/docs/cli/fundamentals/industry-peers.mdx @@ -6,12 +6,12 @@ sidebar_position: 17 # longbridge industry-peers -Explore the hierarchical sub-sector tree for an industry group. Takes a BK counter ID from [`industry-rank`](./industry-rank) and expands it into a full competitive landscape — sub-sectors, their sub-sectors, and the stocks in each. +Explore the hierarchical sub-sector tree for an industry group. Takes a BK symbol from [`industry-rank`](./industry-rank) and expands it into a full competitive landscape — sub-sectors, their sub-sectors, and the stocks in each. ## Basic Usage ```bash -longbridge industry-peers BK/US/IN00258 +longbridge industry-peers IN00258.US ``` ``` @@ -35,17 +35,17 @@ Root: Semiconductors (US) # Step 1: find a sector longbridge industry-rank --market US -# Step 2: drill into it using the counter_id column -longbridge industry-peers BK/US/IN00258 +# Step 2: drill into it using the symbol column +longbridge industry-peers IN00258.US ``` ### HK sector tree ```bash -longbridge industry-peers BK/HK/IN00012 +longbridge industry-peers IN00012.HK ``` -Works the same way across markets — use counter IDs from `industry-rank --market HK`, `--market CN`, or `--market SG`. +Works the same way across markets — use symbols from `industry-rank --market HK`, `--market CN`, or `--market SG`. ## Options @@ -55,5 +55,5 @@ Works the same way across markets — use counter IDs from `industry-rank --mark ## Notes -- Counter IDs follow the format `BK//IN` — copy them directly from `industry-rank` output +- Symbols follow the format `BK//IN` — copy them directly from `industry-rank` output - Each node shows stock count, daily change, and YTD change diff --git a/docs/en/docs/cli/fundamentals/industry-rank.mdx b/docs/en/docs/cli/fundamentals/industry-rank.mdx index fafcec6f5..1743c2718 100644 --- a/docs/en/docs/cli/fundamentals/industry-rank.mdx +++ b/docs/en/docs/cli/fundamentals/industry-rank.mdx @@ -15,16 +15,16 @@ longbridge industry-rank --market US ``` ``` -| rank | name | counter_id | chg | indicator | +| rank | name | symbol | chg | indicator | |------|-----------------------------------------|-----------------|--------|-----------| -| 1 | Semiconductors | BK/US/IN00258 | +3.82% | ... | -| 2 | Software - Infrastructure | BK/US/IN00305 | +2.91% | ... | -| 3 | Biotechnology | BK/US/IN00043 | +2.54% | ... | -| 4 | Electronic Components | BK/US/IN00099 | +1.98% | ... | -| 5 | Asset Management | BK/US/IN00033 | +1.73% | ... | +| 1 | Semiconductors | IN00258.US | +3.82% | ... | +| 2 | Software - Infrastructure | IN00305.US | +2.91% | ... | +| 3 | Biotechnology | IN00043.US | +2.54% | ... | +| 4 | Electronic Components | IN00099.US | +1.98% | ... | +| 5 | Asset Management | IN00033.US | +1.73% | ... | ``` -The `counter_id` column (e.g. `BK/US/IN00258`) can be passed directly to [`industry-peers`](./industry-peers) to explore the competitive tree within that sector. +The `symbol` column (e.g. `IN00258.US`) can be passed directly to [`industry-peers`](./industry-peers) to explore the competitive tree within that sector. ## Examples @@ -51,8 +51,8 @@ longbridge industry-rank --market CN --indicator revenue-growth ### Then drill into a sector ```bash -# Get the counter_id from industry-rank, then explore its sub-sectors -longbridge industry-peers BK/US/IN00258 +# Get the symbol from industry-rank, then explore its sub-sectors +longbridge industry-peers IN00258.US ``` ## Options diff --git a/docs/en/docs/cli/research/screener.mdx b/docs/en/docs/cli/research/screener.mdx index e761d792c..1a5a44227 100644 --- a/docs/en/docs/cli/research/screener.mdx +++ b/docs/en/docs/cli/research/screener.mdx @@ -82,7 +82,7 @@ Growth: **Step 2: Run a custom screen** ```bash -longbridge screener search --market HK --filter filter_marketcap:100:1000 --filter filter_divyld:3: +longbridge screener search --market HK --filter marketcap:100:1000 --filter divyld:3: ``` Filter format: `::`. Omit `min` or `max` to leave that side unbounded. diff --git a/docs/en/docs/fundamental/fundamental/industry_peers.mdx b/docs/en/docs/fundamental/fundamental/industry_peers.mdx index a1aca54f5..c158d081f 100644 --- a/docs/en/docs/fundamental/fundamental/industry_peers.mdx +++ b/docs/en/docs/fundamental/fundamental/industry_peers.mdx @@ -10,11 +10,11 @@ highlight_theme: '' headingLevel: 2 --- -Get the hierarchical sub-sector tree for an industry group, including stock count, daily change, and YTD change at each node. Counter IDs come from `industry_rank`. +Get the hierarchical sub-sector tree for an industry group, including stock count, daily change, and YTD change at each node. Symbols come from `industry_rank`. -longbridge industry-peers BK/US/IN00258 -longbridge industry-peers BK/HK/IN20337 +longbridge industry-peers IN00258.US +longbridge industry-peers IN20337.HK @@ -26,7 +26,7 @@ longbridge industry-peers BK/HK/IN20337 | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | -| counter_id | string | YES | Industry unique identifier (BK/MARKET/ID format) from `industry_rank` | +| symbol | string | YES | Industry unique identifier (BK/MARKET/ID format) from `industry_rank` | | market | string | YES | Market code: `US` / `HK` / `CN` / `SG` | ## Request Example @@ -41,7 +41,7 @@ oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) config = Config.from_oauth(oauth) ctx = FundamentalContext(config) -resp = ctx.industry_peers("BK/US/IN00258", "US") +resp = ctx.industry_peers("IN00258.US", "US") print(resp) ``` @@ -57,7 +57,7 @@ async def main() -> None: config = Config.from_oauth(oauth) ctx = AsyncFundamentalContext.create(config) - resp = await ctx.industry_peers("BK/US/IN00258", "US") + resp = await ctx.industry_peers("IN00258.US", "US") print(resp) if __name__ == "__main__": @@ -76,7 +76,7 @@ async function main() { }) const config = Config.fromOAuth(oauth) const ctx = FundamentalContext.new(config) - const resp = await ctx.industryPeers('BK/US/IN00258', 'US') + const resp = await ctx.industryPeers('IN00258.US', 'US') console.log(resp) } main().catch(console.error) @@ -94,7 +94,7 @@ class Main { try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); Config config = Config.fromOAuth(oauth); FundamentalContext ctx = FundamentalContext.create(config)) { - var resp = ctx.getIndustryPeers("BK/US/IN00258", "US").get(); + var resp = ctx.getIndustryPeers("IN00258.US", "US").get(); System.out.println(resp); } } @@ -113,7 +113,7 @@ async fn main() -> Result<(), Box> { let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; let config = Arc::new(Config::from_oauth(oauth)); let ctx = FundamentalContext::new(config); - let resp = ctx.industry_peers("BK/US/IN00258", "US").await?; + let resp = ctx.industry_peers("IN00258.US", "US").await?; println!("{:?}", resp); Ok(()) } @@ -136,7 +136,7 @@ int main() { if (!res) return; Config config = Config::from_oauth(*res); FundamentalContext ctx = FundamentalContext::create(config); - ctx.industry_peers("BK/US/IN00258", "US", [](auto resp) { + ctx.industry_peers("IN00258.US", "US", [](auto resp) { if (resp) std::cout << "OK" << std::endl; }); }); @@ -175,7 +175,7 @@ func main() { log.Fatal(err) } defer c.Close() - resp, err := c.IndustryPeers(context.Background(), "BK/US/IN00258", "US") + resp, err := c.IndustryPeers(context.Background(), "IN00258.US", "US") if err != nil { log.Fatal(err) } @@ -199,14 +199,14 @@ func main() { "top": {"name": "All Industries", "market": "US"}, "chain": { "name": "Technology", - "counter_id": "BK/US/IN00258", + "symbol": "IN00258.US", "stock_num": 542, "chg": "0.0231", "ytd_chg": "0.0875", "next": [ { "name": "在线消费电子产品零售", - "counter_id": "", + "symbol": "", "stock_num": 4, "chg": "0.0268", "ytd_chg": "-0.1869", @@ -252,7 +252,7 @@ func main() { | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | | name | string | false | Sector name | -| counter_id | string | false | Sector counter ID (present on root node; empty string on child nodes) | +| symbol | string | false | Sector symbol (present on root node; empty string on child nodes) | | stock_num | integer | false | Number of stocks in this sector | | chg | string | false | Daily change (decimal; may be empty string) | | ytd_chg | string | false | Year-to-date change (decimal; may be empty string) | diff --git a/docs/en/docs/fundamental/fundamental/industry_rank.mdx b/docs/en/docs/fundamental/fundamental/industry_rank.mdx index 93ac410bb..8db209b03 100644 --- a/docs/en/docs/fundamental/fundamental/industry_rank.mdx +++ b/docs/en/docs/fundamental/fundamental/industry_rank.mdx @@ -10,7 +10,7 @@ highlight_theme: '' headingLevel: 2 --- -Get the industry ranking list by market and indicator. The returned Counter ID can be passed directly to `industry_peers` to explore the sub-sector hierarchy. +Get the industry ranking list by market and indicator. The returned Symbol can be passed directly to `industry_peers` to explore the sub-sector hierarchy. longbridge industry-rank --market US @@ -203,7 +203,7 @@ func main() { "lists": [ { "name": "Technology", - "counter_id": "BK/US/IN00258", + "symbol": "IN00258.US", "chg": "0.0231", "leading_name": "NVIDIA", "leading_ticker": "NVDA.US", @@ -236,7 +236,7 @@ func main() { | items | object[] | false | Ranked group list | | ∟ lists | object[] | false | Industry item list | | ∟ ∟ name | string | false | Industry name | -| ∟ ∟ counter_id | string | false | Industry counter ID (`BK/MARKET/ID` format), usable in `industry_peers` | +| ∟ ∟ symbol | string | false | Industry symbol (`BK/MARKET/ID` format), usable in `industry_peers` | | ∟ ∟ chg | string | false | Daily change (decimal) | | ∟ ∟ leading_name | string | false | Leading stock name | | ∟ ∟ leading_ticker | string | false | Leading stock ticker | diff --git a/docs/en/docs/fundamental/fundamental/ratings.mdx b/docs/en/docs/fundamental/fundamental/ratings.mdx deleted file mode 100644 index 0894cebf5..000000000 --- a/docs/en/docs/fundamental/fundamental/ratings.mdx +++ /dev/null @@ -1,229 +0,0 @@ ---- -slug: ratings -title: Analyst Ratings -sidebar_position: 2 -language_tabs: false -toc_footers: [] -includes: [] -search: true -highlight_theme: '' -headingLevel: 2 ---- - -Get analyst institution ratings and consensus data for a security. - - - - -## Parameters - -> **SDK method parameters.** - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| symbol | string | YES | Security symbol, e.g. `AAPL.US` | - -## Request Example - - - - -```python -from longbridge.openapi import FundamentalContext, Config, OAuthBuilder - -oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) -config = Config.from_oauth(oauth) -ctx = FundamentalContext(config) - -resp = ctx.ratings("AAPL.US") -print(resp) -``` - - - - -```python -import asyncio -from longbridge.openapi import AsyncFundamentalContext, Config, OAuthBuilder - -async def main() -> None: - oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) - config = Config.from_oauth(oauth) - ctx = AsyncFundamentalContext.create(config) - - resp = await ctx.ratings("AAPL.US") - print(resp) - -if __name__ == "__main__": - asyncio.run(main()) -``` - - - - -```javascript -const { Config, FundamentalContext, OAuth } = require('longbridge') - -async function main() { - const oauth = await OAuth.build('your-client-id', (_, url) => { - console.log('Open this URL to authorize: ' + url) - }) - const config = Config.fromOAuth(oauth) - const ctx = FundamentalContext.new(config) - const resp = await ctx.ratings('AAPL.US') - console.log(resp) -} -main().catch(console.error) -``` - - - - -```java -import com.longbridge.*; -import com.longbridge.fundamental.*; - -class Main { - public static void main(String[] args) throws Exception { - try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); - Config config = Config.fromOAuth(oauth); - FundamentalContext ctx = FundamentalContext.create(config)) { - var resp = ctx.getRatings("AAPL.US").get(); - System.out.println(resp); - } - } -} -``` - - - - -```rust -use std::sync::Arc; -use longbridge::{oauth::OAuthBuilder, fundamental::FundamentalContext, Config}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; - let config = Arc::new(Config::from_oauth(oauth)); - let ctx = FundamentalContext::new(config); - let resp = ctx.ratings("AAPL.US").await?; - println!("{:?}", resp); - Ok(()) -} -``` - - - - -```cpp -#include -#include - -using namespace longbridge; -using namespace longbridge::fundamental; - -int main() { - OAuthBuilder("your-client-id").build( - [](const std::string& url) { std::cout << "Open: " << url << std::endl; }, - [](auto res) { - if (!res) return; - Config config = Config::from_oauth(*res); - FundamentalContext ctx = FundamentalContext::create(config); - ctx.ratings("AAPL.US", [](auto resp) { - if (resp) std::cout << "OK" << std::endl; - }); - }); - std::cin.get(); -} -``` - - - - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/longbridge/openapi-go/config" - "github.com/longbridge/openapi-go/oauth" - "github.com/longbridge/openapi-go/fundamental" -) - -func main() { - o := oauth.New("your-client-id"). - OnOpenURL(func(url string) { fmt.Println("Open this URL to authorize:", url) }) - if err := o.Build(context.Background()); err != nil { - log.Fatal(err) - } - conf, err := config.New(config.WithOAuthClient(o)) - if err != nil { - log.Fatal(err) - } - c, err := fundamental.NewFromCfg(conf) - if err != nil { - log.Fatal(err) - } - defer c.Close() - resp, err := c.Ratings(context.Background(), "AAPL.US") - if err != nil { - log.Fatal(err) - } - fmt.Printf("%+v\n", resp) -} -``` - - - - -## Response - - -### Response Example - -```json -{ - "code": 0, - "message": "success", - "data": { - "industry_name": "Technology Hardware, Storage and Peripherals", - "industry_rank": 2, - "multi_letter": "B", - "multi_score": "0.32", - "multi_score_change": -1, - "scale_txt_name": "Large", - "style_txt_name": "Blend", - "report_period_txt": "Rating based on Fiscal Year 2026 s.a.", - "ratings_json": "[]" - } -} -``` - -### Response Status - -| Status | Description | Schema | -| ------ | ----------- | ------ | -| 200 | Success | [StockRatingsResponse](#StockRatingsResponse) | -| 400 | Bad request | None | - -## Schemas - -### StockRatingsResponse - - - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| industry_name | string | false | Industry name | -| industry_rank | integer | false | Rank within industry | -| multi_letter | string | false | Rating letter grade | -| multi_score | string | false | Composite score | -| multi_score_change | integer | false | Score change vs previous period | -| report_period_txt | string | false | Report period description | -| scale_txt_name | string | false | Rating scale name | -| style_txt_name | string | false | Rating style name | -| ratings_json | string | false | Raw rating detail JSON | diff --git a/docs/en/docs/fundamental/fundamental/valuation_comparison.mdx b/docs/en/docs/fundamental/fundamental/valuation_comparison.mdx index f046aa16d..f895935b5 100644 --- a/docs/en/docs/fundamental/fundamental/valuation_comparison.mdx +++ b/docs/en/docs/fundamental/fundamental/valuation_comparison.mdx @@ -203,7 +203,7 @@ func main() { "data": { "list": [ { - "counter_id": "ST/US/AAPL", + "symbol": "AAPL.US", "name": "Apple Inc.", "currency": "USD", "market_value": "3241500000000", @@ -223,7 +223,7 @@ func main() { ] }, { - "counter_id": "ST/US/MSFT", + "symbol": "MSFT.US", "name": "Microsoft", "currency": "USD", "market_value": "3085000000000", @@ -262,7 +262,7 @@ func main() { | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | | list | object[] | false | Valuation comparison list | -| ∟ counter_id | string | false | Counter ID (e.g. `ST/US/AAPL`) | +| ∟ symbol | string | false | Symbol (e.g. `AAPL.US`) | | ∟ name | string | false | Security name | | ∟ currency | string | false | Currency of the values | | ∟ market_value | string | false | Market capitalisation | diff --git a/docs/en/docs/fundamental/overview.mdx b/docs/en/docs/fundamental/overview.mdx index ee3a954f1..75b5802a0 100644 --- a/docs/en/docs/fundamental/overview.mdx +++ b/docs/en/docs/fundamental/overview.mdx @@ -18,7 +18,6 @@ Company-level financial data and corporate information. | [company_profile](./fundamental/company-profile) | Company overview, industry, and key facts | | [financial_report](./fundamental/financial-report) | Income statement, balance sheet, and cash flow | | [valuations](./fundamental/valuations) | PE, PB, PS, EV/EBITDA and other valuation metrics | -| [ratings](./fundamental/ratings) | Analyst ratings and price targets | | [dividends](./fundamental/dividends) | Historical dividend records | | [fund_holdings](./fundamental/fund-holdings) | Institutional and fund ownership | | [shareholders](./fundamental/shareholders) | Major shareholders | diff --git a/docs/en/docs/getting-started.mdx b/docs/en/docs/getting-started.mdx index 3fedceeb8..bfcfe29cf 100644 --- a/docs/en/docs/getting-started.mdx +++ b/docs/en/docs/getting-started.mdx @@ -189,7 +189,7 @@ Let's take obtaining assets as an example to demonstrate how to use the SDK. Longbridge OpenAPI supports two authentication methods: -#### Method 1: OAuth 2.0 (Recommended) ⭐ +#### Method 1: OAuth 2.0 (Recommended) ⭐ {#oauth-2-0} OAuth 2.0 is the modern authentication method that uses Bearer tokens without requiring HMAC signatures, making it more secure and convenient. diff --git a/docs/en/docs/market/status/top_movers.mdx b/docs/en/docs/market/status/top_movers.mdx index 3588cf7ac..54fdbd3db 100644 --- a/docs/en/docs/market/status/top_movers.mdx +++ b/docs/en/docs/market/status/top_movers.mdx @@ -203,7 +203,7 @@ func main() { { "stock": { "code": "TSLA", - "counter_id": "ST/US/TSLA", + "symbol": "TSLA.US", "name": "特斯拉", "change": "-0.0388", "last_done": "404.110", @@ -243,7 +243,7 @@ func main() { | events | object[] | false | List of moving stocks | | ∟ stock | object | false | Basic stock information | | ∟ ∟ code | string | false | Ticker code (e.g. `TSLA`) | -| ∟ ∟ counter_id | string | false | Counter ID (e.g. `ST/US/TSLA`) | +| ∟ ∟ symbol | string | false | Symbol (e.g. `TSLA.US`) | | ∟ ∟ name | string | false | Security name | | ∟ ∟ change | string | false | Price change ratio (e.g. `-0.0388`) | | ∟ ∟ last_done | string | false | Latest trade price | diff --git a/docs/en/docs/screener/screener_search.mdx b/docs/en/docs/screener/screener_search.mdx index c3054b73e..256a5c5e0 100644 --- a/docs/en/docs/screener/screener_search.mdx +++ b/docs/en/docs/screener/screener_search.mdx @@ -18,7 +18,7 @@ Endpoint: `POST /v1/quote/ai/screener/search` longbridge screener search --strategy-id 42 -longbridge screener search --market HK --filter filter_marketcap:100:1000 +longbridge screener search --market HK --filter marketcap:100:1000 diff --git a/docs/en/docs/trade/grid/symbol_info.mdx b/docs/en/docs/trade/grid/symbol_info.mdx index 742eaf389..0f6f115f4 100644 --- a/docs/en/docs/trade/grid/symbol_info.mdx +++ b/docs/en/docs/trade/grid/symbol_info.mdx @@ -35,7 +35,7 @@ longbridge grid info 700.HK | Name | Type | Required | Description | | ---------- | ------ | -------- | ---------------------------------------------------------- | -| counter_id | string | YES | Security symbol, `ticker.region` format, example: `700.HK` | +| symbol | string | YES | Security symbol, `ticker.region` format, example: `700.HK` | ### Request Example diff --git a/docs/zh-CN/docs/account/alert/update_alert.mdx b/docs/zh-CN/docs/account/alert/update_alert.mdx deleted file mode 100644 index 5bb27993d..000000000 --- a/docs/zh-CN/docs/account/alert/update_alert.mdx +++ /dev/null @@ -1,214 +0,0 @@ ---- -slug: update-alert -title: 更新股价提醒 -sidebar_position: 3 -language_tabs: false -toc_footers: [] -includes: [] -search: true -highlight_theme: '' -headingLevel: 2 ---- - -启用或禁用已有的股价提醒。先通过 `list` 获取完整的 `AlertItem`,修改 `item.enabled` 后调用 `update(item)`。 - - -# 启用 -longbridge alert enable 486469 -# 禁用 -longbridge alert disable 486469 - - - - - -## Parameters - -> **SDK 方法参数。** - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| id | int64 | 是 | 提醒 ID(路径参数) | -| enabled | bool | 是 | 设为 `true` 启用,`false` 禁用 | - -## Request Example - - - - -```python -from longbridge.openapi import AlertContext, Config, OAuthBuilder - -oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) -config = Config.from_oauth(oauth) -ctx = AlertContext(config) - -resp = ctx.update_alert("112326", enabled=True) -``` - - - - -```python -import asyncio -from longbridge.openapi import AsyncAlertContext, Config, OAuthBuilder - -async def main() -> None: - oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) - config = Config.from_oauth(oauth) - ctx = AsyncAlertContext.create(config) - - resp = await ctx.update_alert("112326", enabled=True) - -if __name__ == "__main__": - asyncio.run(main()) -``` - - - - -```javascript -const { Config, AlertContext, OAuth } = require('longbridge') - -async function main() { - const oauth = await OAuth.build('your-client-id', (_, url) => { - console.log('Open this URL to authorize: ' + url) - }) - const config = Config.fromOAuth(oauth) - const ctx = AlertContext.new(config) - const resp = await ctx.update_alert() - console.log(resp) -} -main().catch(console.error) -``` - - - - -```java -import com.longbridge.*; -import com.longbridge.alert.*; - -class Main { - public static void main(String[] args) throws Exception { - try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); - Config config = Config.fromOAuth(oauth); - AlertContext ctx = AlertContext.create(config)) { - var resp = ctx.getUpdateAlert().get(); - System.out.println(resp); - } - } -} -``` - - - - -```rust -use std::sync::Arc; -use longbridge::{oauth::OAuthBuilder, alert::AlertContext, Config}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; - let config = Arc::new(Config::from_oauth(oauth)); - let ctx = AlertContext::new(config); - let resp = ctx.update_alert().await?; - Ok(()) -} -``` - - - - -```cpp -#include -#include - -using namespace longbridge; -using namespace longbridge::alert; - -int main() { - OAuthBuilder("your-client-id").build( - [](const std::string& url) { std::cout << "Open: " << url << std::endl; }, - [](auto res) { - if (!res) return; - Config config = Config::from_oauth(*res); - AlertContext ctx = AlertContext::create(config); - ctx.update_alert([](auto resp) { - if (resp) std::cout << "OK" << std::endl; - }); - }); - std::cin.get(); -} -``` - - - - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/longbridge/openapi-go/config" - "github.com/longbridge/openapi-go/oauth" - "github.com/longbridge/openapi-go/alert" -) - -func main() { - o := oauth.New("your-client-id"). - OnOpenURL(func(url string) { fmt.Println("Open this URL to authorize:", url) }) - if err := o.Build(context.Background()); err != nil { - log.Fatal(err) - } - conf, err := config.New(config.WithOAuthClient(o)) - if err != nil { - log.Fatal(err) - } - c, err := alert.NewFromCfg(conf) - if err != nil { - log.Fatal(err) - } - defer c.Close() - resp, err := c.UpdateAlert(context.Background()) - if err != nil { - log.Fatal(err) - } - fmt.Printf("%+v\n", resp) -} -``` - - - - -## Response - - -### Response Example - -```json -{ - "code": 0, - "message": "success", - "data": {} -} -``` - -### Response Status - -| Status | Description | Schema | -| ------ | ----------- | ------ | -| 200 | 成功 | [UpdateAlertResponse](#UpdateAlertResponse) | -| 400 | 请求错误 | None | - -## Schemas - -### UpdateAlertResponse - - - -无响应体字段。 diff --git a/docs/zh-CN/docs/account/overview.mdx b/docs/zh-CN/docs/account/overview.mdx index 92ae105d1..7de67c9b8 100644 --- a/docs/zh-CN/docs/account/overview.mdx +++ b/docs/zh-CN/docs/account/overview.mdx @@ -29,7 +29,6 @@ slug: overview |---|---| | [list_alerts](./alert/list-alerts) | 查看所有有效的股价提醒 | | [create_alert](./alert/create-alert) | 创建新的股价提醒 | -| [update_alert](./alert/update-alert) | 修改已有提醒 | | [delete_alert](./alert/delete-alert) | 删除股价提醒 | ## DCAContext diff --git a/docs/zh-CN/docs/cli/fundamentals/industry-peers.mdx b/docs/zh-CN/docs/cli/fundamentals/industry-peers.mdx index 1f4fe8484..3b712e645 100644 --- a/docs/zh-CN/docs/cli/fundamentals/industry-peers.mdx +++ b/docs/zh-CN/docs/cli/fundamentals/industry-peers.mdx @@ -11,7 +11,7 @@ sidebar_position: 17 ## 基本用法 ```bash -longbridge industry-peers BK/US/IN00258 +longbridge industry-peers IN00258.US ``` ``` @@ -35,14 +35,14 @@ Root: Semiconductors (US) # 第一步:查找板块 longbridge industry-rank --market US -# 第二步:使用 counter_id 列深入探索 -longbridge industry-peers BK/US/IN00258 +# 第二步:使用 symbol 列深入探索 +longbridge industry-peers IN00258.US ``` ### 港股板块树形结构 ```bash -longbridge industry-peers BK/HK/IN00012 +longbridge industry-peers IN00012.HK ``` 在不同市场下使用方式相同——从 `industry-rank --market HK`、`--market CN` 或 `--market SG` 获取计数器 ID 即可。 diff --git a/docs/zh-CN/docs/cli/fundamentals/industry-rank.mdx b/docs/zh-CN/docs/cli/fundamentals/industry-rank.mdx index dfd9d3764..248176d45 100644 --- a/docs/zh-CN/docs/cli/fundamentals/industry-rank.mdx +++ b/docs/zh-CN/docs/cli/fundamentals/industry-rank.mdx @@ -15,16 +15,16 @@ longbridge industry-rank --market US ``` ``` -| rank | name | counter_id | chg | indicator | +| rank | name | symbol | chg | indicator | |------|-----------------------------------------|-----------------|--------|-----------| -| 1 | Semiconductors | BK/US/IN00258 | +3.82% | ... | -| 2 | Software - Infrastructure | BK/US/IN00305 | +2.91% | ... | -| 3 | Biotechnology | BK/US/IN00043 | +2.54% | ... | -| 4 | Electronic Components | BK/US/IN00099 | +1.98% | ... | -| 5 | Asset Management | BK/US/IN00033 | +1.73% | ... | +| 1 | Semiconductors | IN00258.US | +3.82% | ... | +| 2 | Software - Infrastructure | IN00305.US | +2.91% | ... | +| 3 | Biotechnology | IN00043.US | +2.54% | ... | +| 4 | Electronic Components | IN00099.US | +1.98% | ... | +| 5 | Asset Management | IN00033.US | +1.73% | ... | ``` -`counter_id` 列(如 `BK/US/IN00258`)可直接传给 [`industry-peers`](./industry-peers),展开该板块的完整竞争树。 +`symbol` 列(如 `IN00258.US`)可直接传给 [`industry-peers`](./industry-peers),展开该板块的完整竞争树。 ## 示例 @@ -51,8 +51,8 @@ longbridge industry-rank --market CN --indicator revenue-growth ### 进一步下探某板块 ```bash -# 从 industry-rank 获取 counter_id,再探索其子板块 -longbridge industry-peers BK/US/IN00258 +# 从 industry-rank 获取 symbol,再探索其子板块 +longbridge industry-peers IN00258.US ``` ## 选项 diff --git a/docs/zh-CN/docs/cli/research/screener.mdx b/docs/zh-CN/docs/cli/research/screener.mdx index 8ca35d27a..e54bfdad6 100644 --- a/docs/zh-CN/docs/cli/research/screener.mdx +++ b/docs/zh-CN/docs/cli/research/screener.mdx @@ -82,7 +82,7 @@ Growth: **第二步:执行自定义筛选** ```bash -longbridge screener search --market HK --filter filter_marketcap:100:1000 --filter filter_divyld:3: +longbridge screener search --market HK --filter marketcap:100:1000 --filter divyld:3: ``` 指标格式:`::`,省略 `min` 或 `max` 表示不限下/上限。 diff --git a/docs/zh-CN/docs/fundamental/fundamental/industry_peers.mdx b/docs/zh-CN/docs/fundamental/fundamental/industry_peers.mdx index 41403f674..03fc08245 100644 --- a/docs/zh-CN/docs/fundamental/fundamental/industry_peers.mdx +++ b/docs/zh-CN/docs/fundamental/fundamental/industry_peers.mdx @@ -10,11 +10,11 @@ highlight_theme: '' headingLevel: 2 --- -获取行业分组的层级子板块树,含各节点股票数量、日涨跌幅和年初至今涨跌幅。Counter ID 可从 `industry_rank` 返回结果中获取。 +获取行业分组的层级子板块树,含各节点股票数量、日涨跌幅和年初至今涨跌幅。Symbol 可从 `industry_rank` 返回结果中获取。 -longbridge industry-peers BK/US/IN00258 -longbridge industry-peers BK/HK/IN20337 +longbridge industry-peers IN00258.US +longbridge industry-peers IN20337.HK @@ -26,7 +26,7 @@ longbridge industry-peers BK/HK/IN20337 | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | -| counter_id | string | 是 | 行业唯一标识(BK/市场/ID 格式),来源于 `industry_rank` | +| symbol | string | 是 | 行业标识,来源于 `industry_rank` | | market | string | 是 | 市场代码:`US` / `HK` / `CN` / `SG` | ## Request Example @@ -41,7 +41,7 @@ oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) config = Config.from_oauth(oauth) ctx = FundamentalContext(config) -resp = ctx.industry_peers("BK/US/IN00258", "US") +resp = ctx.industry_peers("IN00258.US", "US") print(resp) ``` @@ -57,7 +57,7 @@ async def main() -> None: config = Config.from_oauth(oauth) ctx = AsyncFundamentalContext.create(config) - resp = await ctx.industry_peers("BK/US/IN00258", "US") + resp = await ctx.industry_peers("IN00258.US", "US") print(resp) if __name__ == "__main__": @@ -76,7 +76,7 @@ async function main() { }) const config = Config.fromOAuth(oauth) const ctx = FundamentalContext.new(config) - const resp = await ctx.industryPeers('BK/US/IN00258', 'US') + const resp = await ctx.industryPeers('IN00258.US', 'US') console.log(resp) } main().catch(console.error) @@ -94,7 +94,7 @@ class Main { try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); Config config = Config.fromOAuth(oauth); FundamentalContext ctx = FundamentalContext.create(config)) { - var resp = ctx.getIndustryPeers("BK/US/IN00258", "US").get(); + var resp = ctx.getIndustryPeers("IN00258.US", "US").get(); System.out.println(resp); } } @@ -113,7 +113,7 @@ async fn main() -> Result<(), Box> { let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; let config = Arc::new(Config::from_oauth(oauth)); let ctx = FundamentalContext::new(config); - let resp = ctx.industry_peers("BK/US/IN00258", "US").await?; + let resp = ctx.industry_peers("IN00258.US", "US").await?; println!("{:?}", resp); Ok(()) } @@ -136,7 +136,7 @@ int main() { if (!res) return; Config config = Config::from_oauth(*res); FundamentalContext ctx = FundamentalContext::create(config); - ctx.industry_peers("BK/US/IN00258", "US", [](auto resp) { + ctx.industry_peers("IN00258.US", "US", [](auto resp) { if (resp) std::cout << "OK" << std::endl; }); }); @@ -175,7 +175,7 @@ func main() { log.Fatal(err) } defer c.Close() - resp, err := c.IndustryPeers(context.Background(), "BK/US/IN00258", "US") + resp, err := c.IndustryPeers(context.Background(), "IN00258.US", "US") if err != nil { log.Fatal(err) } @@ -199,14 +199,14 @@ func main() { "top": {"name": "All Industries", "market": "US"}, "chain": { "name": "Technology", - "counter_id": "BK/US/IN00258", + "symbol": "IN00258.US", "stock_num": 542, "chg": "0.0231", "ytd_chg": "0.0875", "next": [ { "name": "在线消费电子产品零售", - "counter_id": "", + "symbol": "", "stock_num": 4, "chg": "0.0268", "ytd_chg": "-0.1869", @@ -252,7 +252,7 @@ func main() { | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | | name | string | 否 | 板块名称 | -| counter_id | string | 否 | 板块唯一标识(根节点有值,子节点为空字符串) | +| symbol | string | 否 | 板块唯一标识(根节点有值,子节点为空字符串) | | stock_num | integer | 否 | 板块内股票数量 | | chg | string | 否 | 当日涨跌幅(小数,可能为空字符串) | | ytd_chg | string | 否 | 年初至今涨跌幅(小数,可能为空字符串) | diff --git a/docs/zh-CN/docs/fundamental/fundamental/industry_rank.mdx b/docs/zh-CN/docs/fundamental/fundamental/industry_rank.mdx index 604eaee77..901b26013 100644 --- a/docs/zh-CN/docs/fundamental/fundamental/industry_rank.mdx +++ b/docs/zh-CN/docs/fundamental/fundamental/industry_rank.mdx @@ -10,7 +10,7 @@ highlight_theme: '' headingLevel: 2 --- -按市场和指标获取行业排行榜。返回的 Counter ID 可直接传入 `industry_peers` 查询子行业树。 +按市场和指标获取行业排行榜。返回的 Symbol 可直接传入 `industry_peers` 查询子行业树。 longbridge industry-rank --market US @@ -203,7 +203,7 @@ func main() { "lists": [ { "name": "Technology", - "counter_id": "BK/US/IN00258", + "symbol": "IN00258.US", "chg": "0.0231", "leading_name": "NVIDIA", "leading_ticker": "NVDA.US", @@ -236,7 +236,7 @@ func main() { | items | object[] | 否 | 排行分组列表 | | ∟ lists | object[] | 否 | 行业条目列表 | | ∟ ∟ name | string | 否 | 行业名称 | -| ∟ ∟ counter_id | string | 否 | 行业唯一标识(`BK/市场/ID` 格式),可直接传入 `industry_peers` | +| ∟ ∟ symbol | string | 否 | 行业标识,可直接传入 `industry_peers` | | ∟ ∟ chg | string | 否 | 当日涨跌幅(小数) | | ∟ ∟ leading_name | string | 否 | 涨幅领先个股名称 | | ∟ ∟ leading_ticker | string | 否 | 涨幅领先个股代码 | diff --git a/docs/zh-CN/docs/fundamental/fundamental/ratings.mdx b/docs/zh-CN/docs/fundamental/fundamental/ratings.mdx deleted file mode 100644 index aaa2a823a..000000000 --- a/docs/zh-CN/docs/fundamental/fundamental/ratings.mdx +++ /dev/null @@ -1,229 +0,0 @@ ---- -slug: ratings -title: 分析师评级 -sidebar_position: 2 -language_tabs: false -toc_footers: [] -includes: [] -search: true -highlight_theme: '' -headingLevel: 2 ---- - -获取指定证券的机构分析师评级和一致预期数据。 - - - - -## Parameters - -> **SDK 方法参数。** - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| symbol | string | 是 | 证券代码,例如 `AAPL.US` | - -## Request Example - - - - -```python -from longbridge.openapi import FundamentalContext, Config, OAuthBuilder - -oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) -config = Config.from_oauth(oauth) -ctx = FundamentalContext(config) - -resp = ctx.ratings("AAPL.US") -print(resp) -``` - - - - -```python -import asyncio -from longbridge.openapi import AsyncFundamentalContext, Config, OAuthBuilder - -async def main() -> None: - oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) - config = Config.from_oauth(oauth) - ctx = AsyncFundamentalContext.create(config) - - resp = await ctx.ratings("AAPL.US") - print(resp) - -if __name__ == "__main__": - asyncio.run(main()) -``` - - - - -```javascript -const { Config, FundamentalContext, OAuth } = require('longbridge') - -async function main() { - const oauth = await OAuth.build('your-client-id', (_, url) => { - console.log('Open this URL to authorize: ' + url) - }) - const config = Config.fromOAuth(oauth) - const ctx = FundamentalContext.new(config) - const resp = await ctx.ratings('AAPL.US') - console.log(resp) -} -main().catch(console.error) -``` - - - - -```java -import com.longbridge.*; -import com.longbridge.fundamental.*; - -class Main { - public static void main(String[] args) throws Exception { - try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); - Config config = Config.fromOAuth(oauth); - FundamentalContext ctx = FundamentalContext.create(config)) { - var resp = ctx.getRatings("AAPL.US").get(); - System.out.println(resp); - } - } -} -``` - - - - -```rust -use std::sync::Arc; -use longbridge::{oauth::OAuthBuilder, fundamental::FundamentalContext, Config}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; - let config = Arc::new(Config::from_oauth(oauth)); - let ctx = FundamentalContext::new(config); - let resp = ctx.ratings("AAPL.US").await?; - println!("{:?}", resp); - Ok(()) -} -``` - - - - -```cpp -#include -#include - -using namespace longbridge; -using namespace longbridge::fundamental; - -int main() { - OAuthBuilder("your-client-id").build( - [](const std::string& url) { std::cout << "Open: " << url << std::endl; }, - [](auto res) { - if (!res) return; - Config config = Config::from_oauth(*res); - FundamentalContext ctx = FundamentalContext::create(config); - ctx.ratings("AAPL.US", [](auto resp) { - if (resp) std::cout << "OK" << std::endl; - }); - }); - std::cin.get(); -} -``` - - - - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/longbridge/openapi-go/config" - "github.com/longbridge/openapi-go/oauth" - "github.com/longbridge/openapi-go/fundamental" -) - -func main() { - o := oauth.New("your-client-id"). - OnOpenURL(func(url string) { fmt.Println("Open this URL to authorize:", url) }) - if err := o.Build(context.Background()); err != nil { - log.Fatal(err) - } - conf, err := config.New(config.WithOAuthClient(o)) - if err != nil { - log.Fatal(err) - } - c, err := fundamental.NewFromCfg(conf) - if err != nil { - log.Fatal(err) - } - defer c.Close() - resp, err := c.Ratings(context.Background(), "AAPL.US") - if err != nil { - log.Fatal(err) - } - fmt.Printf("%+v\n", resp) -} -``` - - - - -## Response - - -### Response Example - -```json -{ - "code": 0, - "message": "success", - "data": { - "industry_name": "Technology Hardware, Storage and Peripherals", - "industry_rank": 2, - "multi_letter": "B", - "multi_score": "0.32", - "multi_score_change": -1, - "scale_txt_name": "Large", - "style_txt_name": "Blend", - "report_period_txt": "Rating based on Fiscal Year 2026 s.a.", - "ratings_json": "[]" - } -} -``` - -### Response Status - -| Status | Description | Schema | -| ------ | ----------- | ------ | -| 200 | 成功 | [StockRatingsResponse](#StockRatingsResponse) | -| 400 | 请求错误 | None | - -## Schemas - -### StockRatingsResponse - - - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| industry_name | string | 否 | 行业名称 | -| industry_rank | integer | 否 | 行业内排名 | -| multi_letter | string | 否 | 评级字母等级 | -| multi_score | string | 否 | 综合评分 | -| multi_score_change | integer | 否 | 评分变化 | -| report_period_txt | string | 否 | 报告期描述 | -| scale_txt_name | string | 否 | 评级量表名称 | -| style_txt_name | string | 否 | 评级风格名称 | -| ratings_json | string | 否 | 原始评级详情 JSON | diff --git a/docs/zh-CN/docs/fundamental/fundamental/valuation_comparison.mdx b/docs/zh-CN/docs/fundamental/fundamental/valuation_comparison.mdx index 0a9eef416..0b958b342 100644 --- a/docs/zh-CN/docs/fundamental/fundamental/valuation_comparison.mdx +++ b/docs/zh-CN/docs/fundamental/fundamental/valuation_comparison.mdx @@ -203,7 +203,7 @@ func main() { "data": { "list": [ { - "counter_id": "ST/US/AAPL", + "symbol": "AAPL.US", "name": "苹果公司", "currency": "USD", "market_value": "3241500000000", @@ -223,7 +223,7 @@ func main() { ] }, { - "counter_id": "ST/US/MSFT", + "symbol": "MSFT.US", "name": "微软", "currency": "USD", "market_value": "3085000000000", @@ -262,7 +262,7 @@ func main() { | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | | list | object[] | false | 股票估值对比列表 | -| ∟ counter_id | string | false | Counter ID(如 `ST/US/AAPL`) | +| ∟ symbol | string | false | Symbol(如 `AAPL.US`) | | ∟ name | string | false | 证券名称 | | ∟ currency | string | false | 数值所用货币 | | ∟ market_value | string | false | 市值 | diff --git a/docs/zh-CN/docs/fundamental/overview.mdx b/docs/zh-CN/docs/fundamental/overview.mdx index dd976dafd..d1341c917 100644 --- a/docs/zh-CN/docs/fundamental/overview.mdx +++ b/docs/zh-CN/docs/fundamental/overview.mdx @@ -18,7 +18,6 @@ slug: overview | [company_profile](./fundamental/company-profile) | 公司概况、行业及基本信息 | | [financial_report](./fundamental/financial-report) | 利润表、资产负债表和现金流量表 | | [valuations](./fundamental/valuations) | PE、PB、PS、EV/EBITDA 等估值指标 | -| [ratings](./fundamental/ratings) | 机构评级与目标价 | | [dividends](./fundamental/dividends) | 历史分红记录 | | [fund_holdings](./fundamental/fund-holdings) | 机构及基金持仓 | | [shareholders](./fundamental/shareholders) | 主要股东 | diff --git a/docs/zh-CN/docs/getting-started.mdx b/docs/zh-CN/docs/getting-started.mdx index 663a17c76..721ca89e8 100644 --- a/docs/zh-CN/docs/getting-started.mdx +++ b/docs/zh-CN/docs/getting-started.mdx @@ -202,7 +202,7 @@ go get github.com/longbridge/openapi-go Longbridge Developers 支持两种认证方式: -#### 方式一:OAuth 2.0(推荐) ⭐ +#### 方式一:OAuth 2.0(推荐) ⭐ {#oauth-2-0} OAuth 2.0 是现代化的认证方式,使用 Bearer Token,无需 HMAC 签名,更加安全便捷。 diff --git a/docs/zh-CN/docs/market/status/top_movers.mdx b/docs/zh-CN/docs/market/status/top_movers.mdx index b5f41c1cf..64d89e338 100644 --- a/docs/zh-CN/docs/market/status/top_movers.mdx +++ b/docs/zh-CN/docs/market/status/top_movers.mdx @@ -203,7 +203,7 @@ func main() { { "stock": { "code": "TSLA", - "counter_id": "ST/US/TSLA", + "symbol": "TSLA.US", "name": "特斯拉", "change": "-0.0388", "last_done": "404.110", @@ -243,7 +243,7 @@ func main() { | events | object[] | false | 异动股票列表 | | ∟ stock | object | false | 股票基本信息 | | ∟ ∟ code | string | false | 股票代码(如 `TSLA`) | -| ∟ ∟ counter_id | string | false | Counter ID(如 `ST/US/TSLA`) | +| ∟ ∟ symbol | string | false | Symbol(如 `TSLA.US`) | | ∟ ∟ name | string | false | 证券名称 | | ∟ ∟ change | string | false | 涨跌幅(如 `-0.0388`) | | ∟ ∟ last_done | string | false | 最新成交价 | diff --git a/docs/zh-CN/docs/screener/screener_search.mdx b/docs/zh-CN/docs/screener/screener_search.mdx index 262f8b426..a30741843 100644 --- a/docs/zh-CN/docs/screener/screener_search.mdx +++ b/docs/zh-CN/docs/screener/screener_search.mdx @@ -18,7 +18,7 @@ headingLevel: 2 longbridge screener search --strategy-id 42 -longbridge screener search --market HK --filter filter_marketcap:100:1000 +longbridge screener search --market HK --filter marketcap:100:1000 diff --git a/docs/zh-CN/docs/trade/grid/symbol_info.mdx b/docs/zh-CN/docs/trade/grid/symbol_info.mdx index b3a5adc5a..b93334015 100644 --- a/docs/zh-CN/docs/trade/grid/symbol_info.mdx +++ b/docs/zh-CN/docs/trade/grid/symbol_info.mdx @@ -31,7 +31,7 @@ longbridge grid info 700.HK | 名称 | 类型 | 必填 | 说明 | | ---------- | ------ | ---- | --------------------------------------------------- | -| counter_id | string | 是 | 标的代码,`ticker.region` 格式,例如:`700.HK` | +| symbol | string | 是 | 标的代码,`ticker.region` 格式,例如:`700.HK` | ### 请求示例 diff --git a/docs/zh-HK/docs/account/alert/update_alert.mdx b/docs/zh-HK/docs/account/alert/update_alert.mdx deleted file mode 100644 index 3489f4216..000000000 --- a/docs/zh-HK/docs/account/alert/update_alert.mdx +++ /dev/null @@ -1,214 +0,0 @@ ---- -slug: update-alert -title: 更新股價提醒 -sidebar_position: 3 -language_tabs: false -toc_footers: [] -includes: [] -search: true -highlight_theme: '' -headingLevel: 2 ---- - -啟用或禁用已有的股價提醒。 - - -# 啟用 -longbridge alert enable 486469 -# 停用 -longbridge alert disable 486469 - - - - - -## Parameters - -> **SDK 方法參數。** - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| id | int64 | 是 | 提醒 ID(路徑參數) | -| enabled | bool | 是 | 設為 `true` 啟用,`false` 禁用 | - -## Request Example - - - - -```python -from longbridge.openapi import AlertContext, Config, OAuthBuilder - -oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) -config = Config.from_oauth(oauth) -ctx = AlertContext(config) - -resp = ctx.update_alert("112326", enabled=True) -``` - - - - -```python -import asyncio -from longbridge.openapi import AsyncAlertContext, Config, OAuthBuilder - -async def main() -> None: - oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) - config = Config.from_oauth(oauth) - ctx = AsyncAlertContext.create(config) - - resp = await ctx.update_alert("112326", enabled=True) - -if __name__ == "__main__": - asyncio.run(main()) -``` - - - - -```javascript -const { Config, AlertContext, OAuth } = require('longbridge') - -async function main() { - const oauth = await OAuth.build('your-client-id', (_, url) => { - console.log('Open this URL to authorize: ' + url) - }) - const config = Config.fromOAuth(oauth) - const ctx = AlertContext.new(config) - const resp = await ctx.update_alert() - console.log(resp) -} -main().catch(console.error) -``` - - - - -```java -import com.longbridge.*; -import com.longbridge.alert.*; - -class Main { - public static void main(String[] args) throws Exception { - try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); - Config config = Config.fromOAuth(oauth); - AlertContext ctx = AlertContext.create(config)) { - var resp = ctx.getUpdateAlert().get(); - System.out.println(resp); - } - } -} -``` - - - - -```rust -use std::sync::Arc; -use longbridge::{oauth::OAuthBuilder, alert::AlertContext, Config}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; - let config = Arc::new(Config::from_oauth(oauth)); - let ctx = AlertContext::new(config); - let resp = ctx.update_alert().await?; - Ok(()) -} -``` - - - - -```cpp -#include -#include - -using namespace longbridge; -using namespace longbridge::alert; - -int main() { - OAuthBuilder("your-client-id").build( - [](const std::string& url) { std::cout << "Open: " << url << std::endl; }, - [](auto res) { - if (!res) return; - Config config = Config::from_oauth(*res); - AlertContext ctx = AlertContext::create(config); - ctx.update_alert([](auto resp) { - if (resp) std::cout << "OK" << std::endl; - }); - }); - std::cin.get(); -} -``` - - - - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/longbridge/openapi-go/config" - "github.com/longbridge/openapi-go/oauth" - "github.com/longbridge/openapi-go/alert" -) - -func main() { - o := oauth.New("your-client-id"). - OnOpenURL(func(url string) { fmt.Println("Open this URL to authorize:", url) }) - if err := o.Build(context.Background()); err != nil { - log.Fatal(err) - } - conf, err := config.New(config.WithOAuthClient(o)) - if err != nil { - log.Fatal(err) - } - c, err := alert.NewFromCfg(conf) - if err != nil { - log.Fatal(err) - } - defer c.Close() - resp, err := c.UpdateAlert(context.Background()) - if err != nil { - log.Fatal(err) - } - fmt.Printf("%+v\n", resp) -} -``` - - - - -## Response - - -### Response Example - -```json -{ - "code": 0, - "message": "success", - "data": {} -} -``` - -### Response Status - -| Status | Description | Schema | -| ------ | ----------- | ------ | -| 200 | 成功 | [UpdateAlertResponse](#UpdateAlertResponse) | -| 400 | 請求錯誤 | None | - -## Schemas - -### UpdateAlertResponse - - - -無響應體字段。 diff --git a/docs/zh-HK/docs/account/overview.mdx b/docs/zh-HK/docs/account/overview.mdx index 8261e6b94..3c1e53876 100644 --- a/docs/zh-HK/docs/account/overview.mdx +++ b/docs/zh-HK/docs/account/overview.mdx @@ -29,7 +29,6 @@ slug: overview |---|---| | [list_alerts](./alert/list-alerts) | 查看所有有效的股價提醒 | | [create_alert](./alert/create-alert) | 建立新的股價提醒 | -| [update_alert](./alert/update-alert) | 修改已有提醒 | | [delete_alert](./alert/delete-alert) | 刪除股價提醒 | ## DCAContext diff --git a/docs/zh-HK/docs/cli/fundamentals/industry-peers.mdx b/docs/zh-HK/docs/cli/fundamentals/industry-peers.mdx index e0f19c0dd..b478ce5be 100644 --- a/docs/zh-HK/docs/cli/fundamentals/industry-peers.mdx +++ b/docs/zh-HK/docs/cli/fundamentals/industry-peers.mdx @@ -11,7 +11,7 @@ sidebar_position: 17 ## 基本用法 ```bash -longbridge industry-peers BK/US/IN00258 +longbridge industry-peers IN00258.US ``` ``` @@ -35,14 +35,14 @@ Root: Semiconductors (US) # 第一步:查找板塊 longbridge industry-rank --market US -# 第二步:使用 counter_id 列深入探索 -longbridge industry-peers BK/US/IN00258 +# 第二步:使用 symbol 列深入探索 +longbridge industry-peers IN00258.US ``` ### 港股板塊樹形結構 ```bash -longbridge industry-peers BK/HK/IN00012 +longbridge industry-peers IN00012.HK ``` 在不同市場下使用方式相同——從 `industry-rank --market HK`、`--market CN` 或 `--market SG` 獲取計數器 ID 即可。 diff --git a/docs/zh-HK/docs/cli/fundamentals/industry-rank.mdx b/docs/zh-HK/docs/cli/fundamentals/industry-rank.mdx index c0d3e36d5..129042925 100644 --- a/docs/zh-HK/docs/cli/fundamentals/industry-rank.mdx +++ b/docs/zh-HK/docs/cli/fundamentals/industry-rank.mdx @@ -15,16 +15,16 @@ longbridge industry-rank --market US ``` ``` -| rank | name | counter_id | chg | indicator | +| rank | name | symbol | chg | indicator | |------|-----------------------------------------|-----------------|--------|-----------| -| 1 | Semiconductors | BK/US/IN00258 | +3.82% | ... | -| 2 | Software - Infrastructure | BK/US/IN00305 | +2.91% | ... | -| 3 | Biotechnology | BK/US/IN00043 | +2.54% | ... | -| 4 | Electronic Components | BK/US/IN00099 | +1.98% | ... | -| 5 | Asset Management | BK/US/IN00033 | +1.73% | ... | +| 1 | Semiconductors | IN00258.US | +3.82% | ... | +| 2 | Software - Infrastructure | IN00305.US | +2.91% | ... | +| 3 | Biotechnology | IN00043.US | +2.54% | ... | +| 4 | Electronic Components | IN00099.US | +1.98% | ... | +| 5 | Asset Management | IN00033.US | +1.73% | ... | ``` -`counter_id` 列(如 `BK/US/IN00258`)可直接傳給 [`industry-peers`](./industry-peers),展開該板塊的完整競爭樹。 +`symbol` 列(如 `IN00258.US`)可直接傳給 [`industry-peers`](./industry-peers),展開該板塊的完整競爭樹。 ## 示例 @@ -51,8 +51,8 @@ longbridge industry-rank --market CN --indicator revenue-growth ### 進一步下探某板塊 ```bash -# 從 industry-rank 獲取 counter_id,再探索其子板塊 -longbridge industry-peers BK/US/IN00258 +# 從 industry-rank 獲取 symbol,再探索其子板塊 +longbridge industry-peers IN00258.US ``` ## 選項 diff --git a/docs/zh-HK/docs/cli/research/screener.mdx b/docs/zh-HK/docs/cli/research/screener.mdx index 1a6c3753e..3af11bf53 100644 --- a/docs/zh-HK/docs/cli/research/screener.mdx +++ b/docs/zh-HK/docs/cli/research/screener.mdx @@ -82,7 +82,7 @@ Growth: **第二步:執行自訂篩選** ```bash -longbridge screener search --market HK --filter filter_marketcap:100:1000 --filter filter_divyld:3: +longbridge screener search --market HK --filter marketcap:100:1000 --filter divyld:3: ``` 指標格式:`::`,省略 `min` 或 `max` 表示不限下/上限。 diff --git a/docs/zh-HK/docs/fundamental/fundamental/industry_peers.mdx b/docs/zh-HK/docs/fundamental/fundamental/industry_peers.mdx index 7012084b0..1547b441c 100644 --- a/docs/zh-HK/docs/fundamental/fundamental/industry_peers.mdx +++ b/docs/zh-HK/docs/fundamental/fundamental/industry_peers.mdx @@ -10,11 +10,11 @@ highlight_theme: '' headingLevel: 2 --- -獲取行業分組的層級子板塊樹,含各節點股票數量、日漲跌幅和年初至今漲跌幅。Counter ID 可從 `industry_rank` 返回結果中獲取。 +獲取行業分組的層級子板塊樹,含各節點股票數量、日漲跌幅和年初至今漲跌幅。Symbol 可從 `industry_rank` 返回結果中獲取。 -longbridge industry-peers BK/US/IN00258 -longbridge industry-peers BK/HK/IN20337 +longbridge industry-peers IN00258.US +longbridge industry-peers IN20337.HK @@ -26,7 +26,7 @@ longbridge industry-peers BK/HK/IN20337 | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | -| counter_id | string | 是 | 行業唯一標識(BK/市場/ID 格式),來源於 `industry_rank` | +| symbol | string | 是 | 行業標識,來源於 `industry_rank` | | market | string | 是 | 市場代碼:`US` / `HK` / `CN` / `SG` | ## Request Example @@ -41,7 +41,7 @@ oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) config = Config.from_oauth(oauth) ctx = FundamentalContext(config) -resp = ctx.industry_peers("BK/US/IN00258", "US") +resp = ctx.industry_peers("IN00258.US", "US") print(resp) ``` @@ -57,7 +57,7 @@ async def main() -> None: config = Config.from_oauth(oauth) ctx = AsyncFundamentalContext.create(config) - resp = await ctx.industry_peers("BK/US/IN00258", "US") + resp = await ctx.industry_peers("IN00258.US", "US") print(resp) if __name__ == "__main__": @@ -76,7 +76,7 @@ async function main() { }) const config = Config.fromOAuth(oauth) const ctx = FundamentalContext.new(config) - const resp = await ctx.industryPeers('BK/US/IN00258', 'US') + const resp = await ctx.industryPeers('IN00258.US', 'US') console.log(resp) } main().catch(console.error) @@ -94,7 +94,7 @@ class Main { try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); Config config = Config.fromOAuth(oauth); FundamentalContext ctx = FundamentalContext.create(config)) { - var resp = ctx.getIndustryPeers("BK/US/IN00258", "US").get(); + var resp = ctx.getIndustryPeers("IN00258.US", "US").get(); System.out.println(resp); } } @@ -113,7 +113,7 @@ async fn main() -> Result<(), Box> { let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; let config = Arc::new(Config::from_oauth(oauth)); let ctx = FundamentalContext::new(config); - let resp = ctx.industry_peers("BK/US/IN00258", "US").await?; + let resp = ctx.industry_peers("IN00258.US", "US").await?; println!("{:?}", resp); Ok(()) } @@ -136,7 +136,7 @@ int main() { if (!res) return; Config config = Config::from_oauth(*res); FundamentalContext ctx = FundamentalContext::create(config); - ctx.industry_peers("BK/US/IN00258", "US", [](auto resp) { + ctx.industry_peers("IN00258.US", "US", [](auto resp) { if (resp) std::cout << "OK" << std::endl; }); }); @@ -175,7 +175,7 @@ func main() { log.Fatal(err) } defer c.Close() - resp, err := c.IndustryPeers(context.Background(), "BK/US/IN00258", "US") + resp, err := c.IndustryPeers(context.Background(), "IN00258.US", "US") if err != nil { log.Fatal(err) } @@ -199,14 +199,14 @@ func main() { "top": {"name": "All Industries", "market": "US"}, "chain": { "name": "Technology", - "counter_id": "BK/US/IN00258", + "symbol": "IN00258.US", "stock_num": 542, "chg": "0.0231", "ytd_chg": "0.0875", "next": [ { "name": "在线消费电子产品零售", - "counter_id": "", + "symbol": "", "stock_num": 4, "chg": "0.0268", "ytd_chg": "-0.1869", @@ -252,7 +252,7 @@ func main() { | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | | name | string | 否 | 板塊名稱 | -| counter_id | string | 否 | 板塊唯一標識(根節點有值,子節點為空字串) | +| symbol | string | 否 | 板塊唯一標識(根節點有值,子節點為空字串) | | stock_num | integer | 否 | 板塊內股票數量 | | chg | string | 否 | 當日漲跌幅(小數,可能為空字串) | | ytd_chg | string | 否 | 年初至今漲跌幅(小數,可能為空字串) | diff --git a/docs/zh-HK/docs/fundamental/fundamental/industry_rank.mdx b/docs/zh-HK/docs/fundamental/fundamental/industry_rank.mdx index 6dabcf309..ce192c4e5 100644 --- a/docs/zh-HK/docs/fundamental/fundamental/industry_rank.mdx +++ b/docs/zh-HK/docs/fundamental/fundamental/industry_rank.mdx @@ -10,7 +10,7 @@ highlight_theme: '' headingLevel: 2 --- -按市場和指標獲取行業排行榜。返回的 Counter ID 可直接傳入 `industry_peers` 查詢子行業樹。 +按市場和指標獲取行業排行榜。返回的 Symbol 可直接傳入 `industry_peers` 查詢子行業樹。 longbridge industry-rank --market US @@ -203,7 +203,7 @@ func main() { "lists": [ { "name": "Technology", - "counter_id": "BK/US/IN00258", + "symbol": "IN00258.US", "chg": "0.0231", "leading_name": "NVIDIA", "leading_ticker": "NVDA.US", @@ -236,7 +236,7 @@ func main() { | items | object[] | 否 | 排行分組列表 | | ∟ lists | object[] | 否 | 行業條目列表 | | ∟ ∟ name | string | 否 | 行業名稱 | -| ∟ ∟ counter_id | string | 否 | 行業唯一標識(`BK/市場/ID` 格式),可直接傳入 `industry_peers` | +| ∟ ∟ symbol | string | 否 | 行業標識,可直接傳入 `industry_peers` | | ∟ ∟ chg | string | 否 | 當日漲跌幅(小數) | | ∟ ∟ leading_name | string | 否 | 漲幅領先個股名稱 | | ∟ ∟ leading_ticker | string | 否 | 漲幅領先個股代碼 | diff --git a/docs/zh-HK/docs/fundamental/fundamental/ratings.mdx b/docs/zh-HK/docs/fundamental/fundamental/ratings.mdx deleted file mode 100644 index 4a07afae6..000000000 --- a/docs/zh-HK/docs/fundamental/fundamental/ratings.mdx +++ /dev/null @@ -1,229 +0,0 @@ ---- -slug: ratings -title: 分析師評級 -sidebar_position: 2 -language_tabs: false -toc_footers: [] -includes: [] -search: true -highlight_theme: '' -headingLevel: 2 ---- - -獲取指定證券的機構分析師評級和一致預期數據。 - - - - -## Parameters - -> **SDK 方法參數。** - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| symbol | string | 是 | 證券代碼,例如 `AAPL.US` | - -## Request Example - - - - -```python -from longbridge.openapi import FundamentalContext, Config, OAuthBuilder - -oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) -config = Config.from_oauth(oauth) -ctx = FundamentalContext(config) - -resp = ctx.ratings("AAPL.US") -print(resp) -``` - - - - -```python -import asyncio -from longbridge.openapi import AsyncFundamentalContext, Config, OAuthBuilder - -async def main() -> None: - oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) - config = Config.from_oauth(oauth) - ctx = AsyncFundamentalContext.create(config) - - resp = await ctx.ratings("AAPL.US") - print(resp) - -if __name__ == "__main__": - asyncio.run(main()) -``` - - - - -```javascript -const { Config, FundamentalContext, OAuth } = require('longbridge') - -async function main() { - const oauth = await OAuth.build('your-client-id', (_, url) => { - console.log('Open this URL to authorize: ' + url) - }) - const config = Config.fromOAuth(oauth) - const ctx = FundamentalContext.new(config) - const resp = await ctx.ratings('AAPL.US') - console.log(resp) -} -main().catch(console.error) -``` - - - - -```java -import com.longbridge.*; -import com.longbridge.fundamental.*; - -class Main { - public static void main(String[] args) throws Exception { - try (OAuth oauth = new OAuthBuilder("your-client-id").build(url -> System.out.println("Open to authorize: " + url)).get(); - Config config = Config.fromOAuth(oauth); - FundamentalContext ctx = FundamentalContext.create(config)) { - var resp = ctx.getRatings("AAPL.US").get(); - System.out.println(resp); - } - } -} -``` - - - - -```rust -use std::sync::Arc; -use longbridge::{oauth::OAuthBuilder, fundamental::FundamentalContext, Config}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open: {url}")).await?; - let config = Arc::new(Config::from_oauth(oauth)); - let ctx = FundamentalContext::new(config); - let resp = ctx.ratings("AAPL.US").await?; - println!("{:?}", resp); - Ok(()) -} -``` - - - - -```cpp -#include -#include - -using namespace longbridge; -using namespace longbridge::fundamental; - -int main() { - OAuthBuilder("your-client-id").build( - [](const std::string& url) { std::cout << "Open: " << url << std::endl; }, - [](auto res) { - if (!res) return; - Config config = Config::from_oauth(*res); - FundamentalContext ctx = FundamentalContext::create(config); - ctx.ratings("AAPL.US", [](auto resp) { - if (resp) std::cout << "OK" << std::endl; - }); - }); - std::cin.get(); -} -``` - - - - -```go -package main - -import ( - "context" - "fmt" - "log" - - "github.com/longbridge/openapi-go/config" - "github.com/longbridge/openapi-go/oauth" - "github.com/longbridge/openapi-go/fundamental" -) - -func main() { - o := oauth.New("your-client-id"). - OnOpenURL(func(url string) { fmt.Println("Open this URL to authorize:", url) }) - if err := o.Build(context.Background()); err != nil { - log.Fatal(err) - } - conf, err := config.New(config.WithOAuthClient(o)) - if err != nil { - log.Fatal(err) - } - c, err := fundamental.NewFromCfg(conf) - if err != nil { - log.Fatal(err) - } - defer c.Close() - resp, err := c.Ratings(context.Background(), "AAPL.US") - if err != nil { - log.Fatal(err) - } - fmt.Printf("%+v\n", resp) -} -``` - - - - -## Response - - -### Response Example - -```json -{ - "code": 0, - "message": "success", - "data": { - "industry_name": "Technology Hardware, Storage and Peripherals", - "industry_rank": 2, - "multi_letter": "B", - "multi_score": "0.32", - "multi_score_change": -1, - "scale_txt_name": "Large", - "style_txt_name": "Blend", - "report_period_txt": "Rating based on Fiscal Year 2026 s.a.", - "ratings_json": "[]" - } -} -``` - -### Response Status - -| Status | Description | Schema | -| ------ | ----------- | ------ | -| 200 | 成功 | [StockRatingsResponse](#StockRatingsResponse) | -| 400 | 請求錯誤 | None | - -## Schemas - -### StockRatingsResponse - - - -| Name | Type | Required | Description | -| ---- | ---- | -------- | ----------- | -| industry_name | string | 否 | 行業名稱 | -| industry_rank | integer | 否 | 行業內排名 | -| multi_letter | string | 否 | 評級字母等級 | -| multi_score | string | 否 | 綜合評分 | -| multi_score_change | integer | 否 | 評分變化 | -| report_period_txt | string | 否 | 報告期描述 | -| scale_txt_name | string | 否 | 評級量表名稱 | -| style_txt_name | string | 否 | 評級風格名稱 | -| ratings_json | string | 否 | 原始評級詳情 JSON | diff --git a/docs/zh-HK/docs/fundamental/fundamental/valuation_comparison.mdx b/docs/zh-HK/docs/fundamental/fundamental/valuation_comparison.mdx index 63a8252e0..b674a8e97 100644 --- a/docs/zh-HK/docs/fundamental/fundamental/valuation_comparison.mdx +++ b/docs/zh-HK/docs/fundamental/fundamental/valuation_comparison.mdx @@ -203,7 +203,7 @@ func main() { "data": { "list": [ { - "counter_id": "ST/US/AAPL", + "symbol": "AAPL.US", "name": "蘋果公司", "currency": "USD", "market_value": "3241500000000", @@ -223,7 +223,7 @@ func main() { ] }, { - "counter_id": "ST/US/MSFT", + "symbol": "MSFT.US", "name": "微軟", "currency": "USD", "market_value": "3085000000000", @@ -262,7 +262,7 @@ func main() { | Name | Type | Required | Description | | ---- | ---- | -------- | ----------- | | list | object[] | false | 股票估值對比列表 | -| ∟ counter_id | string | false | Counter ID(如 `ST/US/AAPL`) | +| ∟ symbol | string | false | Symbol(如 `AAPL.US`) | | ∟ name | string | false | 證券名稱 | | ∟ currency | string | false | 數值所用貨幣 | | ∟ market_value | string | false | 市值 | diff --git a/docs/zh-HK/docs/fundamental/overview.mdx b/docs/zh-HK/docs/fundamental/overview.mdx index bdf928c87..df3213a07 100644 --- a/docs/zh-HK/docs/fundamental/overview.mdx +++ b/docs/zh-HK/docs/fundamental/overview.mdx @@ -18,7 +18,6 @@ slug: overview | [company_profile](./fundamental/company-profile) | 公司概況、行业及基本信息 | | [financial_report](./fundamental/financial-report) | 利润表、资产负债表和现金流量表 | | [valuations](./fundamental/valuations) | PE、PB、PS、EV/EBITDA 等估值指标 | -| [ratings](./fundamental/ratings) | 機構评级与目标价 | | [dividends](./fundamental/dividends) | 歷史分红记录 | | [fund_holdings](./fundamental/fund-holdings) | 機構及基金持仓 | | [shareholders](./fundamental/shareholders) | 主要股东 | diff --git a/docs/zh-HK/docs/getting-started.mdx b/docs/zh-HK/docs/getting-started.mdx index 662030735..f3f332cad 100644 --- a/docs/zh-HK/docs/getting-started.mdx +++ b/docs/zh-HK/docs/getting-started.mdx @@ -191,7 +191,7 @@ go get github.com/longbridge/openapi-go Longbridge Developers 支援兩種認證方式: -#### 方式一:OAuth 2.0(推薦) ⭐ +#### 方式一:OAuth 2.0(推薦) ⭐ {#oauth-2-0} OAuth 2.0 是現代化的認證方式,使用 Bearer Token,無需 HMAC 簽名,更加安全便捷。 diff --git a/docs/zh-HK/docs/market/status/top_movers.mdx b/docs/zh-HK/docs/market/status/top_movers.mdx index 6d4d945dc..b8e979978 100644 --- a/docs/zh-HK/docs/market/status/top_movers.mdx +++ b/docs/zh-HK/docs/market/status/top_movers.mdx @@ -203,7 +203,7 @@ func main() { { "stock": { "code": "TSLA", - "counter_id": "ST/US/TSLA", + "symbol": "TSLA.US", "name": "特斯拉", "change": "-0.0388", "last_done": "404.110", @@ -243,7 +243,7 @@ func main() { | events | object[] | false | 異動股票列表 | | ∟ stock | object | false | 股票基本信息 | | ∟ ∟ code | string | false | 股票代碼(如 `TSLA`) | -| ∟ ∟ counter_id | string | false | Counter ID(如 `ST/US/TSLA`) | +| ∟ ∟ symbol | string | false | Symbol(如 `TSLA.US`) | | ∟ ∟ name | string | false | 證券名稱 | | ∟ ∟ change | string | false | 漲跌幅(如 `-0.0388`) | | ∟ ∟ last_done | string | false | 最新成交價 | diff --git a/docs/zh-HK/docs/screener/screener_search.mdx b/docs/zh-HK/docs/screener/screener_search.mdx index 4587844dc..2ac85a546 100644 --- a/docs/zh-HK/docs/screener/screener_search.mdx +++ b/docs/zh-HK/docs/screener/screener_search.mdx @@ -18,7 +18,7 @@ headingLevel: 2 longbridge screener search --strategy-id 42 -longbridge screener search --market HK --filter filter_marketcap:100:1000 +longbridge screener search --market HK --filter marketcap:100:1000 diff --git a/docs/zh-HK/docs/trade/grid/symbol_info.mdx b/docs/zh-HK/docs/trade/grid/symbol_info.mdx index 294158c02..6fb4ee098 100644 --- a/docs/zh-HK/docs/trade/grid/symbol_info.mdx +++ b/docs/zh-HK/docs/trade/grid/symbol_info.mdx @@ -35,7 +35,7 @@ longbridge grid info 700.HK | 名稱 | 類型 | 必填 | 說明 | | ---------- | ------ | ---- | --------------------------------------------------- | -| counter_id | string | 是 | 標的代碼,`ticker.region` 格式,例如:`700.HK` | +| symbol | string | 是 | 標的代碼,`ticker.region` 格式,例如:`700.HK` | ### 請求示例 diff --git a/openapi.yaml b/openapi.yaml index 58bafe055..e73a4fcb4 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -6,9 +6,350 @@ info: watchlist management, and content queries for the Longbridge trading platform. version: '1.0.0' x-pages: + - id: authentication + title: Authentication + x-title-zh: 鉴权 + x-title-zh-hk: 鑑權 + x-icon: lock + content: | + Longbridge OpenAPI authenticates every HTTP request with three credentials plus an + HMAC-SHA256 signature. + + ## Credentials + + | Credential | Description | Where to get it | + | ---------- | ----------- | --------------- | + | **App Key** | Public application identifier. Sent as the `X-Api-Key` header. | [OpenAPI console](https://open.longbridge.com/account) | + | **App Secret** | Private key used only to compute the request signature. **Never sent over the wire.** | OpenAPI console | + | **Access Token** | Per-user token. Sent as the `Authorization` header. | OpenAPI console (or the token-refresh API) | + + > Prefer an SDK: the Rust/Python/Node.js/Java/C++/Go SDKs compute the signature for you + > from these three values. The raw-HTTP examples below are for building a client by hand. + + ## Request headers + + Every request carries these headers: + + | Header | Value | + | ------ | ----- | + | `Authorization` | `` (the access token, not a Bearer token) | + | `X-Api-Key` | `` | + | `X-Timestamp` | Current unix time in **seconds** | + | `X-Api-Signature` | `HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature=` | + + ## Computing `X-Api-Signature` + + 1. Build the signed-headers string: + + ```text + authorization: + x-api-key: + x-timestamp: + ``` + + 2. Build the string to sign (`|`-separated), where `sha1(...)` is a lowercase hex SHA-1 + digest and the trailing body hash is present only when there is a request body: + + ```text + {METHOD}|{PATH}|{QUERY}|{signed_values}|authorization;x-api-key;x-timestamp|{sha1(body)} + ``` + + 3. Sign it: + + ```text + payload = "HMAC-SHA256|" + sha1(string_to_sign) + signature = hex( HMAC_SHA256(payload, App Secret) ) + ``` + + 4. Send `X-Api-Signature: HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature={signature}`. + + ### Reference signing implementation + + Compute the four API-Key headers from your credentials. Other languages follow the same steps. + + [[SIGNING_TABS]] + + > The per-endpoint **Request Example** tabs default to **OAuth 2.0** (`Authorization: Bearer `). + > To use API-Key auth instead, drop the `Authorization: Bearer …` header and add the four signed + > headers returned by `sign(...)` above. + x-content-zh: | + Longbridge OpenAPI 的每个 HTTP 请求都用三项凭证 + 一个 HMAC-SHA256 签名进行鉴权。 + + ## 凭证 + + | 凭证 | 说明 | 获取方式 | + | ---- | ---- | -------- | + | **App Key** | 应用公开标识,作为 `X-Api-Key` 头发送。 | [OpenAPI 控制台](https://open.longbridge.com/account) | + | **App Secret** | 仅用于计算请求签名的私钥,**不会在网络上传输**。 | OpenAPI 控制台 | + | **Access Token** | 用户级令牌,作为 `Authorization` 头发送。 | OpenAPI 控制台(或令牌刷新接口) | + + > 建议直接用 SDK:Rust/Python/Node.js/Java/C++/Go SDK 会用这三项凭证自动计算签名。下面的 raw HTTP 示例仅用于手写客户端。 + + ## 请求头 + + | 头 | 值 | + | -- | -- | + | `Authorization` | ``(直接是 access token,不是 Bearer token) | + | `X-Api-Key` | `` | + | `X-Timestamp` | 当前 unix 时间(**毫秒**) | + | `X-Api-Signature` | `HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature=` | + + ## 计算 `X-Api-Signature` + + 1. 构造 signed-values: + + ```text + authorization: + x-api-key: + x-timestamp: + ``` + + 2. 构造待签名字符串(`|` 分隔,`sha1(...)` 为小写十六进制 SHA-1;末尾 body 哈希仅在有请求体时出现): + + ```text + {METHOD}|{PATH}|{QUERY}|{signed_values}|authorization;x-api-key;x-timestamp|{sha1(body)} + ``` + + 3. 签名: + + ```text + payload = "HMAC-SHA256|" + sha1(string_to_sign) + signature = hex( HMAC_SHA256(payload, App Secret) ) + ``` + + 4. 发送 `X-Api-Signature: HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature={signature}`. + + ### 签名参考实现 + + 用凭证计算四个 API-Key 头,其他语言遵循相同步骤。 + + [[SIGNING_TABS]] + + > 各接口的 **请求示例** 默认用 **OAuth 2.0**(`Authorization: Bearer `)。若改用 API Key 鉴权,去掉 `Authorization: Bearer …`,改为附上 `sign(...)` 返回的四个签名头。 + x-content-zh-hk: | + Longbridge OpenAPI 的每個 HTTP 請求都用三項憑證 + 一個 HMAC-SHA256 簽名進行鑑權。 + + ## 憑證 + + | 憑證 | 說明 | 獲取方式 | + | ---- | ---- | -------- | + | **App Key** | 應用公開標識,作為 `X-Api-Key` 標頭發送。 | [OpenAPI 控制台](https://open.longbridge.com/account) | + | **App Secret** | 僅用於計算請求簽名的私鑰,**不會在網絡上傳輸**。 | OpenAPI 控制台 | + | **Access Token** | 用戶級令牌,作為 `Authorization` 標頭發送。 | OpenAPI 控制台(或令牌刷新接口) | + + > 建議直接用 SDK:Rust/Python/Node.js/Java/C++/Go SDK 會用這三項憑證自動計算簽名。下面的 raw HTTP 示例僅用於手寫客戶端。 + + ## 請求標頭 + + | 標頭 | 值 | + | -- | -- | + | `Authorization` | ``(直接是 access token,不是 Bearer token) | + | `X-Api-Key` | `` | + | `X-Timestamp` | 當前 unix 時間(**毫秒**) | + | `X-Api-Signature` | `HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature=` | + + ## 計算 `X-Api-Signature` + + 1. 構造 signed-values: + + ```text + authorization: + x-api-key: + x-timestamp: + ``` + + 2. 構造待簽名字符串(`|` 分隔,`sha1(...)` 為小寫十六進制 SHA-1;末尾 body 哈希僅在有請求體時出現): + + ```text + {METHOD}|{PATH}|{QUERY}|{signed_values}|authorization;x-api-key;x-timestamp|{sha1(body)} + ``` + + 3. 簽名: + + ```text + payload = "HMAC-SHA256|" + sha1(string_to_sign) + signature = hex( HMAC_SHA256(payload, App Secret) ) + ``` + + 4. 發送 `X-Api-Signature: HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature={signature}`. + + ### 簽名參考實現 + + 用憑證計算四個 API-Key 標頭,其他語言遵循相同步驟。 + + [[SIGNING_TABS]] + + > 各接口的 **請求示例** 預設用 **OAuth 2.0**(`Authorization: Bearer `)。若改用 API Key 鑑權,去掉 `Authorization: Bearer …`,改為附上 `sign(...)` 返回的四個簽名標頭。 + x-code-tabs: + - label: cURL + lang: Shell + source: | + #!/usr/bin/env bash + # Requires openssl. Fill in APP_KEY / APP_SECRET / ACCESS_TOKEN. + METHOD="GET"; REQ_PATH="/v1/dailycoins/query"; QUERY=""; BODY="" + TS="$(date +%s)" + SIGNED_HEADERS="authorization;x-api-key;x-timestamp" + SIGNED_VALUES="authorization:${ACCESS_TOKEN} + x-api-key:${APP_KEY} + x-timestamp:${TS} + " + CANON="${METHOD}|${REQ_PATH}|${QUERY}|${SIGNED_VALUES}|${SIGNED_HEADERS}|" + [ -n "$BODY" ] && CANON="${CANON}$(printf %s "$BODY" | openssl dgst -sha1 | awk '{print $2}')" + PAYLOAD="HMAC-SHA256|$(printf %s "$CANON" | openssl dgst -sha1 | awk '{print $2}')" + SIG="$(printf %s "$PAYLOAD" | openssl dgst -sha256 -hmac "$APP_SECRET" | awk '{print $2}')" + + curl --request "$METHOD" \ + --url "https://openapi.longbridge.com${REQ_PATH}${QUERY:+?$QUERY}" \ + --header "Authorization: ${ACCESS_TOKEN}" \ + --header "X-Api-Key: ${APP_KEY}" \ + --header "X-Timestamp: ${TS}" \ + --header "X-Api-Signature: HMAC-SHA256 SignedHeaders=${SIGNED_HEADERS}, Signature=${SIG}" + - label: Python + lang: Python + source: | + import hashlib, hmac, time + + def sign(method, path, query="", body="", *, app_key, app_secret, access_token): + ts = str(int(time.time())) + signed_headers = "authorization;x-api-key;x-timestamp" + signed_values = f"authorization:{access_token}\nx-api-key:{app_key}\nx-timestamp:{ts}\n" + canonical = f"{method}|{path}|{query}|{signed_values}|{signed_headers}|" + if body: + canonical += hashlib.sha1(body.encode()).hexdigest() + payload = "HMAC-SHA256|" + hashlib.sha1(canonical.encode()).hexdigest() + sig = hmac.new(app_secret.encode(), payload.encode(), hashlib.sha256).hexdigest() + return { + "Authorization": access_token, + "X-Api-Key": app_key, + "X-Timestamp": ts, + "X-Api-Signature": f"HMAC-SHA256 SignedHeaders={signed_headers}, Signature={sig}", + } + - label: Node.js + lang: JavaScript + source: | + const crypto = require('crypto') + + function sign(method, path, query = '', body = '', { appKey, appSecret, accessToken }) { + const ts = String(Math.floor(Date.now() / 1000)) + const signedHeaders = 'authorization;x-api-key;x-timestamp' + const signedValues = `authorization:${accessToken}\nx-api-key:${appKey}\nx-timestamp:${ts}\n` + let canonical = `${method}|${path}|${query}|${signedValues}|${signedHeaders}|` + if (body) canonical += crypto.createHash('sha1').update(body).digest('hex') + const payload = 'HMAC-SHA256|' + crypto.createHash('sha1').update(canonical).digest('hex') + const sig = crypto.createHmac('sha256', appSecret).update(payload).digest('hex') + return { + Authorization: accessToken, + 'X-Api-Key': appKey, + 'X-Timestamp': ts, + 'X-Api-Signature': `HMAC-SHA256 SignedHeaders=${signedHeaders}, Signature=${sig}`, + } + } + - label: Java + lang: Java + source: | + import java.nio.charset.StandardCharsets; + import java.security.MessageDigest; + import java.util.HexFormat; + import java.util.Map; + import javax.crypto.Mac; + import javax.crypto.spec.SecretKeySpec; + + static String sha1Hex(String s) throws Exception { + byte[] d = MessageDigest.getInstance("SHA-1").digest(s.getBytes(StandardCharsets.UTF_8)); + return HexFormat.of().formatHex(d); + } + + static Map sign(String method, String path, String query, String body, + String appKey, String appSecret, String accessToken) throws Exception { + String ts = String.valueOf(System.currentTimeMillis() / 1000); + String signedHeaders = "authorization;x-api-key;x-timestamp"; + String signedValues = "authorization:" + accessToken + "\nx-api-key:" + appKey + "\nx-timestamp:" + ts + "\n"; + String canonical = method + "|" + path + "|" + query + "|" + signedValues + "|" + signedHeaders + "|"; + if (!body.isEmpty()) canonical += sha1Hex(body); + String payload = "HMAC-SHA256|" + sha1Hex(canonical); + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + String sig = HexFormat.of().formatHex(mac.doFinal(payload.getBytes(StandardCharsets.UTF_8))); + return Map.of( + "Authorization", accessToken, + "X-Api-Key", appKey, + "X-Timestamp", ts, + "X-Api-Signature", "HMAC-SHA256 SignedHeaders=" + signedHeaders + ", Signature=" + sig); + } + - label: Rust + lang: Rust + source: | + use hmac::{Hmac, Mac}; + use sha1::{Digest, Sha1}; + use sha2::Sha256; + + fn sha1_hex(s: &str) -> String { + format!("{:x}", Sha1::digest(s.as_bytes())) + } + + fn sign(method: &str, path: &str, query: &str, body: &str, + app_key: &str, app_secret: &str, access_token: &str) -> Vec<(String, String)> { + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH).unwrap().as_secs().to_string(); + let signed_headers = "authorization;x-api-key;x-timestamp"; + let signed_values = format!("authorization:{access_token}\nx-api-key:{app_key}\nx-timestamp:{ts}\n"); + let mut canonical = format!("{method}|{path}|{query}|{signed_values}|{signed_headers}|"); + if !body.is_empty() { + canonical.push_str(&sha1_hex(body)); + } + let payload = format!("HMAC-SHA256|{}", sha1_hex(&canonical)); + let mut mac = Hmac::::new_from_slice(app_secret.as_bytes()).unwrap(); + mac.update(payload.as_bytes()); + let sig = format!("{:x}", mac.finalize().into_bytes()); + vec![ + ("Authorization".into(), access_token.into()), + ("X-Api-Key".into(), app_key.into()), + ("X-Timestamp".into(), ts), + ("X-Api-Signature".into(), format!("HMAC-SHA256 SignedHeaders={signed_headers}, Signature={sig}")), + ] + } + - label: C++ + lang: C++ + source: | + #include + #include + #include + + static std::string to_hex(const unsigned char* d, size_t n) { + static const char* h = "0123456789abcdef"; + std::string s; + for (size_t i = 0; i < n; i++) { s += h[d[i] >> 4]; s += h[d[i] & 0xf]; } + return s; + } + static std::string sha1_hex(const std::string& s) { + unsigned char d[SHA_DIGEST_LENGTH]; + SHA1((const unsigned char*)s.data(), s.size(), d); + return to_hex(d, SHA_DIGEST_LENGTH); + } + + // ts = current unix time in seconds (as a string) + std::string signature(const std::string& method, const std::string& path, const std::string& query, + const std::string& body, const std::string& app_key, const std::string& app_secret, + const std::string& access_token, const std::string& ts) { + std::string signed_headers = "authorization;x-api-key;x-timestamp"; + std::string signed_values = + "authorization:" + access_token + "\nx-api-key:" + app_key + "\nx-timestamp:" + ts + "\n"; + std::string canonical = + method + "|" + path + "|" + query + "|" + signed_values + "|" + signed_headers + "|"; + if (!body.empty()) canonical += sha1_hex(body); + std::string payload = "HMAC-SHA256|" + sha1_hex(canonical); + unsigned char mac[EVP_MAX_MD_SIZE]; unsigned int len = 0; + HMAC(EVP_sha256(), app_secret.data(), (int)app_secret.size(), + (const unsigned char*)payload.data(), payload.size(), mac, &len); + return "HMAC-SHA256 SignedHeaders=" + signed_headers + ", Signature=" + to_hex(mac, len); + } + - label: Go + lang: Go + source: "package main\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/sha1\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n)\n\nfunc sign(method, path, query, body, appKey, appSecret, accessToken string) map[string]string {\n\tts := strconv.FormatInt(time.Now().Unix(), 10)\n\tsignedHeaders := \"authorization;x-api-key;x-timestamp\"\n\tsignedValues := fmt.Sprintf(\"authorization:%s\\nx-api-key:%s\\nx-timestamp:%s\\n\", accessToken, appKey, ts)\n\tcanonical := fmt.Sprintf(\"%s|%s|%s|%s|%s|\", method, path, query, signedValues, signedHeaders)\n\tif body != \"\" {\n\t\tbh := sha1.Sum([]byte(body))\n\t\tcanonical += hex.EncodeToString(bh[:])\n\t}\n\tch := sha1.Sum([]byte(canonical))\n\tpayload := \"HMAC-SHA256|\" + hex.EncodeToString(ch[:])\n\tmac := hmac.New(sha256.New, []byte(appSecret))\n\tmac.Write([]byte(payload))\n\tsig := hex.EncodeToString(mac.Sum(nil))\n\treturn map[string]string{\n\t\t\"Authorization\": accessToken,\n\t\t\"X-Api-Key\": appKey,\n\t\t\"X-Timestamp\": ts,\n\t\t\"X-Api-Signature\": fmt.Sprintf(\"HMAC-SHA256 SignedHeaders=%s, Signature=%s\", signedHeaders, sig),\n\t}\n}\n" - id: overview title: Overview x-title-zh: 概览 + x-title-zh-hk: 概覽 x-icon: book content: | This page is reorganized as a practical **OAuth 2.0 access flow** for new integrations. @@ -40,9 +381,9 @@ x-pages: - `authorization_code` - `refresh_token` - ## OAuth 2.0 flow (step-by-step) + ## OAuth 2.0 authorization flow - ### 1) Register OAuth client + ### Register OAuth client If there is no UI for client creation in your environment, register dynamically: @@ -59,7 +400,7 @@ x-pages: > Registration may return only `client_id` (public client, no `client_secret`). In this case, use PKCE and do not send `client_secret` in token requests. - ### 2) Build authorization URL and get `code` + ### Build authorization URL and get `code` ```text https://openapi.longbridge.com/oauth2/authorize @@ -78,7 +419,7 @@ x-pages: YOUR_REDIRECT_URI?code=AUTH_CODE&state=YOUR_RANDOM_STATE ``` - ### 3) Exchange `code` for `access_token` + ### Exchange `code` for `access_token` ```shell curl -X POST https://openapi.longbridge.com/oauth2/token \ @@ -92,7 +433,7 @@ x-pages: # -d "client_secret=YOUR_CLIENT_SECRET" ``` - ### 4) Call API with Bearer token (TSLA.US example) + ### Call API with Bearer token (TSLA.US example) ```shell curl -X GET "https://openapi.longbridge.com/v1/quote/get_security_list?market=US&category=Overnight" \ @@ -118,7 +459,7 @@ x-pages: } ``` - ### 5) Refresh token + ### Refresh token Use OAuth token endpoint for refresh (details in Refresh Token section below): @@ -217,9 +558,9 @@ x-pages: - `authorization_code` - `refresh_token` - ## OAuth 2.0 接入流程(一步一步) + ## OAuth 2.0 授权流程 - ### 1)注册 OAuth 客户端 + ### 注册 OAuth 客户端 如果没有可视化后台入口,可通过接口动态注册: @@ -236,7 +577,7 @@ x-pages: > 注册返回可能仅包含 `client_id`(public client,不返回 `client_secret`)。这种情况下请使用 PKCE,并在 token 请求里不传 `client_secret`。 - ### 2)构造授权链接并获取 code + ### 构造授权链接并获取 code ```text https://openapi.longbridge.com/oauth2/authorize @@ -255,7 +596,7 @@ x-pages: YOUR_REDIRECT_URI?code=AUTH_CODE&state=YOUR_RANDOM_STATE ``` - ### 3)用 code 换 access_token + ### 用 code 换 access_token ```bash curl -X POST https://openapi.longbridge.com/oauth2/token \ @@ -269,7 +610,7 @@ x-pages: # -d "client_secret=YOUR_CLIENT_SECRET" ``` - ### 4)用 Bearer token 调 API(TSLA.US 实例) + ### 用 Bearer token 调 API(TSLA.US 实例) ```bash curl -X GET "https://openapi.longbridge.com/v1/quote/get_security_list?market=US&category=Overnight" \ @@ -295,7 +636,7 @@ x-pages: } ``` - ### 5)刷新 token + ### 刷新 token 通过 OAuth token endpoint 刷新(详见下方"刷新 Token"部分): @@ -365,156 +706,35439 @@ x-pages: ## 兼容说明 历史接口 `/v1/token/refresh` 属于旧方案兼容路径,不建议新接入继续采用。新接入请统一使用 OAuth 2.0 token endpoint。 + x-content-zh-hk: | + 本頁按 **OAuth 2.0 實際接入流程** 重新整理,用於新接入用戶快速走通。 + + > **提示:** 優先使用 SDK,接入更簡單:https://open.longbridge.com/sdk + + ## API 須知 + + | 注意事項 | 參考文檔 | + | -------------------------------------------- | ------------------------------------------------- | + | 推薦使用各自語言的 SDK,而不是調用原生的接口 | [SDK 快速開始頁面](/docs/getting-started) | + | 閱讀 OpenAPI 介紹中開通相應服務 | [OpenAPI 如何開通](/docs/#如何開通) | + | 閱讀 OpenAPI 介紹中使用權限及限制 | [OpenAPI 使用權限及限制](/docs/#使用權限及限制) | + | 瞭解通用錯誤碼,便於查找調用接口出錯的原因 | [通用錯誤碼](/docs/error-codes) | + + ## OAuth 2.0(推薦方案) + + 新接入默認使用 OAuth 2.0。API Key 簽名方式作爲備選兼容方案可保留(例如在 SDK/歷史實現中),但不作爲默認接入方式。 + + ### Discovery 地址 + + - 生產環境:`https://openapi.longbridge.com/.well-known/oauth-authorization-server` + - 中國內地:`https://openapi.longbridge.cn/.well-known/oauth-authorization-server` + + 支持授權類型(以 Discovery 返回爲準): + + - `authorization_code` + - `refresh_token` + + ## OAuth 2.0 授權流程 + + ### 註冊 OAuth 客戶端 + + 如果沒有可視化後台入口,可通過接口動態註冊: + + ```bash + curl -X POST https://openapi.longbridge.com/oauth2/register \ + -H "Content-Type: application/json" \ + -d '{ + "client_name": "my-openapi-app", + "redirect_uris": ["https://your-app.com/callback"], + "grant_types": ["authorization_code", "refresh_token"], + "response_types": ["code"] + }' + ``` + + > 註冊返回可能僅包含 `client_id`(public client,不返回 `client_secret`)。這種情況下請使用 PKCE,並在 token 請求裏不傳 `client_secret`。 + + ### 構造授權鏈接並獲取 code + + ```text + https://openapi.longbridge.com/oauth2/authorize + ?response_type=code + &client_id=YOUR_CLIENT_ID + &redirect_uri=YOUR_REDIRECT_URI + &scope=3 + &state=YOUR_RANDOM_STATE + &code_challenge=YOUR_CODE_CHALLENGE + &code_challenge_method=S256 + ``` + + 用戶授權後,回調地址會收到: + + ```text + YOUR_REDIRECT_URI?code=AUTH_CODE&state=YOUR_RANDOM_STATE + ``` + + ### 用 code 換 access_token + + ```bash + curl -X POST https://openapi.longbridge.com/oauth2/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=authorization_code" \ + -d "client_id=YOUR_CLIENT_ID" \ + -d "redirect_uri=YOUR_REDIRECT_URI" \ + -d "code=AUTH_CODE" \ + -d "code_verifier=YOUR_CODE_VERIFIER" + # 僅當客戶端有 secret 時再加: + # -d "client_secret=YOUR_CLIENT_SECRET" + ``` + + ### 用 Bearer token 調 API(TSLA.US 實例) + + ```bash + curl -X GET "https://openapi.longbridge.com/v1/quote/get_security_list?market=US&category=Overnight" \ + -H "Authorization: Bearer ACCESS_TOKEN" + ``` + + 實際返回(節選,保留 `TSLA.US` 項): + + ```json + { + "code": 0, + "message": "success", + "data": { + "list": [ + { + "symbol": "TSLA.US", + "name_cn": "特斯拉", + "name_hk": "", + "name_en": "" + } + ] + } + } + ``` + + ### 刷新 token + + 通過 OAuth token endpoint 刷新(詳見下方"刷新 Token"部分): + + ```bash + curl -X POST https://openapi.longbridge.com/oauth2/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=refresh_token" \ + -d "client_id=YOUR_CLIENT_ID" \ + -d "refresh_token=REFRESH_TOKEN" + # 僅當客戶端有 secret 時再加: + # -d "client_secret=YOUR_CLIENT_SECRET" + ``` + + ## 與舊文檔的關係 + + - 本頁:只講 **OAuth 2.0 主流程**(新接入默認看這裏)。 + - 下方"刷新 Token"部分:只講刷新步驟細節與常見問題,避免重複。 + + --- + + # 刷新 Token(OAuth 2.0) + + 本頁僅說明 OAuth 2.0 的 **refresh token** 刷新步驟。 + + - 如果你還沒走完完整授權流程,請先看上方認證部分。 + - 本頁不重複註冊 client / 獲取 code 的流程,只關注"刷新"這一步 + + ## 推薦刷新方式(OAuth 2.0) + + 使用 OAuth token endpoint: + + - `POST https://openapi.longbridge.com/oauth2/token` + - 或中國內地:`POST https://openapi.longbridge.cn/oauth2/token` + + ### 請求參數(`application/x-www-form-urlencoded`) + + | 名稱 | 必須 | 說明 | + | ------------- | ---- | ---- | + | grant_type | 是 | 固定爲 `refresh_token` | + | client_id | 是 | OAuth client id | + | refresh_token | 是 | 上一次簽發的 refresh token | + | client_secret | 否 | 僅機密客戶端需要;public client 不傳 | + + ### 刷新示例 + + ```bash + curl -X POST https://openapi.longbridge.com/oauth2/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=refresh_token" \ + -d "client_id=YOUR_CLIENT_ID" \ + -d "refresh_token=YOUR_REFRESH_TOKEN" + # 僅當你的客戶端有 secret 時再加: + # -d "client_secret=YOUR_CLIENT_SECRET" + ``` + + ### 響應示例 + + ```json + { + "access_token": "...", + "refresh_token": "...", + "expires_in": 2592000, + "token_type": "Bearer" + } + ``` + + ## 兼容說明 + + 歷史接口 `/v1/token/refresh` 屬於舊方案兼容路徑,不建議新接入繼續採用。新接入請統一使用 OAuth 2.0 token endpoint。 - id: real-time-data title: Real-Time Market Data x-title-zh: 实时行情 + x-title-zh-hk: 實時行情 x-icon: activity content: | - > **Note:** Real-time market data is **not** part of this HTTP REST API. Quotes, price feeds, order book depth, broker queues, and trade ticks are delivered via **WebSocket / TCP long connection** through a dedicated quote gateway — see the [Socket Feed](/docs/socket/hosts) documentation. + > **Note:** Real-time market data is **not** part of this HTTP REST API. Quotes, depth, broker queues and trade ticks are delivered over a persistent **WebSocket / TCP** connection to a dedicated gateway. This page documents that protocol end-to-end — you do not need to leave it. ## Overview - The HTTP REST API (documented on this page) covers trading operations, account management, and historical/snapshot data queries. Real-time streaming data — all market quote subscriptions and push feeds — is a separate system: + The HTTP REST API on this site covers trading, account management and historical/snapshot queries. Real-time streaming is a separate, connection-oriented protocol: + + | Data | Access | + |------|--------| + | Orders, account, watchlist, snapshots | HTTP REST (this reference) | + | Real-time quote / depth / brokers / trades | WebSocket or TCP gateway | + + > The official SDKs (Rust/Python/Node.js/Java/C++/Go) implement this whole protocol — connect, auth, heartbeat, subscribe and decode — for you: https://open.longbridge.com/sdk . The reference below is for building a client by hand. + + ## Gateway hosts + + | Feed | Region | WebSocket | TCP | + |------|--------|-----------|-----| + | Quote | Global | `wss://openapi-quote.longbridge.com` | `openapi-quote.longbridge.com:2020` | + | Quote | Mainland China | `wss://openapi-quote.longbridge.cn` | `openapi-quote.longbridge.cn:2020` | + | Trade | Global | `wss://openapi-trade.longbridge.com` | `openapi-trade.longbridge.com:2020` | + | Trade | Mainland China | `wss://openapi-trade.longbridge.cn` | `openapi-trade.longbridge.cn:2020` | + + Payloads are Protobuf and integers are **big-endian**. The TCP port is `2020` for every host. + + ## 1. Get a connection OTP + + A socket connection authenticates with a one-time password (OTP) fetched from the REST API: + + ``` + GET /v1/socket/token + Authorization: + ``` + + Response `data`: + + | Field | Type | Meaning | + |-------|------|---------| + | `otp` | string | One-time password, used as the auth token below | + | `limit` | int | Max concurrent connections | + | `online` | int | Current online connections | - | Data Type | Access Method | - |-----------|---------------| - | Trading orders, account info, watchlist | HTTP REST API (this page) | - | Real-time quotes, depth, trade ticks | WebSocket / TCP Socket Feed | + ```json + { "code": 0, "message": "", "data": { "otp": "xxxxxxxx", "online": 1, "limit": 10 } } + ``` + + The OTP is single-use — it is consumed the moment you auth. + + ## 2. Handshake + + The handshake negotiates three fixed values: protocol `version` = `1`, `codec` = `1` (Protobuf) and `platform` = `9` (OpenAPI). + + - **WebSocket** — pass them as query params: `wss://openapi-quote.longbridge.com?version=1&codec=1&platform=9` + - **TCP** — send a 2-byte handshake packing the negotiated values: byte 1 = `codec << 4 | ver`, byte 2 = `reserve << 4 | platform` (so `version` and `platform` sit in the low nibble, matching the `type`-in-low-nibble packet header below). For `ver=1, codec=1, platform=9` that is `0x11 0x09` (`0b00010001 0b00001001`). + + ## 3. Packet framing + + Every packet begins with a 1-byte header. `type` occupies the **low 4 bits**; the flags sit above it: - ## Connecting to the Quote Gateway + | Bit(s) | Field | Meaning | + |--------|-------|---------| + | 0–3 | `type` | `1` request · `2` response · `3` push | + | 4 | `verify` | `1` = nonce + signature present | + | 5 | `gzip` | `1` = body is gzip-compressed | + | 6–7 | `reserve` | — | - Connect to Longbridge's quote gateway directly via WebSocket or TCP: + So a plain request header byte is `0x01` (type 1, no verify/gzip); set bit 4 (`0x10`) for `verify` or bit 5 (`0x20`) for `gzip`. - **WebSocket:** `wss://openapi-quote.longbridge.com` + **Request** (`type=1`): `cmd_code` (1B) · `request_id` (4B uint32, unique per connection) · `timeout` (2B ms, ≤ 60000) · `body_len` (3B) · `body` (protobuf) · then `nonce` (8B) + `signature` (16B) when `verify=1`. - **TCP:** `openapi-quote.longbridge.com:2020` + **Response** (`type=2`): `cmd_code` (1B) · `request_id` (4B, matches the request) · `status` (1B) · `body_len` (3B) · `body` · (`nonce` + `signature` when `verify=1`). - > Mainland China users: `wss://openapi-quote.longbridge.cn` / `openapi-quote.longbridge.cn:2020` + **Push** (`type=3`): `cmd_code` (1B) · `body_len` (3B) · `body` · (`nonce` + `signature` when `verify=1`). No `request_id` / `status`. - ## Subscribing to Market Data + Response `status`: `0` OK · `1` server timeout (408) · `3` bad request (400) · `5` unauthenticated (401) · `7` server error (500). `body_len` maxes out at 16 MB. - After connecting, authenticate with your API credentials, then send a subscribe command specifying symbols and subscription types (price, depth, broker queue, trade ticks). The server will then push real-time data as it arrives. + ## 4. Control commands - The SDK handles connection lifecycle and subscriptions automatically — for most integrations the SDK is the recommended approach: https://open.longbridge.com/sdk + Connection-level `cmd_code`s: - ## Further Reading + | Code | Command | Purpose | + |------|---------|---------| + | `0` | Close | Server-sent push right before it closes the connection | + | `1` | Heartbeat | Keep-alive; echo the body back (`Heartbeat { int64 timestamp }`) | + | `2` | Auth | First packet after the handshake | + | `3` | Reconnect | Re-auth with a session id, no new OTP required | - - [Socket Feed endpoints](/docs/socket/hosts) - - [Subscribe to market data](/docs/socket/subscribe_quote) - - [Protocol overview](/docs/socket/protocol/overview) + **Auth** — send `cmd_code=2` with `AuthRequest { string token = 1 }`, where `token` is the OTP from step 1. The server replies `AuthResponse { string session_id = 1; int64 expires = 2 }`. Keep `session_id` to `Reconnect` (`ReconnectRequest { string session_id }`) without fetching a new OTP. + + Close reason codes (`Close.Code`): `0` HeartbeatTimeout · `1` ServerError · `2` ServerShutdown · `3` UnpackError · `4` AuthError · `5` SessExpired · `6` ConnectDuplicate. + + Protobuf: https://github.com/longbridge/openapi-protobufs/blob/main/control/control.proto + + ## Commands + + After authenticating, send business commands over the same connection. Every quote pull, subscription, push and trade command — with its cmd code, protobuf request/response, field tables and native call example — is documented under the **WebSocket** groups in the sidebar (**行情 → Pull / Subscription / Push**, **交易 → Notification**). + + ## Reference implementation + + Prefer the SDK: https://open.longbridge.com/sdk . To parse the protocol by hand, see the Go reference implementation: https://github.com/longbridge/openapi-protocol/tree/main/go x-content-zh: | - > **注意:** 实时行情数据**不**属于本 HTTP REST API 的范围。报价、价格推送、盘口、经纪队列及成交明细通过专用行情网关以 **WebSocket / TCP 长连接**方式推送,详见 [Socket 实时推送](/docs/socket/hosts) 文档。 + > **注意:** 实时行情**不**属于本 HTTP REST API。报价、盘口、经纪队列与成交明细通过与专用网关保持的 **WebSocket / TCP** 长连接推送。本页完整记录该协议,无需跳转其他文档。 ## 概述 - 本 HTTP REST API(即本页面记录的接口)涵盖交易操作、账户管理及历史/快照数据查询。实时流式数据——所有行情订阅与推送——属于独立的系统: + 本站 HTTP REST API 覆盖交易、账户管理及历史/快照查询;实时流式数据是一套独立的、面向连接的协议: - | 数据类型 | 接入方式 | - |----------|----------| - | 交易委托、账户信息、自选股 | HTTP REST API(本页) | - | 实时报价、盘口、成交明细 | WebSocket / TCP Socket Feed | + | 数据 | 接入方式 | + |------|----------| + | 委托、账户、自选股、快照 | HTTP REST(本参考) | + | 实时报价 / 盘口 / 经纪队列 / 成交 | WebSocket 或 TCP 网关 | - ## 连接行情网关 + > 官方 SDK(Rust/Python/Node.js/Java/C++/Go)已完整实现该协议,包括连接、鉴权、心跳、订阅与解码:https://open.longbridge.com/sdk。下面的参考适用于手写客户端。 - 通过 WebSocket 或 TCP 直连 Longbridge 行情网关: + ## 网关地址 - **WebSocket:** `wss://openapi-quote.longbridge.com` + | 类型 | 区域 | WebSocket | TCP | + |------|------|-----------|-----| + | 行情 | 全球 | `wss://openapi-quote.longbridge.com` | `openapi-quote.longbridge.com:2020` | + | 行情 | 中国大陆 | `wss://openapi-quote.longbridge.cn` | `openapi-quote.longbridge.cn:2020` | + | 交易 | 全球 | `wss://openapi-trade.longbridge.com` | `openapi-trade.longbridge.com:2020` | + | 交易 | 中国大陆 | `wss://openapi-trade.longbridge.cn` | `openapi-trade.longbridge.cn:2020` | - **TCP:** `openapi-quote.longbridge.com:2020` + 载荷为 Protobuf,整数为**大端序**。所有主机的 TCP 端口均为 `2020`。 - > 中国大陆用户:`wss://openapi-quote.longbridge.cn` / `openapi-quote.longbridge.cn:2020` + ## 1. 获取连接 OTP - ## 订阅行情 + Socket 连接使用一次性密码(OTP)鉴权,OTP 从 REST API 获取: - 连接后,使用 API 凭证完成鉴权,然后发送订阅指令,指定标的和订阅类型(价格、盘口、经纪队列、成交明细)。服务端将实时推送订阅的行情数据。 + ``` + GET /v1/socket/token + Authorization: + ``` - SDK 已完整实现连接生命周期管理与订阅功能,推荐大多数接入场景直接使用 SDK:https://open.longbridge.com/sdk + 响应 `data`: - ## 相关文档 + | 字段 | 类型 | 含义 | + |------|------|------| + | `otp` | string | 一次性密码,用作下面的鉴权 token | + | `limit` | int | 最大并发连接数 | + | `online` | int | 当前在线连接数 | - - [Socket 实时推送接入地址](/docs/socket/hosts) - - [订阅行情推送](/docs/socket/subscribe_quote) - - [协议概览](/docs/socket/protocol/overview) - - id: error-codes - title: Error Codes - x-title-zh: 错误码 - x-icon: alert-circle - content: | - ## Error Codes + ```json + { "code": 0, "message": "", "data": { "otp": "xxxxxxxx", "online": 1, "limit": 10 } } + ``` - | HTTP Status | Code | Message | Description | - | ----------- | ------ | ---------------------- | ------------------------------------------------------------------ | - | 403 | 403201 | signature invalid | signature is invalid | - | 403 | 403202 | duplicate request | Repeat request, same request without replacement `x-timestamp` | - | 403 | 403203 | apikey illegal | `App Key` is illegal | - | 403 | 403205 | ip is not allowed | IP address is not authorized to access | - | 401 | 401003 | token expired | Access token expired. Legacy API Key: obtain a new token from [https://open.longbridge.com/](https://open.longbridge.com/). OAuth: use the refresh token flow. | - | 429 | 429001 | ip request ratelimit | Too frequent requests as a same IP address, please try again later | - | 429 | 429002 | api request is limited | Too frequent requests on an API, please try again later | - | 500 | 500000 | internal error | server internal error, please contact customer support | - x-content-zh: | - ## 错误码 + OTP 为一次性使用,鉴权后即失效。 - | HTTP Status | code | message | 说明 | - | ----------- | ------ | ---------------------- | ---------------------------------------- | - | 403 | 403201 | signature invalid | 签名无效 | - | 403 | 403202 | duplicate request | 重复请求,同一个请求没有更换 X-Timestamp | - | 403 | 403203 | apikey illegal | App Key 无效 | - | 403 | 403205 | ip is not allowed | IP 地址无权访问 | - | 401 | 401003 | token expired | Access Token 已过期。旧版 API Key:请在 [https://open.longbridge.com/](https://open.longbridge.com/) 重新获取;OAuth:请使用 refresh token 流程刷新。 | - | 429 | 429001 | ip request ratelimit | IP 访问过于频繁,请稍后再试 | - | 429 | 429002 | api request is limited | 接口访问过于频繁,请稍后再试 | - | 500 | 500000 | internal error | 服务内部错误,请联系客户经理进行处理 | -servers: - - url: https://openapi.longbridge.com + ## 2. 握手 + + 握手协商三个固定值:协议 `version` = `1`、`codec` = `1`(Protobuf)、`platform` = `9`(OpenAPI)。 + + - **WebSocket** — 作为查询参数传入:`wss://openapi-quote.longbridge.com?version=1&codec=1&platform=9` + - **TCP** — 发送 2 字节握手,按如下打包协商值:字节 1 = `codec << 4 | ver`,字节 2 = `reserve << 4 | platform`(于是 `version` 与 `platform` 落在低半字节,与下方包头 type 在低位的约定一致)。当 `ver=1, codec=1, platform=9` 时即 `0x11 0x09`(`0b00010001 0b00001001`)。 + + ## 3. 报文结构 + + 每个报文以 1 字节头部开始。`type` 位于**低 4 位**,其余标志位在其之上: + + | 位 | 字段 | 含义 | + |----|------|------| + | 0–3 | `type` | `1` 请求 · `2` 响应 · `3` 推送 | + | 4 | `verify` | `1` = 含 nonce + 签名 | + | 5 | `gzip` | `1` = body 经 gzip 压缩 | + | 6–7 | `reserve` | — | + + 因此普通请求的头字节是 `0x01`(type 1,不含 verify/gzip);置 bit 4(`0x10`)表示 `verify`,置 bit 5(`0x20`)表示 `gzip`。 + + **请求**(`type=1`):`cmd_code`(1B)· `request_id`(4B uint32,连接内唯一)· `timeout`(2B 毫秒,≤ 60000)· `body_len`(3B)· `body`(protobuf)· 当 `verify=1` 时追加 `nonce`(8B)+ `signature`(16B)。 + + **响应**(`type=2`):`cmd_code`(1B)· `request_id`(4B,与请求一致)· `status`(1B)· `body_len`(3B)· `body` ·(`verify=1` 时含 `nonce` + `signature`)。 + + **推送**(`type=3`):`cmd_code`(1B)· `body_len`(3B)· `body` ·(`verify=1` 时含 `nonce` + `signature`)。无 `request_id` / `status`。 + + 响应 `status`:`0` 成功 · `1` 服务端超时 (408)· `3` 请求错误 (400)· `5` 未鉴权 (401)· `7` 服务端错误 (500)。`body_len` 上限 16 MB。 + + ## 4. 控制命令 + + 连接级 `cmd_code`: + + | 命令码 | 命令 | 用途 | + |--------|------|------| + | `0` | Close | 服务端在关闭连接前推送 | + | `1` | Heartbeat | 心跳保活;原样回显 body(`Heartbeat { int64 timestamp }`) | + | `2` | Auth | 握手后的第一个报文 | + | `3` | Reconnect | 用 session 重新鉴权,无需新 OTP | + + **鉴权** — 发送 `cmd_code=2`,body 为 `AuthRequest { string token = 1 }`,其中 `token` 为第 1 步的 OTP。服务端返回 `AuthResponse { string session_id = 1; int64 expires = 2 }`。保存 `session_id` 可用 `Reconnect`(`ReconnectRequest { string session_id }`)重连,无需重新获取 OTP。 + + 关闭原因码(`Close.Code`):`0` 心跳超时 · `1` 服务端错误 · `2` 服务端关闭 · `3` 解包错误 · `4` 鉴权错误 · `5` 会话过期 · `6` 连接重复。 + + Protobuf:https://github.com/longbridge/openapi-protobufs/blob/main/control/control.proto + + ## 命令 + + 鉴权后在同一连接上发送业务命令。所有行情拉取、订阅、推送与交易命令——含 cmd 号、protobuf 请求/响应、字段表与原生调用示例——都在侧栏的 **WebSocket** 分组下(**行情 → 拉取 / 订阅 / 推送**,**交易 → 推送**)。 + + ## 参考实现 + + 推荐使用 SDK:https://open.longbridge.com/sdk。如需手写解析协议,参考 Go 实现:https://github.com/longbridge/openapi-protocol/tree/main/go + x-content-zh-hk: | + > **注意:** 實時行情**不**屬於本 HTTP REST API。報價、盤口、經紀隊列與成交明細透過與專用閘道保持的 **WebSocket / TCP** 長連接推送。本頁完整記錄該協議,無需跳轉其他文件。 + + ## 概述 + + 本站 HTTP REST API 涵蓋交易、賬戶管理及歷史/快照查詢;實時流式資料是一套獨立的、面向連接的協議: + + | 資料 | 接入方式 | + |------|----------| + | 委託、賬戶、自選股、快照 | HTTP REST(本參考) | + | 實時報價 / 盤口 / 經紀隊列 / 成交 | WebSocket 或 TCP 閘道 | + + > 官方 SDK(Rust/Python/Node.js/Java/C++/Go)已完整實現該協議,包括連接、鑑權、心跳、訂閱與解碼:https://open.longbridge.com/sdk。下面的參考適用於手寫客戶端。 + + ## 閘道地址 + + | 類型 | 區域 | WebSocket | TCP | + |------|------|-----------|-----| + | 行情 | 全球 | `wss://openapi-quote.longbridge.com` | `openapi-quote.longbridge.com:2020` | + | 行情 | 中國大陸 | `wss://openapi-quote.longbridge.cn` | `openapi-quote.longbridge.cn:2020` | + | 交易 | 全球 | `wss://openapi-trade.longbridge.com` | `openapi-trade.longbridge.com:2020` | + | 交易 | 中國大陸 | `wss://openapi-trade.longbridge.cn` | `openapi-trade.longbridge.cn:2020` | + + 載荷為 Protobuf,整數為**大端序**。所有主機的 TCP 端口均為 `2020`。 + + ## 1. 獲取連接 OTP + + Socket 連接使用一次性密碼(OTP)鑑權,OTP 從 REST API 獲取: + + ``` + GET /v1/socket/token + Authorization: + ``` + + 響應 `data`: + + | 欄位 | 類型 | 含義 | + |------|------|------| + | `otp` | string | 一次性密碼,用作下面的鑑權 token | + | `limit` | int | 最大並發連接數 | + | `online` | int | 當前線上連接數 | + + ```json + { "code": 0, "message": "", "data": { "otp": "xxxxxxxx", "online": 1, "limit": 10 } } + ``` + + OTP 為一次性使用,鑑權後即失效。 + + ## 2. 握手 + + 握手協商三個固定值:協議 `version` = `1`、`codec` = `1`(Protobuf)、`platform` = `9`(OpenAPI)。 + + - **WebSocket** — 作為查詢參數傳入:`wss://openapi-quote.longbridge.com?version=1&codec=1&platform=9` + - **TCP** — 發送 2 位元組握手,按如下打包協商值:位元組 1 = `codec << 4 | ver`,位元組 2 = `reserve << 4 | platform`(於是 `version` 與 `platform` 落在低半位元組,與下方封包頭 type 在低位的約定一致)。當 `ver=1, codec=1, platform=9` 時即 `0x11 0x09`(`0b00010001 0b00001001`)。 + + ## 3. 報文結構 + + 每個報文以 1 位元組頭部開始。`type` 位於**低 4 位**,其餘旗標位在其之上: + + | 位 | 欄位 | 含義 | + |----|------|------| + | 0–3 | `type` | `1` 請求 · `2` 響應 · `3` 推送 | + | 4 | `verify` | `1` = 含 nonce + 簽名 | + | 5 | `gzip` | `1` = body 經 gzip 壓縮 | + | 6–7 | `reserve` | — | + + 因此普通請求的頭位元組是 `0x01`(type 1,不含 verify/gzip);置 bit 4(`0x10`)表示 `verify`,置 bit 5(`0x20`)表示 `gzip`。 + + **請求**(`type=1`):`cmd_code`(1B)· `request_id`(4B uint32,連接內唯一)· `timeout`(2B 毫秒,≤ 60000)· `body_len`(3B)· `body`(protobuf)· 當 `verify=1` 時追加 `nonce`(8B)+ `signature`(16B)。 + + **響應**(`type=2`):`cmd_code`(1B)· `request_id`(4B,與請求一致)· `status`(1B)· `body_len`(3B)· `body` ·(`verify=1` 時含 `nonce` + `signature`)。 + + **推送**(`type=3`):`cmd_code`(1B)· `body_len`(3B)· `body` ·(`verify=1` 時含 `nonce` + `signature`)。無 `request_id` / `status`。 + + 響應 `status`:`0` 成功 · `1` 服務端超時 (408)· `3` 請求錯誤 (400)· `5` 未鑑權 (401)· `7` 服務端錯誤 (500)。`body_len` 上限 16 MB。 + + ## 4. 控制命令 + + 連接級 `cmd_code`: + + | 命令碼 | 命令 | 用途 | + |--------|------|------| + | `0` | Close | 服務端在關閉連接前推送 | + | `1` | Heartbeat | 心跳保活;原樣回顯 body(`Heartbeat { int64 timestamp }`) | + | `2` | Auth | 握手後的第一個報文 | + | `3` | Reconnect | 用 session 重新鑑權,無需新 OTP | + + **鑑權** — 發送 `cmd_code=2`,body 為 `AuthRequest { string token = 1 }`,其中 `token` 為第 1 步的 OTP。服務端返回 `AuthResponse { string session_id = 1; int64 expires = 2 }`。保存 `session_id` 可用 `Reconnect`(`ReconnectRequest { string session_id }`)重連,無需重新獲取 OTP。 + + 關閉原因碼(`Close.Code`):`0` 心跳超時 · `1` 服務端錯誤 · `2` 服務端關閉 · `3` 解包錯誤 · `4` 鑑權錯誤 · `5` 會話過期 · `6` 連接重複。 + + Protobuf:https://github.com/longbridge/openapi-protobufs/blob/main/control/control.proto + + ## 命令 + + 鑑權後在同一連接上發送業務命令。所有行情拉取、訂閱、推送與交易命令——含 cmd 號、protobuf 請求/響應、欄位表與原生調用示例——都在側欄的 **WebSocket** 分組下(**行情 → 拉取 / 訂閱 / 推送**,**交易 → 推送**)。 + + ## 參考實現 + + 推薦使用 SDK:https://open.longbridge.com/sdk。如需手寫解析協議,參考 Go 實現:https://github.com/longbridge/openapi-protocol/tree/main/go + - id: error-codes + title: Error Codes + x-title-zh: 错误码 + x-title-zh-hk: 錯誤碼 + x-icon: alert-circle + content: | + ## Error Codes + + | HTTP Status | Code | Message | Description | + | ----------- | ------ | ---------------------- | ------------------------------------------------------------------ | + | 403 | 403201 | signature invalid | signature is invalid | + | 403 | 403202 | duplicate request | Repeat request, same request without replacement `x-timestamp` | + | 403 | 403203 | apikey illegal | `App Key` is illegal | + | 403 | 403205 | ip is not allowed | IP address is not authorized to access | + | 401 | 401003 | token expired | Access token expired. Legacy API Key: obtain a new token from [https://open.longbridge.com/](https://open.longbridge.com/). OAuth: use the refresh token flow. | + | 429 | 429001 | ip request ratelimit | Too frequent requests as a same IP address, please try again later | + | 429 | 429002 | api request is limited | Too frequent requests on an API, please try again later | + | 500 | 500000 | internal error | server internal error, please contact customer support | + x-content-zh: | + ## 错误码 + + | HTTP Status | code | message | 说明 | + | ----------- | ------ | ---------------------- | ---------------------------------------- | + | 403 | 403201 | signature invalid | 签名无效 | + | 403 | 403202 | duplicate request | 重复请求,同一个请求没有更换 X-Timestamp | + | 403 | 403203 | apikey illegal | App Key 无效 | + | 403 | 403205 | ip is not allowed | IP 地址无权访问 | + | 401 | 401003 | token expired | Access Token 已过期。旧版 API Key:请在 [https://open.longbridge.com/](https://open.longbridge.com/) 重新获取;OAuth:请使用 refresh token 流程刷新。 | + | 429 | 429001 | ip request ratelimit | IP 访问过于频繁,请稍后再试 | + | 429 | 429002 | api request is limited | 接口访问过于频繁,请稍后再试 | + | 500 | 500000 | internal error | 服务内部错误,请联系客户经理进行处理 | + x-content-zh-hk: | + ## 錯誤碼 + + | HTTP Status | code | message | 說明 | + | ----------- | ------ | ---------------------- | ---------------------------------------- | + | 403 | 403201 | signature invalid | 簽名無效 | + | 403 | 403202 | duplicate request | 重複請求,同一個請求沒有更換 X-Timestamp | + | 403 | 403203 | apikey illegal | App Key 無效 | + | 403 | 403205 | ip is not allowed | IP 地址無權訪問 | + | 401 | 401003 | token expired | Access Token 已過期。舊版 API Key:請在 [https://open.longbridge.com/](https://open.longbridge.com/) 重新獲取;OAuth:請使用 refresh token 流程刷新。 | + | 429 | 429001 | ip request ratelimit | IP 訪問過於頻繁,請稍後再試 | + | 429 | 429002 | api request is limited | 接口訪問過於頻繁,請稍後再試 | + | 500 | 500000 | internal error | 服務內部錯誤,請聯繫客戶經理進行處理 | +x-websocket: + groups: + - name: Pull (WebSocket) + x-name-zh: 拉取 (WebSocket) + x-name-zh-hk: 拉取 (WebSocket) + x-tag: Quote + commands: + - id: ws-static + x-quote-command: static + x-subgroup: Stocks + name: Static Info + x-name-zh: 标的基础信息 + x-name-zh-hk: 標的基礎信息 + cmd: 10 + direction: request + description: | + This API is used to obtain the basic information of securities. + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message SecurityStaticInfoResponse { + repeated StaticInfo secu_static_info = 1; + } + ``` + x-description-zh: | + 该接口用于获取标的的基础信息。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message SecurityStaticInfoResponse { + repeated StaticInfo secu_static_info = 1; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的基礎信息。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message SecurityStaticInfoResponse { + repeated StaticInfo secu_static_info = 1; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["700.HK", "AAPL.US"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 10]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityStaticInfoResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["700.HK", "AAPL.US"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 10]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityStaticInfoResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = MultiSecurityRequest.encode({ symbol: ["700.HK", "AAPL.US"] }).finish() + const hdr = Buffer.from([0x01, 10]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityStaticInfoResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = MultiSecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 10); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityStaticInfoResponse resp = SecurityStaticInfoResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = MultiSecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 10]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityStaticInfoResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + MultiSecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(10); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityStaticInfoResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.MultiSecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 10} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityStaticInfoResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "secu_static_info": [ + { + "symbol": "700.HK", + "name_cn": "腾讯控股", + "name_en": "TENCENT", + "exchange": "SEHK", + "currency": "HKD", + "lot_size": 100, + "total_shares": 9612464038, + "eps": "28.4394", + "board": "HKEquity" + } + ] + } + x-fields: + - name: "symbol" + type: "string[]" + required: true + description: "Security code list, in ticker.region format; max 500 symbols per request" + x-description-zh: "标的代码列表,使用 ticker.region 格式;每次请求上限 500 个" + x-description-zh-hk: "標的代碼列表,使用 ticker.region 格式;每次請求上限 500 個" + x-response-fields: + - name: "secu_static_info" + type: "object[]" + required: false + description: "Securities Basic Information" + x-description-zh: "标的基础数据列表" + x-description-zh-hk: "標的基礎數據列表" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ name_cn" + type: "string" + required: false + description: "Security name (zh-CN)" + x-description-zh: "中文简体标的名称" + x-description-zh-hk: "中文簡體標的名稱" + - name: "└ name_en" + type: "string" + required: false + description: "Security name (en)" + x-description-zh: "英文标的名称" + x-description-zh-hk: "英文標的名稱" + - name: "└ name_hk" + type: "string" + required: false + description: "Security name (zh-HK)" + x-description-zh: "中文繁体标的名称" + x-description-zh-hk: "中文繁體標的名稱" + - name: "└ exchange" + type: "string" + required: false + description: "Exchange which the security belongs to" + x-description-zh: "标的所属交易所" + x-description-zh-hk: "標的所屬交易所" + - name: "└ currency" + type: "string" + required: false + description: "Trading currency (CNY/USD/SGD/HKD)" + x-description-zh: "交易币种 (CNY/USD/SGD/HKD)" + x-description-zh-hk: "交易幣種 (CNY/USD/SGD/HKD)" + - name: "└ lot_size" + type: "int32" + required: false + description: "Lot size" + x-description-zh: "每手股数" + x-description-zh-hk: "每手股數" + - name: "└ total_shares" + type: "int64" + required: false + description: "Total shares" + x-description-zh: "总股本" + x-description-zh-hk: "總股本" + - name: "└ circulating_shares" + type: "int64" + required: false + description: "Circulating shares" + x-description-zh: "流通股本" + x-description-zh-hk: "流通股本" + - name: "└ hk_shares" + type: "int64" + required: false + description: "HK shares (only HK stocks)" + x-description-zh: "港股股本 (仅港股)" + x-description-zh-hk: "港股股本 (僅港股)" + - name: "└ eps" + type: "string" + required: false + description: "Earnings per share" + x-description-zh: "每股盈利" + x-description-zh-hk: "每股盈利" + - name: "└ eps_ttm" + type: "string" + required: false + description: "Earnings per share (TTM)" + x-description-zh: "每股盈利 (TTM)" + x-description-zh-hk: "每股盈利 (TTM)" + - name: "└ bps" + type: "string" + required: false + description: "Net assets per share" + x-description-zh: "每股净资产" + x-description-zh-hk: "每股淨資產" + - name: "└ dividend_yield" + type: "string" + required: false + description: "Dividend yield" + x-description-zh: "股息" + x-description-zh-hk: "股息" + - name: "└ stock_derivatives" + type: "int32[]" + required: false + description: "Types of supported derivatives (1-Option, 2-Warrant)" + x-description-zh: "可提供的衍生品行情类型 (1-期权,2-轮证)" + x-description-zh-hk: "可提供的衍生品行情類型 (1-期權,2-輪證)" + - name: "└ board" + type: "string" + required: false + description: "The board to which the security belongs" + x-description-zh: "标的所属板块" + x-description-zh-hk: "標的所屬板塊" + - id: ws-quote + x-quote-command: quote + x-subgroup: Stocks + name: Quotes + x-name-zh: 实时报价 + x-name-zh-hk: 實時報價 + cmd: 11 + direction: request + description: | + This API is used to obtain the real-time quotes of securities, and supports all types of securities. To view these real-time data streams aggregated into live indices, sector heatmaps, and macro market overviews, you can reference the [Longbridge Global Markets](https://longbridge.com/en/markets). + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message SecurityQuoteResponse { + repeated SecurityQuote secu_quote = 1; + } + ``` + x-description-zh: | + 该接口用于获取标的的实时行情 (支持所有类型标的)。如需查看这些实时数据流汇聚而成的实时指数、板块热力图与宏观市场概览,可参考 [长桥全球市场](https://longbridge.com/en/markets)。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message SecurityQuoteResponse { + repeated SecurityQuote secu_quote = 1; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的實時行情 (支持所有類型標的)。如需查看這些實時數據流匯聚而成的實時指數、板塊熱力圖與宏觀市場概覽,可參考 [長橋全球市場](https://longbridge.com/en/markets)。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message SecurityQuoteResponse { + repeated SecurityQuote secu_quote = 1; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["700.HK", "AAPL.US"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 11]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["700.HK", "AAPL.US"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 11]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = MultiSecurityRequest.encode({ symbol: ["700.HK", "AAPL.US"] }).finish() + const hdr = Buffer.from([0x01, 11]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityQuoteResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = MultiSecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 11); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityQuoteResponse resp = SecurityQuoteResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = MultiSecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 11]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityQuoteResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + MultiSecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(11); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityQuoteResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.MultiSecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 11} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityQuoteResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "secu_quote": [ + { + "symbol": "700.HK", + "last_done": "338.000", + "prev_close": "334.800", + "open": "340.600", + "high": "340.600", + "low": "333.000", + "timestamp": 1651115955, + "volume": 7310881, + "turnover": "2461463161.000" + } + ] + } + x-fields: + - name: "symbol" + type: "string[]" + required: true + description: "Security code list, in ticker.region format; max 500 symbols per request" + x-description-zh: "标的代码列表,使用 ticker.region 格式;每次请求上限 500 个" + x-description-zh-hk: "標的代碼列表,使用 ticker.region 格式;每次請求上限 500 個" + x-response-fields: + - name: "secu_quote" + type: "object[]" + required: false + description: "Securities quote" + x-description-zh: "标的实时行情数据列表" + x-description-zh-hk: "標的實時行情數據列表" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: "└ prev_close" + type: "string" + required: false + description: "Yesterday's close" + x-description-zh: "昨收价" + x-description-zh-hk: "昨收價" + - name: "└ open" + type: "string" + required: false + description: "Open" + x-description-zh: "开盘价" + x-description-zh-hk: "開盤價" + - name: "└ high" + type: "string" + required: false + description: "High" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: "└ low" + type: "string" + required: false + description: "Low" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: "└ timestamp" + type: "int64" + required: false + description: "Time of latest price" + x-description-zh: "最新成交的时间戳" + x-description-zh-hk: "最新成交的時間戳" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "└ trade_status" + type: "int32" + required: false + description: "Security trading status" + x-description-zh: "标的交易状态" + x-description-zh-hk: "標的交易狀態" + - name: "└ pre_market_quote" + type: "object" + required: false + description: "Quote of US pre market" + x-description-zh: "美股盘前交易行情" + x-description-zh-hk: "美股盤前交易行情" + - name: " └ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: " └ timestamp" + type: "int64" + required: false + description: "Time of latest price" + x-description-zh: "最新成交的时间戳" + x-description-zh-hk: "最新成交的時間戳" + - name: " └ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: " └ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: " └ high" + type: "string" + required: false + description: "High" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: " └ low" + type: "string" + required: false + description: "Low" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: " └ prev_close" + type: "string" + required: false + description: "Close of the last trade session" + x-description-zh: "上一个交易阶段的收盘价" + x-description-zh-hk: "上一個交易階段的收盤價" + - name: "└ post_market_quote" + type: "object" + required: false + description: "Quote of US post market" + x-description-zh: "美股盘后交易行情" + x-description-zh-hk: "美股盤後交易行情" + - name: " └ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: " └ timestamp" + type: "int64" + required: false + description: "Time of latest price" + x-description-zh: "最新成交的时间戳" + x-description-zh-hk: "最新成交的時間戳" + - name: " └ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: " └ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: " └ high" + type: "string" + required: false + description: "High" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: " └ low" + type: "string" + required: false + description: "Low" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: " └ prev_close" + type: "string" + required: false + description: "Close of the last trade session" + x-description-zh: "上一个交易阶段的收盘价" + x-description-zh-hk: "上一個交易階段的收盤價" + - name: "└ over_night_quote" + type: "object" + required: false + description: "Quote of US overnight market; requires enable_overnight, returns null otherwise (US stocks only)" + x-description-zh: "美股夜盘交易行情;需开启 enable_overnight 参数,否则返回 null(仅支持美股)" + x-description-zh-hk: "美股夜盤交易行情;需開啟 enable_overnight 參數,否則返回 null(僅支援美股)" + - name: " └ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: " └ timestamp" + type: "int64" + required: false + description: "Time of latest price" + x-description-zh: "最新成交的时间戳" + x-description-zh-hk: "最新成交的時間戳" + - name: " └ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: " └ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: " └ high" + type: "string" + required: false + description: "High" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: " └ low" + type: "string" + required: false + description: "Low" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: " └ prev_close" + type: "string" + required: false + description: "Close of the last trade session" + x-description-zh: "上一个交易阶段的收盘价" + x-description-zh-hk: "上一個交易階段的收盤價" + - id: ws-option-quote + x-quote-command: option + x-subgroup: Options + name: Option Quotes + x-name-zh: 期权实时报价 + x-name-zh-hk: 期權實時報價 + cmd: 12 + direction: request + description: | + This API is used to obtain the real-time quotes of US stock options, including the option-specific data. + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message OptionQuoteResponse { + repeated OptionQuote secu_quote = 1; + } + ``` + x-description-zh: | + 该接口用于获取美股期权标的的实时行情,包括期权的特有数据。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message OptionQuoteResponse { + repeated OptionQuote secu_quote = 1; + } + ``` + x-description-zh-hk: | + 該接口用於獲取美股期權標的的實時行情,包括期權的特有數據。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message OptionQuoteResponse { + repeated OptionQuote secu_quote = 1; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["AAPL220429P162500.US"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 12]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.OptionQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["AAPL220429P162500.US"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 12]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.OptionQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = MultiSecurityRequest.encode({ symbol: ["AAPL220429P162500.US"] }).finish() + const hdr = Buffer.from([0x01, 12]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = OptionQuoteResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = MultiSecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 12); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + OptionQuoteResponse resp = OptionQuoteResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = MultiSecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 12]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = OptionQuoteResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + MultiSecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(12); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + OptionQuoteResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.MultiSecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 12} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.OptionQuoteResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "secu_quote": [ + { + "symbol": "AAPL220429P162500.US", + "last_done": "7.78", + "prev_close": "4.13", + "timestamp": 1651003200, + "volume": 3082, + "turnover": "1813434.00", + "option_extend": { + "implied_volatility": "0.592", + "open_interest": 11463, + "expiry_date": "20220429", + "strike_price": "162.50", + "contract_type": "A", + "direction": "P", + "underlying_symbol": "AAPL.US" + } + } + ] + } + x-fields: + - name: "symbol" + type: "string[]" + required: true + description: "Security code list. obtain the symbol of the options through the optionchain API, for example: [BABA230120C160000.US]. Max 500 symbols per request" + x-description-zh: "标的代码列表,通过期权链接口获取期权标的的 symbol,例如:[BABA230120C160000.US]。每次请求上限 500 个" + x-description-zh-hk: "標的代碼列表,通過期權鏈接口獲取期權標的的 symbol,例如:[BABA230120C160000.US]。每次請求上限 500 個" + x-response-fields: + - name: "secu_quote" + type: "object[]" + required: false + description: "Options quote" + x-description-zh: "期权标的行情数据列表" + x-description-zh-hk: "期權標的行情數據列表" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: "└ prev_close" + type: "string" + required: false + description: "Yesterday's close" + x-description-zh: "昨收价" + x-description-zh-hk: "昨收價" + - name: "└ open" + type: "string" + required: false + description: "Open" + x-description-zh: "开盘价" + x-description-zh-hk: "開盤價" + - name: "└ high" + type: "string" + required: false + description: "High" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: "└ low" + type: "string" + required: false + description: "Low" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: "└ timestamp" + type: "int64" + required: false + description: "Time of latest price" + x-description-zh: "最新成交的时间戳" + x-description-zh-hk: "最新成交的時間戳" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "└ trade_status" + type: "int32" + required: false + description: "Security trading status, see TradeStatus" + x-description-zh: "标的交易状态,详见 TradeStatus" + x-description-zh-hk: "標的交易狀態,詳見 TradeStatus" + - name: "└ option_extend" + type: "object" + required: false + description: "Option extend quote" + x-description-zh: "期权扩展行情" + x-description-zh-hk: "期權擴展行情" + - name: " └ implied_volatility" + type: "string" + required: false + description: "Implied volatility" + x-description-zh: "隐含波动率" + x-description-zh-hk: "隱含波動率" + - name: " └ open_interest" + type: "int64" + required: false + description: "Number of open positions" + x-description-zh: "未平仓数" + x-description-zh-hk: "未平倉數" + - name: " └ expiry_date" + type: "string" + required: false + description: "Expiry date, in YYMMDD format" + x-description-zh: "到期日,使用 YYMMDD 格式" + x-description-zh-hk: "到期日,使用 YYMMDD 格式" + - name: " └ strike_price" + type: "string" + required: false + description: "Strike price" + x-description-zh: "行权价" + x-description-zh-hk: "行權價" + - name: " └ contract_multiplier" + type: "string" + required: false + description: "Contract multiplier" + x-description-zh: "合约乘数" + x-description-zh-hk: "合約乘數" + - name: " └ contract_type" + type: "string" + required: false + description: "Option type: A - American, U - Europe" + x-description-zh: "期权类型:A - 美式,U - 欧式" + x-description-zh-hk: "期權類型:A - 美式,U - 歐式" + - name: " └ contract_size" + type: "string" + required: false + description: "Contract size" + x-description-zh: "合约规模" + x-description-zh-hk: "合約規模" + - name: " └ direction" + type: "string" + required: false + description: "Direction: P - put, C - call" + x-description-zh: "方向:P - put,C - call" + x-description-zh-hk: "方向:P - put,C - call" + - name: " └ historical_volatility" + type: "string" + required: false + description: "Underlying security historical volatility of the option" + x-description-zh: "对应正股的历史波动率" + x-description-zh-hk: "對應正股的歷史波動率" + - name: " └ underlying_symbol" + type: "string" + required: false + description: "Underlying security symbol of the option" + x-description-zh: "对应的正股标的代码" + x-description-zh-hk: "對應的正股標的代碼" + - id: ws-warrant-quote + x-quote-command: warrant + x-subgroup: Warrants + x-subgroup-zh: 轮证 + x-subgroup-zh-hk: 輪證 + name: Warrant Quotes + x-name-zh: 权证实时报价 + x-name-zh-hk: 權證實時報價 + cmd: 13 + direction: request + description: | + This API is used to obtain the real-time quotes of HK warrants, including the warrant-specific data. + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message WarrantQuoteResponse { + repeated WarrantQuote secu_quote = 2; + } + ``` + x-description-zh: | + 该接口用于获取港股轮证标的的实时行情,包括轮证的特有数据。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message WarrantQuoteResponse { + repeated WarrantQuote secu_quote = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取港股輪證標的的實時行情,包括輪證的特有數據。 + + ```protobuf + message MultiSecurityRequest { + repeated string symbol = 1; + } + + message WarrantQuoteResponse { + repeated WarrantQuote secu_quote = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["66642.HK"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 13]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.WarrantQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.MultiSecurityRequest(symbol=["66642.HK"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 13]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.WarrantQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = MultiSecurityRequest.encode({ symbol: ["66642.HK"] }).finish() + const hdr = Buffer.from([0x01, 13]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = WarrantQuoteResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = MultiSecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 13); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + WarrantQuoteResponse resp = WarrantQuoteResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = MultiSecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 13]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = WarrantQuoteResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + MultiSecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(13); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + WarrantQuoteResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.MultiSecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 13} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.WarrantQuoteResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "secu_quote": [ + { + "symbol": "66642.HK", + "last_done": "0.345", + "prev_close": "0.365", + "timestamp": 1651130421, + "volume": 200000, + "turnover": "69000.000", + "warrant_extend": { + "implied_volatility": "0.319", + "expiry_date": "20220830", + "category": "Bear", + "strike_price": "23200.000", + "underlying_symbol": "HSI.HK" + } + } + ] + } + x-fields: + - name: "symbol" + type: "string[]" + required: true + description: "Security code list, in ticker.region format, for example: [13447.HK]. Max 500 symbols per request" + x-description-zh: "标的代码列表,使用 ticker.region 格式,例如:[13447.HK]。每次请求上限 500 个" + x-description-zh-hk: "標的代碼列表,使用 ticker.region 格式,例如:[13447.HK]。每次請求上限 500 個" + x-response-fields: + - name: "secu_quote" + type: "object[]" + required: false + description: "Warrants quote" + x-description-zh: "期权标的行情数据列表" + x-description-zh-hk: "期權標的行情數據列表" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: "└ prev_close" + type: "string" + required: false + description: "Yesterday's close" + x-description-zh: "昨收价" + x-description-zh-hk: "昨收價" + - name: "└ open" + type: "string" + required: false + description: "Open" + x-description-zh: "开盘价" + x-description-zh-hk: "開盤價" + - name: "└ high" + type: "string" + required: false + description: "High" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: "└ low" + type: "string" + required: false + description: "Low" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: "└ timestamp" + type: "int64" + required: false + description: "Time of latest price" + x-description-zh: "最新成交的时间戳" + x-description-zh-hk: "最新成交的時間戳" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "└ trade_status" + type: "int32" + required: false + description: "Security trading status, see TradeStatus" + x-description-zh: "标的交易状态,详见 TradeStatus" + x-description-zh-hk: "標的交易狀態,詳見 TradeStatus" + - name: "└ warrant_extend" + type: "object" + required: false + description: "Warrant extend quote" + x-description-zh: "轮证扩展行情" + x-description-zh-hk: "輪證擴展行情" + - name: " └ implied_volatility" + type: "string" + required: false + description: "Implied volatility" + x-description-zh: "引申波幅" + x-description-zh-hk: "引申波幅" + - name: " └ expiry_date" + type: "string" + required: false + description: "Expiry date, in YYMMDD format" + x-description-zh: "到期日,使用 YYMMDD 格式" + x-description-zh-hk: "到期日,使用 YYMMDD 格式" + - name: " └ last_trade_date" + type: "string" + required: false + description: "Last tradable date, in YYMMDD format" + x-description-zh: "最后交易日,使用 YYMMDD 格式" + x-description-zh-hk: "最後交易日,使用 YYMMDD 格式" + - name: " └ outstanding_ratio" + type: "string" + required: false + description: "Outstanding ratio" + x-description-zh: "街货比" + x-description-zh-hk: "街貨比" + - name: " └ outstanding_qty" + type: "int64" + required: false + description: "Outstanding quantity" + x-description-zh: "街货量" + x-description-zh-hk: "街貨量" + - name: " └ conversion_ratio" + type: "string" + required: false + description: "Conversion ratio" + x-description-zh: "换股比率" + x-description-zh-hk: "換股比率" + - name: " └ category" + type: "string" + required: false + description: "Warrant type: Call, Put, Bull, Bear, Inline" + x-description-zh: "轮证类型:Call 认购证,Put 认沽证,Bull 牛证,Bear 熊证,Inline 界内证" + x-description-zh-hk: "輪證類型:Call 認購證,Put 認沽證,Bull 牛證,Bear 熊證,Inline 界內證" + - name: " └ strike_price" + type: "string" + required: false + description: "Strike price" + x-description-zh: "行权价" + x-description-zh-hk: "行權價" + - name: " └ upper_strike_price" + type: "string" + required: false + description: "Upper bound price" + x-description-zh: "上限价" + x-description-zh-hk: "上限價" + - name: " └ lower_strike_price" + type: "string" + required: false + description: "Lower bound price" + x-description-zh: "下限价" + x-description-zh-hk: "下限價" + - name: " └ call_price" + type: "string" + required: false + description: "Call price" + x-description-zh: "收回价" + x-description-zh-hk: "收回價" + - name: " └ underlying_symbol" + type: "string" + required: false + description: "Underlying security symbol of the option" + x-description-zh: "对应的正股标的代码" + x-description-zh-hk: "對應的正股標的代碼" + - id: ws-pull-depth + x-quote-command: depth + x-subgroup: Stocks + name: Depth + x-name-zh: 盘口 + x-name-zh-hk: 盤口 + cmd: 14 + direction: request + description: | + This API is used to obtain the depth data of security. + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message SecurityDepthResponse { + string symbol = 1; + repeated Depth ask = 2; + repeated Depth bid = 3; + } + ``` + x-description-zh: | + 该接口用于获取标的的盘口数据。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message SecurityDepthResponse { + string symbol = 1; + repeated Depth ask = 2; + repeated Depth bid = 3; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的盤口數據。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message SecurityDepthResponse { + string symbol = 1; + repeated Depth ask = 2; + repeated Depth bid = 3; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 14]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityDepthResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 14]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityDepthResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityRequest.encode({ symbol: "700.HK" }).finish() + const hdr = Buffer.from([0x01, 14]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityDepthResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 14); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityDepthResponse resp = SecurityDepthResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 14]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityDepthResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(14); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityDepthResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 14} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityDepthResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "700.HK", + "ask": [ + { + "position": 1, + "price": "335.000", + "volume": 500, + "order_num": 1 + } + ], + "bid": [ + { + "position": 1, + "price": "334.800", + "volume": 69400, + "order_num": 13 + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, e.g. 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "ask" + type: "object[]" + required: false + description: "Ask depth" + x-description-zh: "卖盘" + x-description-zh-hk: "賣盤" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ price" + type: "string" + required: false + description: "Price" + x-description-zh: "价格" + x-description-zh-hk: "價格" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "挂单量" + x-description-zh-hk: "掛單量" + - name: "└ order_num" + type: "int64" + required: false + description: "Number of orders" + x-description-zh: "订单数量" + x-description-zh-hk: "訂單數量" + - name: "bid" + type: "object[]" + required: false + description: "Bid depth" + x-description-zh: "买盘" + x-description-zh-hk: "買盤" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ price" + type: "string" + required: false + description: "Price" + x-description-zh: "价格" + x-description-zh-hk: "價格" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "挂单量" + x-description-zh-hk: "掛單量" + - name: "└ order_num" + type: "int64" + required: false + description: "Number of orders" + x-description-zh: "订单数量" + x-description-zh-hk: "訂單數量" + - id: ws-pull-brokers + x-quote-command: brokers + x-subgroup: Stocks + name: Broker Queue + x-name-zh: 经纪队列 + x-name-zh-hk: 經紀隊列 + cmd: 15 + direction: request + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + This API is used to obtain the real-time broker queue data of security. + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message SecurityBrokersResponse { + string symbol = 1; + repeated Brokers ask_brokers = 2; + repeated Brokers bid_brokers = 3; + } + ``` + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可查询任意标的,包括美股。 + ::: + + 该接口用于获取标的的实时经纪队列数据。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message SecurityBrokersResponse { + string symbol = 1; + repeated Brokers ask_brokers = 2; + repeated Brokers bid_brokers = 3; + } + ``` + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可查詢任意標的,包括美股。 + ::: + + 該接口用於獲取標的的實時經紀隊列數據。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message SecurityBrokersResponse { + string symbol = 1; + repeated Brokers ask_brokers = 2; + repeated Brokers bid_brokers = 3; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 15]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityBrokersResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 15]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityBrokersResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityRequest.encode({ symbol: "700.HK" }).finish() + const hdr = Buffer.from([0x01, 15]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityBrokersResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 15); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityBrokersResponse resp = SecurityBrokersResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 15]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityBrokersResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(15); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityBrokersResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 15} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityBrokersResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "700.HK", + "ask_brokers": [ + { + "position": 1, + "broker_ids": [ + 7358, + 9057, + 9028, + 7364 + ] + } + ], + "bid_brokers": [ + { + "position": 1, + "broker_ids": [ + 6996, + 5465, + 8026, + 8304, + 4978 + ] + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, e.g. 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "ask_brokers" + type: "object[]" + required: false + description: "Ask brokers" + x-description-zh: "卖盘经纪队列" + x-description-zh-hk: "賣盤經紀隊列" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ broker_ids" + type: "int32[]" + required: false + description: "Broker IDs, obtained through the Get Broker IDs API" + x-description-zh: "券商席位 ID,通过获取券商席位 ID 接口获取" + x-description-zh-hk: "券商席位 ID,通過獲取券商席位 ID 接口獲取" + - name: "bid_brokers" + type: "object[]" + required: false + description: "Bid brokers" + x-description-zh: "买盘经纪队列" + x-description-zh-hk: "買盤經紀隊列" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ broker_ids" + type: "int32[]" + required: false + description: "Broker IDs, obtained through the Get Broker IDs API" + x-description-zh: "券商席位 ID,通过获取券商席位 ID 接口获取" + x-description-zh-hk: "券商席位 ID,通過獲取券商席位 ID 接口獲取" + - id: ws-broker-ids + x-subgroup: Stocks + name: Broker IDs + x-name-zh: 经纪商席位 + x-name-zh-hk: 經紀商席位 + cmd: 16 + direction: request + description: | + This API is used to obtain participant IDs data (which can be synchronized once a day). + + ```protobuf + message ParticipantBrokerIdsResponse { + repeated ParticipantInfo participant_broker_numbers = 1; + } + + message ParticipantInfo { + repeated int32 broker_ids = 1; + string participant_name_cn = 2; + string participant_name_en = 3; + string participant_name_hk = 4; + } + ``` + x-description-zh: | + 该接口用于获取券商席位 ID 数据 (可每天同步一次)。 + + ```protobuf + message ParticipantBrokerIdsResponse { + repeated ParticipantInfo participant_broker_numbers = 1; + } + + message ParticipantInfo { + repeated int32 broker_ids = 1; + string participant_name_cn = 2; + string participant_name_en = 3; + string participant_name_hk = 4; + } + ``` + x-description-zh-hk: | + 該接口用於獲取券商席位 ID 數據 (可每天同步一次)。 + + ```protobuf + message ParticipantBrokerIdsResponse { + repeated ParticipantInfo participant_broker_numbers = 1; + } + + message ParticipantInfo { + repeated int32 broker_ids = 1; + string participant_name_cn = 2; + string participant_name_en = 3; + string participant_name_hk = 4; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = b"" + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 16]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.ParticipantBrokerIdsResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = b"" + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 16]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.ParticipantBrokerIdsResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = new Uint8Array() + const hdr = Buffer.from([0x01, 16]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = ParticipantBrokerIdsResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = new byte[0]; + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 16); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + ParticipantBrokerIdsResponse resp = ParticipantBrokerIdsResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body: Vec = Vec::new(); + let mut pkt = vec![0x01u8, 16]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = ParticipantBrokerIdsResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + std::string body; + std::string pkt; + pkt.push_back(0x01); pkt.push_back(16); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + ParticipantBrokerIdsResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + var body []byte + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 16} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.ParticipantBrokerIdsResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "participant_broker_numbers": [ + { + "broker_ids": [ + 7738, + 7739 + ], + "participant_name_cn": "华兴金融 (香港)", + "participant_name_en": "China Renaissance(HK)", + "participant_name_hk": "華興金融 (香港)" + } + ] + } + x-response-fields: + - name: "participant_broker_numbers" + type: "object[]" + required: false + description: "participant data" + x-description-zh: "券商席位" + x-description-zh-hk: "券商席位" + - name: "└ broker_ids" + type: "int32[]" + required: false + description: "broker IDs" + x-description-zh: "券商对应的多个席位 ID" + x-description-zh-hk: "券商對應的多個席位 ID" + - name: "└ participant_name_cn" + type: "string" + required: false + description: "participant name (zh-CN)" + x-description-zh: "券商名称 (简)" + x-description-zh-hk: "券商名稱 (簡)" + - name: "└ participant_name_en" + type: "string" + required: false + description: "participant name (en)" + x-description-zh: "券商名称 (英)" + x-description-zh-hk: "券商名稱 (英)" + - name: "└ participant_name_hk" + type: "string" + required: false + description: "participant name (zh-HK)" + x-description-zh: "券商名称 (繁)" + x-description-zh-hk: "券商名稱 (繁)" + - id: ws-pull-trade + x-quote-command: trades + x-subgroup: Stocks + name: Trades + x-name-zh: 成交明细 + x-name-zh-hk: 成交明細 + cmd: 17 + direction: request + description: | + This API is used to obtain the trades data of security. + + ```protobuf + message SecurityTradeRequest { + string symbol = 1; + int32 count = 2; + } + + message SecurityTradeResponse { + string symbol = 1; + repeated Trade trades = 2; + } + ``` + x-description-zh: | + 该接口用于获取标的的成交明细数据。 + + ```protobuf + message SecurityTradeRequest { + string symbol = 1; + int32 count = 2; + } + + message SecurityTradeResponse { + string symbol = 1; + repeated Trade trades = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的成交明細數據。 + + ```protobuf + message SecurityTradeRequest { + string symbol = 1; + int32 count = 2; + } + + message SecurityTradeResponse { + string symbol = 1; + repeated Trade trades = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityTradeRequest(symbol="700.HK", count=10).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 17]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityTradeResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityTradeRequest(symbol="700.HK", count=10).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 17]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityTradeResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityTradeRequest.encode({ symbol: "700.HK", count: 10 }).finish() + const hdr = Buffer.from([0x01, 17]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityTradeResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityTradeRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 17); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityTradeResponse resp = SecurityTradeResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityTradeRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 17]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityTradeResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityTradeRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(17); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityTradeResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityTradeRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 17} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityTradeResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "AAPL.US", + "trades": [ + { + "price": "158.760", + "volume": 1, + "timestamp": 1651103979, + "trade_type": "I", + "direction": 0, + "trade_session": 2 + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, e.g. 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + - name: "count" + type: "int32" + required: true + description: "Count of trades; max 1000 per request" + x-description-zh: "请求的逐笔明细数量;最大 1000" + x-description-zh-hk: "請求的逐筆明細數量;最大 1000" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "trades" + type: "object[]" + required: false + description: "Trades data" + x-description-zh: "逐笔明细数据" + x-description-zh-hk: "逐筆明細數據" + - name: "└ price" + type: "string" + required: false + description: "Price" + x-description-zh: "价格" + x-description-zh-hk: "價格" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ timestamp" + type: "int64" + required: false + description: "Time of trading" + x-description-zh: "成交时间" + x-description-zh-hk: "成交時間" + - name: "└ trade_type" + type: "string" + required: false + description: "Trade type" + x-description-zh: "交易类型说明" + x-description-zh-hk: "交易類型說明" + - name: "└ direction" + type: "int32" + required: false + description: "Trade direction (0-neutral, 1-down, 2-up)" + x-description-zh: "交易方向 (0-neutral, 1-down, 2-up)" + x-description-zh-hk: "交易方向 (0-neutral, 1-down, 2-up)" + - name: "└ trade_session" + type: "int32" + required: false + description: "Trade session" + x-description-zh: "交易时段" + x-description-zh-hk: "交易時段" + - id: ws-intraday + x-quote-command: intraday + x-subgroup: Stocks + name: Intraday + x-name-zh: 分时 + x-name-zh-hk: 分時 + cmd: 18 + direction: request + description: | + This API is used to obtain the intraday data of security. + + ```protobuf + message SecurityIntradayRequest { + string symbol = 1; + } + + message SecurityIntradayResponse { + string symbol = 1; + repeated Line lines = 2; + } + ``` + x-description-zh: | + 该接口用于获取标的的当日分时数据。 + + ```protobuf + message SecurityIntradayRequest { + string symbol = 1; + } + + message SecurityIntradayResponse { + string symbol = 1; + repeated Line lines = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的當日分時數據。 + + ```protobuf + message SecurityIntradayRequest { + string symbol = 1; + } + + message SecurityIntradayResponse { + string symbol = 1; + repeated Line lines = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityIntradayRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 18]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityIntradayResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityIntradayRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 18]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityIntradayResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityIntradayRequest.encode({ symbol: "700.HK" }).finish() + const hdr = Buffer.from([0x01, 18]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityIntradayResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityIntradayRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 18); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityIntradayResponse resp = SecurityIntradayResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityIntradayRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 18]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityIntradayResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityIntradayRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(18); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityIntradayResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityIntradayRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 18} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityIntradayResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "700.HK", + "lines": [ + { + "price": "330.400", + "timestamp": 1651023000, + "volume": 375870, + "turnover": "123949699.000", + "avg_price": "329.767470" + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, e.g. 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, e.g. AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "lines" + type: "object[]" + required: false + description: "Intraday line data" + x-description-zh: "分时数据" + x-description-zh-hk: "分時數據" + - name: "└ price" + type: "string" + required: false + description: "Close price of the minute" + x-description-zh: "当前分钟的收盘价格" + x-description-zh-hk: "當前分鐘的收盤價格" + - name: "└ timestamp" + type: "int64" + required: false + description: "Start time stamp of the minute" + x-description-zh: "当前分钟的开始时间" + x-description-zh-hk: "當前分鐘的開始時間" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "└ avg_price" + type: "string" + required: false + description: "Average price" + x-description-zh: "均价" + x-description-zh-hk: "均價" + - id: ws-pull-candlestick + x-quote-command: kline + x-subgroup: Stocks + name: Candlesticks + x-name-zh: K 线 + x-name-zh-hk: K 線 + cmd: 19 + direction: request + description: | + This API is used to obtain the candlestick data of security. + + ```protobuf + message SecurityCandlestickRequest { + string symbol = 1; + Period period = 2; + int32 count = 3; + AdjustType adjust_type = 4; + int32 trade_session = 5; + } + + message SecurityCandlestickResponse { + string symbol = 1; + repeated Candlestick candlesticks = 2; + } + ``` + x-description-zh: | + 该接口用于获取标的的 K 线数据。 + + ```protobuf + message SecurityCandlestickRequest { + string symbol = 1; + Period period = 2; + int32 count = 3; + AdjustType adjust_type = 4; + int32 trade_session = 5; + } + + message SecurityCandlestickResponse { + string symbol = 1; + repeated Candlestick candlesticks = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的 K 線數據。 + + ```protobuf + message SecurityCandlestickRequest { + string symbol = 1; + Period period = 2; + int32 count = 3; + AdjustType adjust_type = 4; + int32 trade_session = 5; + } + + message SecurityCandlestickResponse { + string symbol = 1; + repeated Candlestick candlesticks = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityCandlestickRequest(symbol="700.HK", period=1000, count=10, adjust_type=0).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 19]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityCandlestickResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityCandlestickRequest(symbol="700.HK", period=1000, count=10, adjust_type=0).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 19]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityCandlestickResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityCandlestickRequest.encode({ symbol: "700.HK", period: 1000, count: 10, adjust_type: 0 }).finish() + const hdr = Buffer.from([0x01, 19]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityCandlestickResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityCandlestickRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 19); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityCandlestickResponse resp = SecurityCandlestickResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityCandlestickRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 19]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityCandlestickResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityCandlestickRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(19); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityCandlestickResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityCandlestickRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 19} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityCandlestickResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "700.HK", + "candlesticks": [ + { + "close": "362.000", + "open": "364.600", + "low": "361.600", + "high": "368.800", + "volume": 10853604, + "turnover": "3954556819.000", + "timestamp": 1650384000 + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, e.g. 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + - name: "period" + type: "int32" + required: true + description: "Candlestick period" + x-description-zh: "K 线周期" + x-description-zh-hk: "K 線週期" + - name: "count" + type: "int32" + required: true + description: "Count of candlestick; max 1000" + x-description-zh: "数据数量;最大 1000" + x-description-zh-hk: "數據數量;最大 1000" + - name: "adjust_type" + type: "int32" + required: true + description: "Adjustment type" + x-description-zh: "复权类型" + x-description-zh-hk: "復權類型" + - name: "trade_session" + type: "int32" + required: false + description: "Trading session, 0: intraday, 100: All (pre, intraday, post, overnight)" + x-description-zh: "交易时段,0: 盘中,100: 所有(盘前,盘中,盘后,夜盘)" + x-description-zh-hk: "交易時段,0: 盤中,100: 所有延長時段(盤前,盤中,盤後,夜盤)" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, e.g. AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "candlesticks" + type: "object[]" + required: false + description: "Candlestick data" + x-description-zh: "K 线数据" + x-description-zh-hk: "K 線數據" + - name: "└ close" + type: "string" + required: false + description: "Close price" + x-description-zh: "当前周期收盘价" + x-description-zh-hk: "當前週期收盤價" + - name: "└ open" + type: "string" + required: false + description: "Open price" + x-description-zh: "当前周期开盘价" + x-description-zh-hk: "當前週期開盤價" + - name: "└ low" + type: "string" + required: false + description: "Low price" + x-description-zh: "当前周期最低价" + x-description-zh-hk: "當前週期最低價" + - name: "└ high" + type: "string" + required: false + description: "High price" + x-description-zh: "当前周期最高价" + x-description-zh-hk: "當前週期最高價" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "当前周期成交量" + x-description-zh-hk: "當前週期成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "当前周期成交额" + x-description-zh-hk: "當前週期成交額" + - name: "└ timestamp" + type: "int64" + required: false + description: "Timestamp" + x-description-zh: "当前周期的时间戳" + x-description-zh-hk: "當前週期的時間戳" + - name: "└ trade_session" + type: "int32" + required: false + description: "Trade session" + x-description-zh: "交易时段" + x-description-zh-hk: "交易時段" + - id: ws-optionchain-date + x-subgroup: Options + name: Option Chain Expiry Dates + x-name-zh: 期权链到期日 + x-name-zh-hk: 期權鏈到期日 + cmd: 20 + direction: request + description: | + This API is used to obtain the the list of expiration dates of option chain + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message OptionChainDateListResponse { + repeated string expiry_date = 1; + } + ``` + x-description-zh: | + 该接口用于获取标的的期权链到期日列表。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message OptionChainDateListResponse { + repeated string expiry_date = 1; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的期權鏈到期日列表。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message OptionChainDateListResponse { + repeated string expiry_date = 1; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="AAPL.US").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 20]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.OptionChainDateListResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="AAPL.US").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 20]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.OptionChainDateListResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityRequest.encode({ symbol: "AAPL.US" }).finish() + const hdr = Buffer.from([0x01, 20]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = OptionChainDateListResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 20); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + OptionChainDateListResponse resp = OptionChainDateListResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 20]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = OptionChainDateListResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(20); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + OptionChainDateListResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 20} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.OptionChainDateListResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "expiry_date": [ + "20220422", + "20220429", + "20220506" + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, for example: 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + x-response-fields: + - name: "expiry_date" + type: "string[]" + required: false + description: "option chain expiry dates list, in YYMMDD format" + x-description-zh: "标的对应的期权链到期日列表,使用 YYMMDD 格式" + x-description-zh-hk: "標的對應的期權鏈到期日列表,使用 YYMMDD 格式" + - id: ws-optionchain-strike + x-subgroup: Options + name: Option Chain Info + x-name-zh: 期权链行权价 + x-name-zh-hk: 期權鏈行權價 + cmd: 21 + direction: request + description: | + This API is used to obtain a list of option securities by the option chain expiry date. + + ```protobuf + message OptionChainDateStrikeInfoRequest { + string symbol = 1; + string expiry_date = 2; + } + + message OptionChainDateStrikeInfoResponse { + repeated StrikePriceInfo strike_price_info = 1; + } + ``` + x-description-zh: | + 该接口用于获取标的的期权链到期日期权标的列表。 + + ```protobuf + message OptionChainDateStrikeInfoRequest { + string symbol = 1; + string expiry_date = 2; + } + + message OptionChainDateStrikeInfoResponse { + repeated StrikePriceInfo strike_price_info = 1; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的期權鏈到期日期權標的列表。 + + ```protobuf + message OptionChainDateStrikeInfoRequest { + string symbol = 1; + string expiry_date = 2; + } + + message OptionChainDateStrikeInfoResponse { + repeated StrikePriceInfo strike_price_info = 1; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.OptionChainDateStrikeInfoRequest(symbol="AAPL.US", expiry_date="20220429").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 21]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.OptionChainDateStrikeInfoResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.OptionChainDateStrikeInfoRequest(symbol="AAPL.US", expiry_date="20220429").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 21]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.OptionChainDateStrikeInfoResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = OptionChainDateStrikeInfoRequest.encode({ symbol: "AAPL.US", expiry_date: "20220429" }).finish() + const hdr = Buffer.from([0x01, 21]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = OptionChainDateStrikeInfoResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = OptionChainDateStrikeInfoRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 21); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + OptionChainDateStrikeInfoResponse resp = OptionChainDateStrikeInfoResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = OptionChainDateStrikeInfoRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 21]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = OptionChainDateStrikeInfoResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + OptionChainDateStrikeInfoRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(21); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + OptionChainDateStrikeInfoResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.OptionChainDateStrikeInfoRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 21} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.OptionChainDateStrikeInfoResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "strike_price_info": [ + { + "price": "100", + "call_symbol": "AAPL220429C100000.US", + "put_symbol": "AAPL220429P100000.US", + "standard": true + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, for example: 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + - name: "expiry_date" + type: "string" + required: true + description: "Option expiry date, in YYMMDD format, for example: 20220429, obtained by Option Expiry Date API" + x-description-zh: "期权到期日,使用 YYMMDD 格式,例如:20220429,通过期权到期日接口获取" + x-description-zh-hk: "期權到期日,使用 YYMMDD 格式,例如:20220429,通過期權到期日接口獲取" + x-response-fields: + - name: "strike_price_info" + type: "object[]" + required: false + description: "Option security info" + x-description-zh: "到期日期权标的列表" + x-description-zh-hk: "到期日期權標的列表" + - name: "└ price" + type: "string" + required: false + description: "Strike price" + x-description-zh: "行权价" + x-description-zh-hk: "行權價" + - name: "└ call_symbol" + type: "string" + required: false + description: "Security code of call option" + x-description-zh: "CALL 期权标的代码" + x-description-zh-hk: "CALL 期權標的代碼" + - name: "└ put_symbol" + type: "string" + required: false + description: "Security code of put option" + x-description-zh: "PUT 期权标的代码" + x-description-zh-hk: "PUT 期權標的代碼" + - name: "└ standard" + type: "bool" + required: false + description: "Is standard" + x-description-zh: "是否标准期权" + x-description-zh-hk: "是否標準期權" + - id: ws-issuers + x-quote-command: warrant + x-subgroup: Warrants + x-subgroup-zh: 轮证 + x-subgroup-zh-hk: 輪證 + name: Warrant Issuers + x-name-zh: 权证发行商 + x-name-zh-hk: 權證發行商 + cmd: 22 + direction: request + description: | + This API is used to obtain the warrant issuer IDs data (which can be synchronized once a day). + + ```protobuf + message IssuerInfoResponse { + repeated IssuerInfo issuer_info = 1; + } + + message IssuerInfo { + int32 id = 1; + string name_cn = 2; + string name_en = 3; + string name_hk = 4; + } + ``` + x-description-zh: | + 该接口用于获取轮证发行商 ID 数据 (可每天同步一次)。 + + ```protobuf + message IssuerInfoResponse { + repeated IssuerInfo issuer_info = 1; + } + + message IssuerInfo { + int32 id = 1; + string name_cn = 2; + string name_en = 3; + string name_hk = 4; + } + ``` + x-description-zh-hk: | + 該接口用於獲取輪證發行商 ID 數據 (可每天同步一次)。 + + ```protobuf + message IssuerInfoResponse { + repeated IssuerInfo issuer_info = 1; + } + + message IssuerInfo { + int32 id = 1; + string name_cn = 2; + string name_en = 3; + string name_hk = 4; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = b"" + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 22]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.IssuerInfoResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = b"" + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 22]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.IssuerInfoResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = new Uint8Array() + const hdr = Buffer.from([0x01, 22]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = IssuerInfoResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = new byte[0]; + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 22); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + IssuerInfoResponse resp = IssuerInfoResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body: Vec = Vec::new(); + let mut pkt = vec![0x01u8, 22]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = IssuerInfoResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + std::string body; + std::string pkt; + pkt.push_back(0x01); pkt.push_back(22); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + IssuerInfoResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + var body []byte + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 22} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.IssuerInfoResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "issuer_info": [ + { + "id": 15, + "name_cn": "瑞银", + "name_en": "UB", + "name_hk": "瑞銀" + } + ] + } + x-response-fields: + - name: "issuer_info" + type: "object[]" + required: false + description: "Issuer information" + x-description-zh: "发行机构信息" + x-description-zh-hk: "發行機構信息" + - name: "└ id" + type: "int32" + required: false + description: "Issuer ID" + x-description-zh: "机构 ID" + x-description-zh-hk: "機構 ID" + - name: "└ name_cn" + type: "string" + required: false + description: "Issuer Name (zh-CN)" + x-description-zh: "机构名称 (简)" + x-description-zh-hk: "機構名稱 (簡)" + - name: "└ name_en" + type: "string" + required: false + description: "Issuer Name (en)" + x-description-zh: "机构名称 (英)" + x-description-zh-hk: "機構名稱 (英)" + - name: "└ name_hk" + type: "string" + required: false + description: "Issuer Name (zh-HK)" + x-description-zh: "机构名称 (繁)" + x-description-zh-hk: "機構名稱 (繁)" + - id: ws-warrant-filter + x-quote-command: warrant + x-subgroup: Warrants + x-subgroup-zh: 轮证 + x-subgroup-zh-hk: 輪證 + name: Warrant Screener + x-name-zh: 权证筛选 + x-name-zh-hk: 權證篩選 + cmd: 23 + direction: request + description: | + This API is used to obtain the quotes of HK warrants, and supports sorting and filtering. + + ```protobuf + message WarrantFilterListRequest { + string symbol = 1; + FilterConfig filter_config = 2; + int32 language = 3; + } + + message WarrantFilterListResponse { + repeated FilterWarrant warrant_list = 1; + int32 total_count = 2; + } + ``` + x-description-zh: | + 该接口用于获取轮证行情列表数据,支持按不同字段排序和筛选轮证。 + + ```protobuf + message WarrantFilterListRequest { + string symbol = 1; + FilterConfig filter_config = 2; + int32 language = 3; + } + + message WarrantFilterListResponse { + repeated FilterWarrant warrant_list = 1; + int32 total_count = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取輪證行情列表數據,支持按不同字段排序和篩選輪證。 + + ```protobuf + message WarrantFilterListRequest { + string symbol = 1; + FilterConfig filter_config = 2; + int32 language = 3; + } + + message WarrantFilterListResponse { + repeated FilterWarrant warrant_list = 1; + int32 total_count = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.WarrantFilterListRequest(symbol="700.HK", filter_config=quote_pb2.FilterConfig(sort_by=1, sort_order=0, sort_offset=0, sort_count=20), language=1).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 23]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.WarrantFilterListResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.WarrantFilterListRequest(symbol="700.HK", filter_config=quote_pb2.FilterConfig(sort_by=1, sort_order=0, sort_offset=0, sort_count=20), language=1).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 23]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.WarrantFilterListResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = WarrantFilterListRequest.encode({ symbol: "700.HK", filter_config: { sort_by: 1, sort_order: 0, sort_offset: 0, sort_count: 20 }, language: 1 }).finish() + const hdr = Buffer.from([0x01, 23]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = WarrantFilterListResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = WarrantFilterListRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 23); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + WarrantFilterListResponse resp = WarrantFilterListResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = WarrantFilterListRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 23]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = WarrantFilterListResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + WarrantFilterListRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(23); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + WarrantFilterListResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.WarrantFilterListRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 23} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.WarrantFilterListResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "warrant_list": [ + { + "symbol": "13157.HK", + "name": "MBTENCT@EP2207A", + "last_done": "2.26", + "change_rate": "-0.0216", + "expiry_date": "20220705", + "strike_price": "442.233", + "status": 4 + } + ], + "total_count": 1197 + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, for example: 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + - name: "filter_config" + type: "object" + required: true + description: "Filter conditions" + x-description-zh: "筛选条件" + x-description-zh-hk: "篩選條件" + - name: "└ sort_by" + type: "int32" + required: true + description: "Which data to sort by, for example: 0, see the OrderSequence field of the response data" + x-description-zh: "根据哪一项数据进行排序,例如:0,序号见响应数据 OrderSequence 字段" + x-description-zh-hk: "根據哪一項數據進行排序,例如:0,序號見響應數據 OrderSequence 字段" + - name: "└ sort_order" + type: "int32" + required: true + description: "Order: 0 - Ascending, 1 - Descending" + x-description-zh: "升降顺序:0 - 升序,1 - 降序" + x-description-zh-hk: "升降順序:0 - 升序,1 - 降序" + - name: "└ sort_offset" + type: "int32" + required: true + description: "The first data offset of paging, for example: 0" + x-description-zh: "分页的第一条数据偏移量,例如 0" + x-description-zh-hk: "分頁的第一條數據偏移量,例如 0" + - name: "└ sort_count" + type: "int32" + required: true + description: "Number of items per page, for example: 20, no pagination when filling in 0" + x-description-zh: "分页的每一页数量,例如 20,填 0 时不分页" + x-description-zh-hk: "分頁的每一頁數量,例如 20,填 0 時不分頁" + - name: "└ type" + type: "int32[]" + required: false + description: "Filter warrant type: 0 Call, 1 Put, 2 Bull, 3 Bear, 4 Inline" + x-description-zh: "筛选轮证类型:0 认购,1 认沽,2 牛证,3 熊证,4 界内证" + x-description-zh-hk: "篩選輪證類型:0 認購,1 認沽,2 牛證,3 熊證,4 界內證" + - name: "└ issuer" + type: "int32[]" + required: false + description: "Filter issuer example: [12,14], obtain Issuer ID through API" + x-description-zh: "筛选发行商,例如:[12,14],发行商 ID 通过接口获取" + x-description-zh-hk: "篩選發行商,例如:[12,14],發行商 ID 通過接口獲取" + - name: "└ expiry_date" + type: "int32[]" + required: false + description: "Filter expiry date: 1 <3 months, 2 3-6 months, 3 6-12 months, 4 >12 months" + x-description-zh: "筛选轮证过期时间:1 低于 3 个月,2 3-6 个月,3 6-12 个月,4 大于 12 个月" + x-description-zh-hk: "篩選輪證過期時間:1 低於 3 個月,2 3-6 個月,3 6-12 個月,4 大於 12 個月" + - name: "└ price_type" + type: "int32[]" + required: false + description: "Filter in/out of bounds: 1 In bounds, 2 Out bounds" + x-description-zh: "筛选价内价外:1 价内,2 价外" + x-description-zh-hk: "篩選價內價外:1 價內,2 價外" + - name: "└ status" + type: "int32[]" + required: false + description: "Filter status: 2 Suspend trading, 3 Prepare List, 4 Normal" + x-description-zh: "筛选状态:2 终止交易,3 等待上市,4 正常" + x-description-zh-hk: "篩選狀態:2 終止交易,3 等待上市,4 正常" + - name: "language" + type: "int32" + required: true + description: "Language: 0 zh-CN, 1 en, 2 zh-HK" + x-description-zh: "响应的语言:0 简体,1 English, 2 繁体" + x-description-zh-hk: "響應的語言:0 簡體,1 English, 2 繁體" + x-response-fields: + - name: "warrant_list" + type: "object[]" + required: false + description: "Filtered warrant data list" + x-description-zh: "涡轮筛选数据列表" + x-description-zh-hk: "渦輪篩選數據列表" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ name" + type: "string" + required: false + description: "Security name" + x-description-zh: "标的名称" + x-description-zh-hk: "標的名稱" + - name: "└ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: "└ change_rate" + type: "string" + required: false + description: "Quote change rate" + x-description-zh: "涨跌幅" + x-description-zh-hk: "漲跌幅" + - name: "└ change_val" + type: "string" + required: false + description: "Quote change" + x-description-zh: "涨跌额" + x-description-zh-hk: "漲跌額" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "└ expiry_date" + type: "string" + required: false + description: "Expiry date, in YYMMDD format" + x-description-zh: "到期日,使用 YYMMDD 格式" + x-description-zh-hk: "到期日,使用 YYMMDD 格式" + - name: "└ strike_price" + type: "string" + required: false + description: "Strike price" + x-description-zh: "行权价" + x-description-zh-hk: "行權價" + - name: "└ upper_strike_price" + type: "string" + required: false + description: "Upper bound price" + x-description-zh: "上限价" + x-description-zh-hk: "上限價" + - name: "└ lower_strike_price" + type: "string" + required: false + description: "Lower bound price" + x-description-zh: "下限价" + x-description-zh-hk: "下限價" + - name: "└ outstanding_qty" + type: "string" + required: false + description: "Outstanding quantity" + x-description-zh: "街货量" + x-description-zh-hk: "街貨量" + - name: "└ outstanding_ratio" + type: "string" + required: false + description: "Outstanding ratio" + x-description-zh: "街货比" + x-description-zh-hk: "街貨比" + - name: "└ premium" + type: "string" + required: false + description: "Premium" + x-description-zh: "溢价率" + x-description-zh-hk: "溢價率" + - name: "└ itm_otm" + type: "string" + required: false + description: "In/out of the bound" + x-description-zh: "价内/价外" + x-description-zh-hk: "價內/價外" + - name: "└ implied_volatility" + type: "string" + required: false + description: "Implied volatility" + x-description-zh: "引伸波幅" + x-description-zh-hk: "引伸波幅" + - name: "└ delta" + type: "string" + required: false + description: "Greek value Delta" + x-description-zh: "对冲值" + x-description-zh-hk: "對沖值" + - name: "└ call_price" + type: "string" + required: false + description: "Call price" + x-description-zh: "收回价" + x-description-zh-hk: "收回價" + - name: "└ to_call_price" + type: "string" + required: false + description: "Price interval from the call price" + x-description-zh: "距收回价" + x-description-zh-hk: "距收回價" + - name: "└ effective_leverage" + type: "string" + required: false + description: "Effective leverage" + x-description-zh: "有效杠杆" + x-description-zh-hk: "有效槓桿" + - name: "└ leverage_ratio" + type: "string" + required: false + description: "Leverage ratio" + x-description-zh: "杠杆比率" + x-description-zh-hk: "槓桿比率" + - name: "└ conversion_ratio" + type: "string" + required: false + description: "Conversion ratio" + x-description-zh: "换股比率" + x-description-zh-hk: "換股比率" + - name: "└ balance_point" + type: "string" + required: false + description: "Breakeven point" + x-description-zh: "打和点" + x-description-zh-hk: "打和點" + - name: "└ status" + type: "int32" + required: false + description: "Status: 2 Suspend trading, 3 Prepare List, 4 Normal" + x-description-zh: "状态:2 终止交易,3 等待上市,4 正常交易" + x-description-zh-hk: "狀態:2 終止交易,3 等待上市,4 正常" + - name: "total_count" + type: "int32" + required: false + description: "Total number of eligible" + x-description-zh: "符合条件的轮证总数量" + x-description-zh-hk: "符合條件的輪證總數量" + - id: ws-capital-flow + x-quote-command: capital + x-subgroup: Analytics + name: Capital Flow (Intraday) + x-name-zh: 资金流向 (分时) + x-name-zh-hk: 資金流向 (分時) + cmd: 24 + direction: request + description: | + This API is used to obtain the daily capital flow intraday of security. + + ```protobuf + message CapitalFlowIntradayRequest { + string symbol = 1; + } + + message CapitalFlowIntradayResponse { + string symbol = 1; + repeated CapitalFlowLine capital_flow_lines = 2; + } + ``` + x-description-zh: | + 该接口用于获取标的当日的资金流向。 + + ```protobuf + message CapitalFlowIntradayRequest { + string symbol = 1; + } + + message CapitalFlowIntradayResponse { + string symbol = 1; + repeated CapitalFlowLine capital_flow_lines = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的當日的資金流向。 + + ```protobuf + message CapitalFlowIntradayRequest { + string symbol = 1; + } + + message CapitalFlowIntradayResponse { + string symbol = 1; + repeated CapitalFlowLine capital_flow_lines = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.CapitalFlowIntradayRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 24]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.CapitalFlowIntradayResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.CapitalFlowIntradayRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 24]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.CapitalFlowIntradayResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = CapitalFlowIntradayRequest.encode({ symbol: "700.HK" }).finish() + const hdr = Buffer.from([0x01, 24]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = CapitalFlowIntradayResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = CapitalFlowIntradayRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 24); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + CapitalFlowIntradayResponse resp = CapitalFlowIntradayResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = CapitalFlowIntradayRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 24]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = CapitalFlowIntradayResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + CapitalFlowIntradayRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(24); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + CapitalFlowIntradayResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.CapitalFlowIntradayRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 24} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.CapitalFlowIntradayResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "700.HK", + "capital_flow_lines": [ + { + "inflow": "-310255860.000", + "timestamp": "1655106960" + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, for example: 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "capital_flow_lines" + type: "object[]" + required: false + description: "Capital flow data" + x-description-zh: "资金流向数据" + x-description-zh-hk: "資金流向數據" + - name: "└ inflow" + type: "string" + required: false + description: "Inflow capital data" + x-description-zh: "净流入" + x-description-zh-hk: "淨流入" + - name: "└ timestamp" + type: "int64" + required: false + description: "Start time stamp of the minute" + x-description-zh: "分钟开始时间戳" + x-description-zh-hk: "分鐘開始時間戳" + - id: ws-capital-dist + x-quote-command: capital + x-subgroup: Analytics + name: Capital Distribution + x-name-zh: 资金分布 + x-name-zh-hk: 資金分佈 + cmd: 25 + direction: request + description: | + This API is used to obtain the daily capital distribution of security. + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message CapitalDistributionResponse { + string symbol = 1; + int64 timestamp = 2; + CapitalDistribution capital_in = 3; + CapitalDistribution capital_out = 4; + } + ``` + x-description-zh: | + 该接口用于获取标的当日的资金分布。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message CapitalDistributionResponse { + string symbol = 1; + int64 timestamp = 2; + CapitalDistribution capital_in = 3; + CapitalDistribution capital_out = 4; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的當日的資金分佈。 + + ```protobuf + message SecurityRequest { + string symbol = 1; + } + + message CapitalDistributionResponse { + string symbol = 1; + int64 timestamp = 2; + CapitalDistribution capital_in = 3; + CapitalDistribution capital_out = 4; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 25]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.CapitalDistributionResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityRequest(symbol="700.HK").SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 25]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.CapitalDistributionResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityRequest.encode({ symbol: "700.HK" }).finish() + const hdr = Buffer.from([0x01, 25]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = CapitalDistributionResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 25); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + CapitalDistributionResponse resp = CapitalDistributionResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 25]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = CapitalDistributionResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(25); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + CapitalDistributionResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 25} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.CapitalDistributionResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "700.HK", + "timestamp": "1655107800", + "capital_in": { + "large": "935389700.000", + "medium": "2056032380.000", + "small": "828715920.000" + }, + "capital_out": { + "large": "1175331560.000", + "medium": "2271829740.000", + "small": "751648940.000" + } + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, for example: 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "timestamp" + type: "int64" + required: false + description: "Data update time" + x-description-zh: "数据更新时间戳" + x-description-zh-hk: "數據更新時間戳" + - name: "capital_in" + type: "object[]" + required: false + description: "Inflow capital data" + x-description-zh: "流入资金" + x-description-zh-hk: "流入資金" + - name: "└ large" + type: "string" + required: false + description: "large order" + x-description-zh: "大单" + x-description-zh-hk: "大單" + - name: "└ medium" + type: "string" + required: false + description: "medium order" + x-description-zh: "中单" + x-description-zh-hk: "中單" + - name: "└ small" + type: "string" + required: false + description: "small order" + x-description-zh: "小单" + x-description-zh-hk: "小單" + - name: "capital_out" + type: "object[]" + required: false + description: "Outflow capital data" + x-description-zh: "流出资金" + x-description-zh-hk: "流出資金" + - name: "└ large" + type: "string" + required: false + description: "large order" + x-description-zh: "大单" + x-description-zh-hk: "大單" + - name: "└ medium" + type: "string" + required: false + description: "medium order" + x-description-zh: "中单" + x-description-zh-hk: "中單" + - name: "└ small" + type: "string" + required: false + description: "small order" + x-description-zh: "小单" + x-description-zh-hk: "小單" + - id: ws-calc-index + x-quote-command: calc-index + x-subgroup: Analytics + name: Calc Indexes + x-name-zh: 计算指标 + x-name-zh-hk: 計算指標 + cmd: 26 + direction: request + description: | + This API is used to obtain the calculate indexes of securities. + + ```protobuf + message SecurityCalcQuoteRequest { + repeated string symbols = 1; + repeated CalcIndex calc_index = 2; + } + + message SecurityCalcQuoteResponse { + repeated SecurityCalcIndex security_calc_index = 1; + } + ``` + x-description-zh: | + 该接口用于获取标的计算指标数据,根据请求指定的计算指标返回数据。 + + ```protobuf + message SecurityCalcQuoteRequest { + repeated string symbols = 1; + repeated CalcIndex calc_index = 2; + } + + message SecurityCalcQuoteResponse { + repeated SecurityCalcIndex security_calc_index = 1; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的計算指標數據,根據請求指定的計算指標返回數據。 + + ```protobuf + message SecurityCalcQuoteRequest { + repeated string symbols = 1; + repeated CalcIndex calc_index = 2; + } + + message SecurityCalcQuoteResponse { + repeated SecurityCalcIndex security_calc_index = 1; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityCalcQuoteRequest(symbols=["700.HK"], calc_index=[1, 4]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 26]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityCalcQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityCalcQuoteRequest(symbols=["700.HK"], calc_index=[1, 4]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 26]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityCalcQuoteResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityCalcQuoteRequest.encode({ symbols: ["700.HK"], calc_index: [1, 4] }).finish() + const hdr = Buffer.from([0x01, 26]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityCalcQuoteResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityCalcQuoteRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 26); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityCalcQuoteResponse resp = SecurityCalcQuoteResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityCalcQuoteRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 26]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityCalcQuoteResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityCalcQuoteRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(26); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityCalcQuoteResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityCalcQuoteRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 26} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityCalcQuoteResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "security_calc_index": [ + { + "symbol": "AAPL.US", + "last_done": "131.880", + "change_val": "-5.2500", + "change_rate": "-3.83", + "volume": "122207099", + "turnover": "16269088361.000" + } + ] + } + x-fields: + - name: "symbols" + type: "string[]" + required: true + description: "Security code list, in ticker.region format, for example: [700.HK]. Max 500 symbols per request" + x-description-zh: "标的代码列表,使用 ticker.region 格式,例如:[700.HK]。每次请求上限 500 个" + x-description-zh-hk: "標的代碼列表,使用 ticker.region 格式,例如:[700.HK]。每次請求上限 500 個" + - name: "calc_index" + type: "int32[]" + required: true + description: "Calc indexes, for example: [1,2,3], see CalcIndex" + x-description-zh: "计算指标,例如:[1,2,3],详见 CalcIndex" + x-description-zh-hk: "計算指標,例如:[1,2,3],詳見 CalcIndex" + x-response-fields: + - name: "security_calc_index" + type: "object[]" + required: false + description: "Security Index Data" + x-description-zh: "标的指标数据" + x-description-zh-hk: "標的指標數據" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: "└ change_val" + type: "string" + required: false + description: "Change value" + x-description-zh: "涨跌额" + x-description-zh-hk: "漲跌額" + - name: "└ change_rate" + type: "string" + required: false + description: "Change ratio (ratio field, excludes %)" + x-description-zh: "涨跌幅 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "漲跌幅 (返回百分比數據,不包含%符號)" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "└ ytd_change_rate" + type: "string" + required: false + description: "Year-to-date change ratio (ratio field, excludes %)" + x-description-zh: "年初至今涨幅 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "年初至今漲幅 (返回百分比數據,不包含%符號)" + - name: "└ turnover_rate" + type: "string" + required: false + description: "Turnover rate (ratio field, excludes %)" + x-description-zh: "换手率 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "換手率 (返回百分比數據,不包含%符號)" + - name: "└ total_market_value" + type: "string" + required: false + description: "Total market value" + x-description-zh: "总市值" + x-description-zh-hk: "總市值" + - name: "└ capital_flow" + type: "string" + required: false + description: "Capital flow" + x-description-zh: "流入资金" + x-description-zh-hk: "流入資金" + - name: "└ amplitude" + type: "string" + required: false + description: "Amplitude (ratio field, excludes %)" + x-description-zh: "振幅 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "振幅 (返回百分比數據,不包含%符號)" + - name: "└ volume_ratio" + type: "string" + required: false + description: "Volume ratio" + x-description-zh: "量比" + x-description-zh-hk: "量比" + - name: "└ pe_ttm_ratio" + type: "string" + required: false + description: "PE (TTM)" + x-description-zh: "市盈率 (TTM)" + x-description-zh-hk: "市盈率 (TTM)" + - name: "└ pb_ratio" + type: "string" + required: false + description: "PB" + x-description-zh: "市净率" + x-description-zh-hk: "市淨率" + - name: "└ dividend_ratio_ttm" + type: "string" + required: false + description: "Dividend ratio (TTM)" + x-description-zh: "股息率 (TTM)" + x-description-zh-hk: "股息率 (TTM)" + - name: "└ five_day_change_rate" + type: "string" + required: false + description: "Five days change ratio (ratio field, excludes %)" + x-description-zh: "五日涨幅 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "五日漲幅 (返回百分比數據,不包含%符號)" + - name: "└ ten_day_change_rate" + type: "string" + required: false + description: "Ten days change ratio (ratio field, excludes %)" + x-description-zh: "十日涨幅 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "十日漲幅 (返回百分比數據,不包含%符號)" + - name: "└ half_year_change_rate" + type: "string" + required: false + description: "Half year change ratio (ratio field, excludes %)" + x-description-zh: "半年涨幅 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "半年漲幅 (返回百分比數據,不包含%符號)" + - name: "└ five_minutes_change_rate" + type: "string" + required: false + description: "Five minutes change ratio (ratio field, excludes %)" + x-description-zh: "五分钟涨幅 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "五分鐘漲幅 (返回百分比數據,不包含%符號)" + - name: "└ expiry_date" + type: "string" + required: false + description: "Expiry date" + x-description-zh: "到期日" + x-description-zh-hk: "到期日" + - name: "└ strike_price" + type: "string" + required: false + description: "Strike price" + x-description-zh: "行权价" + x-description-zh-hk: "行權價" + - name: "└ upper_strike_price" + type: "string" + required: false + description: "Upper bound price" + x-description-zh: "上限价" + x-description-zh-hk: "上限價" + - name: "└ lower_strike_price" + type: "string" + required: false + description: "Lower bound price" + x-description-zh: "下限价" + x-description-zh-hk: "下限價" + - name: "└ outstanding_qty" + type: "int64" + required: false + description: "Outstanding quantity" + x-description-zh: "街货量" + x-description-zh-hk: "街貨量" + - name: "└ outstanding_ratio" + type: "string" + required: false + description: "Outstanding ratio (ratio field, excludes %)" + x-description-zh: "街货比 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "街貨比 (返回百分比數據,不包含%符號)" + - name: "└ premium" + type: "string" + required: false + description: "Premium (ratio field, excludes %)" + x-description-zh: "溢价率 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "溢價率 (返回百分比數據,不包含%符號)" + - name: "└ itm_otm" + type: "string" + required: false + description: "In/out of the bound (ratio field, excludes %)" + x-description-zh: "价内/价外 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "價內/價外 (返回百分比數據,不包含%符號)" + - name: "└ implied_volatility" + type: "string" + required: false + description: "Implied volatility (ratio field, excludes %)" + x-description-zh: "隐含波动率 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "隱含波動率 (返回百分比數據,不包含%符號)" + - name: "└ warrant_delta" + type: "string" + required: false + description: "Warrant delta" + x-description-zh: "对冲值" + x-description-zh-hk: "對沖值" + - name: "└ call_price" + type: "string" + required: false + description: "Call price" + x-description-zh: "收回价" + x-description-zh-hk: "收回價" + - name: "└ to_call_price" + type: "string" + required: false + description: "Price interval from the call price (ratio field, excludes %)" + x-description-zh: "距收回价 (返回百分比数据,不包含%符号)" + x-description-zh-hk: "距收回價 (返回百分比數據,不包含%符號)" + - name: "└ effective_leverage" + type: "string" + required: false + description: "Effective leverage" + x-description-zh: "有效杠杆" + x-description-zh-hk: "有效槓桿" + - name: "└ leverage_ratio" + type: "string" + required: false + description: "Leverage ratio" + x-description-zh: "杠杆比率" + x-description-zh-hk: "槓桿比率" + - name: "└ conversion_ratio" + type: "string" + required: false + description: "Conversion ratio" + x-description-zh: "换股比率" + x-description-zh-hk: "換股比率" + - name: "└ balance_point" + type: "string" + required: false + description: "Breakeven point" + x-description-zh: "打和点" + x-description-zh-hk: "打和點" + - name: "└ open_interest" + type: "int64" + required: false + description: "Open interest" + x-description-zh: "未平仓数" + x-description-zh-hk: "未平倉數" + - name: "└ delta" + type: "string" + required: false + description: "Delta" + x-description-zh: "Delta" + x-description-zh-hk: "Delta" + - name: "└ gamma" + type: "string" + required: false + description: "Gamma" + x-description-zh: "Gamma" + x-description-zh-hk: "Gamma" + - name: "└ theta" + type: "string" + required: false + description: "Theta. Raw value divided by 100 for standard per-share per-day value" + x-description-zh: "Theta,原始值需除以 100 得到标准的每股每天值" + x-description-zh-hk: "Theta,原始值需除以 100 得到標準的每股每天值" + - name: "└ vega" + type: "string" + required: false + description: "Vega. Raw value divided by 100 for standard per-share per-1%-IV value" + x-description-zh: "Vega,原始值需除以 100 得到标准的每股每 1% IV 值" + x-description-zh-hk: "Vega,原始值需除以 100 得到標準的每股每 1% IV 值" + - name: "└ rho" + type: "string" + required: false + description: "Rho. Raw value divided by 100 for standard per-share per-1%-rate value" + x-description-zh: "Rho,原始值需除以 100 得到标准的每股每 1% 利率值" + x-description-zh-hk: "Rho,原始值需除以 100 得到標準的每股每 1% 利率值" + - id: ws-history-candlestick + x-quote-command: kline + x-subgroup: Stocks + name: History Candlesticks + x-name-zh: 历史 K 线 + x-name-zh-hk: 歷史 K 線 + cmd: 27 + direction: request + description: | + This API is used to obtain the history candlestick data of security. + + ```protobuf + message SecurityHistoryCandlestickRequest { + string symbol = 1; + Period period = 2; + AdjustType adjust_type = 3; + HistoryCandlestickQueryType query_type = 4; + OffsetQuery offset_request = 5; + DateQuery date_request = 6; + } + + message SecurityCandlestickResponse { + string symbol = 1; + repeated Candlestick candlesticks = 2; + } + ``` + x-description-zh: | + 该接口用于获取标的的历史 K 线数据。 + + ```protobuf + message SecurityHistoryCandlestickRequest { + string symbol = 1; + Period period = 2; + AdjustType adjust_type = 3; + HistoryCandlestickQueryType query_type = 4; + OffsetQuery offset_request = 5; + DateQuery date_request = 6; + } + + message SecurityCandlestickResponse { + string symbol = 1; + repeated Candlestick candlesticks = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取標的的曆史 K 線數據。 + + ```protobuf + message SecurityHistoryCandlestickRequest { + string symbol = 1; + Period period = 2; + AdjustType adjust_type = 3; + HistoryCandlestickQueryType query_type = 4; + OffsetQuery offset_request = 5; + DateQuery date_request = 6; + } + + message SecurityCandlestickResponse { + string symbol = 1; + repeated Candlestick candlesticks = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityHistoryCandlestickRequest(symbol="700.HK", period=1000, adjust_type=0, query_type=1, + offset_request=quote_pb2.SecurityHistoryCandlestickRequest.OffsetQuery(direction=1, count=10)).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 27]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityCandlestickResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SecurityHistoryCandlestickRequest(symbol="700.HK", period=1000, adjust_type=0, query_type=1, + offset_request=quote_pb2.SecurityHistoryCandlestickRequest.OffsetQuery(direction=1, count=10)).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 27]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SecurityCandlestickResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SecurityHistoryCandlestickRequest.encode({ symbol: "700.HK", period: 1000, adjust_type: 0, query_type: 1, + offset_request: { direction: 1, count: 10 } }).finish() + const hdr = Buffer.from([0x01, 27]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SecurityCandlestickResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SecurityHistoryCandlestickRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 27); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SecurityCandlestickResponse resp = SecurityCandlestickResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SecurityHistoryCandlestickRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 27]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SecurityCandlestickResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SecurityHistoryCandlestickRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(27); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SecurityCandlestickResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SecurityHistoryCandlestickRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 27} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SecurityCandlestickResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "symbol": "700.HK", + "candlesticks": [ + { + "close": "362.000", + "open": "364.600", + "low": "361.600", + "high": "368.800", + "volume": 10853604, + "turnover": "3954556819.000", + "timestamp": 1650384000 + } + ] + } + x-fields: + - name: "symbol" + type: "string" + required: true + description: "Security code, in ticker.region format, e.g. 700.HK" + x-description-zh: "标的代码,使用 ticker.region 格式,例如:700.HK" + x-description-zh-hk: "標的代碼,使用 ticker.region 格式,例如:700.HK" + - name: "period" + type: "int32" + required: true + description: "Candlestick period" + x-description-zh: "K 线周期" + x-description-zh-hk: "K 線週期" + - name: "adjust_type" + type: "int32" + required: true + description: "Adjustment type" + x-description-zh: "复权类型" + x-description-zh-hk: "複權類型" + - name: "query_type" + type: "int32" + required: true + description: "Type of query (1-by offset, 2-by date)" + x-description-zh: "查询方式 (1-按偏移查询,2-按日期区间查询)" + x-description-zh-hk: "查詢方式 (1-按偏移查詢,2-按日期區間查詢)" + - name: "date_request" + type: "object" + required: false + description: "Required when querying by date" + x-description-zh: "按日期查询时必填" + x-description-zh-hk: "按日期查詢時必填" + - name: "└ start_date" + type: "string" + required: false + description: "Date of query begin, in YYYYMMDD format" + x-description-zh: "开始日期,格式为 YYYYMMDD" + x-description-zh-hk: "開始日期,格式為 YYYYMMDD" + - name: "└ end_date" + type: "string" + required: false + description: "Date of query end, in YYYYMMDD format" + x-description-zh: "结束日期,格式为 YYYYMMDD" + x-description-zh-hk: "結束日期,格式為 YYYYMMDD" + - name: "offset_request" + type: "object" + required: false + description: "Required when querying by offset" + x-description-zh: "按偏移查询时必填" + x-description-zh-hk: "按偏移查詢時必填" + - name: "└ direction" + type: "int32" + required: true + description: "Query direction (0-historical, 1-latest)" + x-description-zh: "查询方向 (0-向历史,1-向最新)" + x-description-zh-hk: "查詢方嚮 (0-向曆史,1-向最新)" + - name: "└ date" + type: "string" + required: false + description: "Query date, in YYYYMMDD format; default latest trading day" + x-description-zh: "查询日期,格式为 YYYYMMDD;为空时使用最新交易日" + x-description-zh-hk: "查詢日期,格式為 YYYYMMDD;為空時使用最新交易日" + - name: "└ minute" + type: "string" + required: false + description: "Query time, in HHMM format; only valid for minute-level data" + x-description-zh: "查询时间,格式为 HHMM;仅在查询分钟级别 K 线时有效" + x-description-zh-hk: "查詢時間,格式為 HHMM;僅在查詢分鍾級別 K 線時有效" + - name: "└ count" + type: "int32" + required: false + description: "Count of candlestick, range [1,1000]; default 10" + x-description-zh: "查询数量,范围 [1,1000];默认 10" + x-description-zh-hk: "查詢數量,範圍 [1,1000];默認 10" + - name: "trade_session" + type: "int32" + required: false + description: "Trading session, 0: intraday, 100: All (pre, intraday, post, overnight)" + x-description-zh: "交易时段,0: 盘中,100: 所有延长时段(盘前,盘中,盘后,夜盘)" + x-description-zh-hk: "交易時段,0: 盤中,100: 所有延長時段(盤前,盤中,盤後,夜盤)" + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, e.g. AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "candlesticks" + type: "object[]" + required: false + description: "Candlestick data" + x-description-zh: "K 线数据" + x-description-zh-hk: "K 線數據" + - name: "└ close" + type: "string" + required: false + description: "Close price" + x-description-zh: "当前周期收盘价" + x-description-zh-hk: "當前週期收盤價" + - name: "└ open" + type: "string" + required: false + description: "Open price" + x-description-zh: "当前周期开盘价" + x-description-zh-hk: "當前週期開盤價" + - name: "└ low" + type: "string" + required: false + description: "Low price" + x-description-zh: "当前周期最低价" + x-description-zh-hk: "當前週期最低價" + - name: "└ high" + type: "string" + required: false + description: "High price" + x-description-zh: "当前周期最高价" + x-description-zh-hk: "當前週期最高價" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "当前周期成交量" + x-description-zh-hk: "當前週期成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "当前周期成交额" + x-description-zh-hk: "當前週期成交額" + - name: "└ timestamp" + type: "int64" + required: false + description: "Timestamp" + x-description-zh: "当前周期的时间戳" + x-description-zh-hk: "當前週期的時間戳" + - name: "└ trade_session" + type: "int32" + required: false + description: "Trade session" + x-description-zh: "交易时段" + x-description-zh-hk: "交易時段" + - name: Subscription (WebSocket) + x-name-zh: 订阅 (WebSocket) + x-name-zh-hk: 訂閱 (WebSocket) + x-tag: Quote + commands: + - id: ws-subscription + x-quote-command: subscriptions + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Get Subscriptions + x-name-zh: 获取订阅信息 + x-name-zh-hk: 獲取訂閱信息 + cmd: 5 + direction: request + description: | + This API is used to obtain the subscription information. + + ```protobuf + message SubscriptionRequest { + } + + message SubscriptionResponse { + repeated SubTypeList sub_list = 1; + } + + message SubTypeList { + string symbol = 1; + repeated SubType sub_type = 2; + } + ``` + x-description-zh: | + 该接口用于获取当前连接已订阅的标的行情。 + + ```protobuf + message SubscriptionRequest { + } + + message SubscriptionResponse { + repeated SubTypeList sub_list = 1; + } + + message SubTypeList { + string symbol = 1; + repeated SubType sub_type = 2; + } + ``` + x-description-zh-hk: | + 該接口用於獲取當前連接已訂閱的標的行情。 + + ```protobuf + message SubscriptionRequest { + } + + message SubscriptionResponse { + repeated SubTypeList sub_list = 1; + } + + message SubTypeList { + string symbol = 1; + repeated SubType sub_type = 2; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = b"" + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 5]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SubscriptionResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = b"" + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 5]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SubscriptionResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = new Uint8Array() + const hdr = Buffer.from([0x01, 5]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SubscriptionResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = new byte[0]; + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 5); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SubscriptionResponse resp = SubscriptionResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body: Vec = Vec::new(); + let mut pkt = vec![0x01u8, 5]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SubscriptionResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + std::string body; + std::string pkt; + pkt.push_back(0x01); pkt.push_back(5); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SubscriptionResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + var body []byte + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 5} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SubscriptionResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "sub_list": [ + { + "symbol": "700.HK", + "sub_type": [ + 1, + 2, + 3 + ] + }, + { + "symbol": "AAPL.US", + "sub_type": [ + 2 + ] + } + ] + } + x-response-fields: + - name: "sub_list" + type: "object[]" + required: false + description: "Subscribed data" + x-description-zh: "订阅的数据" + x-description-zh-hk: "訂閱的數據" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ sub_type" + type: "int32[]" + required: false + description: "Subscription type, see SubType" + x-description-zh: "订阅的数据类型,详见 SubType" + x-description-zh-hk: "訂閱的數據類型,詳見 SubType" + - id: ws-subscribe + x-quote-command: subscriptions + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Subscribe Quote + x-name-zh: 订阅行情 + x-name-zh-hk: 訂閱行情 + cmd: 6 + direction: request + description: | + This API is used to subscribe quote. + + ```protobuf + message SubscribeRequest { + repeated string symbol = 1; + repeated SubType sub_type = 2; + bool is_first_push = 3; + } + + message SubscriptionResponse { + repeated SubTypeList sub_list = 1; + } + ``` + x-description-zh: | + 该接口用于订阅标的行情数据。 + + ```protobuf + message SubscribeRequest { + repeated string symbol = 1; + repeated SubType sub_type = 2; + bool is_first_push = 3; + } + + message SubscriptionResponse { + repeated SubTypeList sub_list = 1; + } + ``` + x-description-zh-hk: | + 該接口用於訂閱標的行情數據。 + + ```protobuf + message SubscribeRequest { + repeated string symbol = 1; + repeated SubType sub_type = 2; + bool is_first_push = 3; + } + + message SubscriptionResponse { + repeated SubTypeList sub_list = 1; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SubscribeRequest(symbol=["700.HK", "AAPL.US"], sub_type=[1], is_first_push=True).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SubscriptionResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.SubscribeRequest(symbol=["700.HK", "AAPL.US"], sub_type=[1], is_first_push=True).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = quote_pb2.SubscriptionResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = SubscribeRequest.encode({ symbol: ["700.HK", "AAPL.US"], sub_type: [1], is_first_push: true }).finish() + const hdr = Buffer.from([0x01, 6]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SubscriptionResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = SubscribeRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 6); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SubscriptionResponse resp = SubscriptionResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = SubscribeRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 6]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SubscriptionResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + SubscribeRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(6); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SubscriptionResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.SubscribeRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 6} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := "e.SubscriptionResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "sub_list": [ + { + "symbol": "700.HK", + "sub_type": [ + 1, + 2, + 3 + ] + }, + { + "symbol": "AAPL.US", + "sub_type": [ + 2 + ] + } + ] + } + x-fields: + - name: "symbol" + type: "string[]" + required: true + description: "Security code list, for example: [700.HK]. Max 500 symbols per request; max 500 subscriptions per user" + x-description-zh: "订阅的标的代码,例如:[700.HK]。每次请求最多 500 个,每个用户最多同时订阅 500 个" + x-description-zh-hk: "訂閱的標的代碼,例如:[700.HK]。每次請求最多 500 個,每個用戶最多同時訂閱 500 個" + - name: "sub_type" + type: "int32[]" + required: true + description: "Subscription type, for example: [1,2], see SubType" + x-description-zh: "订阅的数据类型,例如:[1,2],详见 SubType" + x-description-zh-hk: "訂閱的數據類型,例如:[1,2],詳見 SubType" + - name: "is_first_push" + type: "bool" + required: true + description: "Whether to perform a data push immediately after subscribing (trade not supported)" + x-description-zh: "订阅后是否立刻进行一次数据推送(trade 不支持)" + x-description-zh-hk: "訂閱後是否立刻進行一次數據推送(trade 不支持)" + x-response-fields: + - name: "sub_list" + type: "object[]" + required: false + description: "Subscription list" + x-description-zh: "订阅的数据" + x-description-zh-hk: "訂閱的數據" + - name: "└ symbol" + type: "string" + required: false + description: "Security code" + x-description-zh: "标的代码" + x-description-zh-hk: "標的代碼" + - name: "└ sub_type" + type: "int32[]" + required: false + description: "Subscription type, see SubType" + x-description-zh: "订阅的数据类型,详见 SubType" + x-description-zh-hk: "訂閱的數據類型,詳見 SubType" + - id: ws-unsubscribe + x-quote-command: subscriptions + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Unsubscribe + x-name-zh: 取消订阅 + x-name-zh-hk: 取消訂閱 + cmd: 7 + direction: request + description: | + This API is used to unsubscribe quote. + + ```protobuf + message UnsubscribeRequest { + repeated string symbol = 1; + repeated SubType sub_type = 2; + bool unsub_all = 3; + } + + message UnsubscribeResponse { + } + ``` + x-description-zh: | + 该接口用于取消订阅标的行情数据。 + + ```protobuf + message UnsubscribeRequest { + repeated string symbol = 1; + repeated SubType sub_type = 2; + bool unsub_all = 3; + } + + message UnsubscribeResponse { + } + ``` + x-description-zh-hk: | + 該接口用於取消訂閱標的行情數據。 + + ```protobuf + message UnsubscribeRequest { + repeated string symbol = 1; + repeated SubType sub_type = 2; + bool unsub_all = 3; + } + + message UnsubscribeResponse { + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.UnsubscribeRequest(symbol=["700.HK", "AAPL.US"], sub_type=[1], unsub_all=False).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 7]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + # empty response + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + body = quote_pb2.UnsubscribeRequest(symbol=["700.HK", "AAPL.US"], sub_type=[1], unsub_all=False).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 7]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + # empty response + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = UnsubscribeRequest.encode({ symbol: ["700.HK", "AAPL.US"], sub_type: [1], unsub_all: false }).finish() + const hdr = Buffer.from([0x01, 7]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + // empty response + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = UnsubscribeRequest.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 7); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + // empty response + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = UnsubscribeRequest { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 7]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + // empty response + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + UnsubscribeRequest req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(7); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + // empty response + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := "e.UnsubscribeRequest{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 7} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + // empty response + x-response-example: | + {} + x-fields: + - name: "symbol" + type: "string[]" + required: true + description: "Security code list, for example: [700.HK]. Max 500 symbols per request" + x-description-zh: "订阅的标的代码,例如:[700.HK]。每次请求最多 500 个" + x-description-zh-hk: "訂閱的標的代碼,例如:[700.HK]。每次請求最多 500 個" + - name: "sub_type" + type: "int32[]" + required: true + description: "Subscription type list, for example: [1,2], see SubType" + x-description-zh: "订阅的数据类型,例如:[1,2],详见 SubType" + x-description-zh-hk: "訂閱的數據類型,例如:[1,2],詳見 SubType" + - name: "unsub_all" + type: "bool" + required: true + description: "Is unsubscribe all; empty symbol unsubscribes all, otherwise all types of these symbols" + x-description-zh: "是否全部取消;symbol 为空时取消所有订阅,否则取消这些标的的所有类型订阅" + x-description-zh-hk: "是否全部取消;symbol 為空時取消所有訂閱,否則取消這些標的的所有類型訂閱" + - name: Push (WebSocket) + x-name-zh: 推送 (WebSocket) + x-name-zh-hk: 推送 (WebSocket) + x-tag: Quote + commands: + - id: ws-push-quote + x-quote-command: quote + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Quote + x-name-zh: 实时价格订阅 + x-name-zh-hk: 價格訂閱 + cmd: 101 + direction: push + description: | + Real-time quote push of the subscribed security. In the pushed data structure, only the fields that have changed will be filled with data. + + ```protobuf + message PushQuote { + string symbol = 1; + int64 sequence = 2; + string last_done = 3; + string open = 4; + string high = 5; + string low = 6; + int64 timestamp = 7; + int64 volume = 8; + string turnover = 9; + TradeStatus trade_status = 10; + TradeSession trade_session = 11; + } + ``` + x-description-zh: | + 已订阅标的的实时价格订阅,推送的数据结构中,只有有变化的字段才会填充数据。 + + ```protobuf + message PushQuote { + string symbol = 1; + int64 sequence = 2; + string last_done = 3; + string open = 4; + string high = 5; + string low = 6; + int64 timestamp = 7; + int64 volume = 8; + string turnover = 9; + TradeStatus trade_status = 10; + TradeSession trade_session = 11; + } + ``` + x-description-zh-hk: | + 訂閱標的的實時價格推送,推送的數據結構中,只有有變化的字段才會填充數據。 + + ```protobuf + message PushQuote { + string symbol = 1; + int64 sequence = 2; + string last_done = 3; + string open = 4; + string high = 5; + string low = 6; + int64 timestamp = 7; + int64 volume = 8; + string turnover = 9; + TradeStatus trade_status = 10; + TradeSession trade_session = 11; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[1], is_first_push=True).SerializeToString() + ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 101): header, cmd, body_len(3), body + raw = ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushQuote() + push.ParseFromString(raw[5:5 + n]) + print(push) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[1], is_first_push=True).SerializeToString() + await ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 101): header, cmd, body_len(3), body + raw = await ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushQuote() + push.ParseFromString(raw[5:5 + n]) + print(push) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + // 1) subscribe (cmd 6) to start the feed: + const sub = SubscribeRequest.encode({ symbol: ["700.HK"], sub_type: [1], is_first_push: true }).finish() + const hdr = Buffer.from([0x01, 6]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([sub.length >> 16, sub.length >> 8, sub.length]) + ws.send(Buffer.concat([hdr, meta, len, sub])) // binary frame + + // 2) read push packets: + ws.on("message", (raw) => { + const n = (raw[2] << 16) | (raw[3] << 8) | raw[4] // push: header, cmd, body_len(3), body + const push = PushQuote.decode(raw.subarray(5, 5 + n)) + console.log(push) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + // 1) subscribe (cmd 6): + byte[] body = SubscribeRequest.newBuilder().addSymbol("700.HK").addSubType(SubType.forNumber(1)).setIsFirstPush(true).build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 6); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // 2) push: header, cmd, body_len(3), body + int n = ((raw[2] & 0xff) << 16) | ((raw[3] & 0xff) << 8) | (raw[4] & 0xff); + PushQuote push = PushQuote.parseFrom(Arrays.copyOfRange(raw, 5, 5 + n)); + System.out.println(push); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + // 1) subscribe (cmd 6): + let body = SubscribeRequest { symbol: vec!["700.HK".into()], sub_type: vec![1], is_first_push: true }.encode_to_vec(); + let mut pkt = vec![0x01u8, 6]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + // 2) read push packets (cmd 101): header, cmd, body_len(3), body + let raw = ws.next().await.unwrap()?.into_data(); + let n = ((raw[2] as usize) << 16) | ((raw[3] as usize) << 8) | raw[4] as usize; + let push = PushQuote::decode(&raw[5..5 + n])?; + println!("{:?}", push); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + // 1) subscribe (cmd 6): + SubscribeRequest req; req.add_symbol("700.HK"); req.add_sub_type((SubType)1); req.set_is_first_push(true); + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(6); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // 2) push: header, cmd, body_len(3), body + int n = ((uint8_t)raw[2] << 16) | ((uint8_t)raw[3] << 8) | (uint8_t)raw[4]; + PushQuote push; push.ParseFromString(raw.substr(5, n)); + std::cout << push.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + // 1) subscribe (cmd 6): + sub := "e.SubscribeRequest{Symbol: []string{"700.HK"}, SubType: []quote.SubType{1}, IsFirstPush: true} + body, _ := proto.Marshal(sub) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 6} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + // 2) read push packets (cmd 101): header, cmd, body_len(3), body + _, raw, _ := ws.ReadMessage() + n := int(raw[2])<<16 | int(raw[3])<<8 | int(raw[4]) + push := "e.PushQuote{} + proto.Unmarshal(raw[5:5+n], push) + fmt.Println(push) + x-response-example: | + { + "symbol": "AAPL.US", + "sequence": 160808750000000, + "last_done": "156.570", + "open": "155.910", + "high": "159.790", + "low": "155.380", + "timestamp": 1651089600, + "volume": 88063191, + "turnover": "13865092584.000", + "trade_status": 0, + "trade_session": 0 + } + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, for example: AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "sequence" + type: "int64" + required: false + description: "Sequence number" + x-description-zh: "序列号" + x-description-zh-hk: "序列號" + - name: "last_done" + type: "string" + required: false + description: "Latest price" + x-description-zh: "最新价" + x-description-zh-hk: "最新價" + - name: "open" + type: "string" + required: false + description: "Open" + x-description-zh: "开盘价" + x-description-zh-hk: "開盤價" + - name: "high" + type: "string" + required: false + description: "High" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: "low" + type: "string" + required: false + description: "Low" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: "timestamp" + type: "int64" + required: false + description: "Time of latest price" + x-description-zh: "最新成交的时间戳" + x-description-zh-hk: "最新成交的時間戳" + - name: "volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "trade_status" + type: "int32" + required: false + description: "Security trading status, see TradeStatus" + x-description-zh: "交易状态,详见 TradeStatus" + x-description-zh-hk: "交易狀態,詳見 TradeStatus" + - name: "trade_session" + type: "int32" + required: false + description: "Trade session, see TradeSession" + x-description-zh: "交易时段,详见 TradeSession" + x-description-zh-hk: "交易時段,詳見 TradeSession" + - name: "current_volume" + type: "int32" + required: false + description: "Increase volume between pushes" + x-description-zh: "两次推送之间增加的成交量" + x-description-zh-hk: "兩次推送之間增加的成交量" + - name: "current_turnover" + type: "string" + required: false + description: "Increase turnover between pushes" + x-description-zh: "两次推送之间增加的成交额" + x-description-zh-hk: "兩次推送之間增加的成交額" + - name: "tag" + type: "int32" + required: false + description: "Price tag: 0 real-time quote, 1 revised data after market close" + x-description-zh: "价格数据标签:0 实时行情,1 收盘后的修正数据" + x-description-zh-hk: "價格數據標籤:0 實時行情,1 收盤後的修正數據" + - id: ws-push-depth + x-quote-command: depth + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Depth + x-name-zh: 实时盘口订阅 + x-name-zh-hk: 盤口訂閱 + cmd: 102 + direction: push + description: | + Real-time depth data push of the subscribed security. + + ```protobuf + message PushDepth { + string symbol = 1; + int64 sequence = 2; + repeated Depth ask = 3; + repeated Depth bid = 4; + } + ``` + x-description-zh: | + 已订阅标的的实时盘口数据推送。 + + ```protobuf + message PushDepth { + string symbol = 1; + int64 sequence = 2; + repeated Depth ask = 3; + repeated Depth bid = 4; + } + ``` + x-description-zh-hk: | + 訂閱標的的實時盤口數據。 + + ```protobuf + message PushDepth { + string symbol = 1; + int64 sequence = 2; + repeated Depth ask = 3; + repeated Depth bid = 4; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[2], is_first_push=True).SerializeToString() + ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 102): header, cmd, body_len(3), body + raw = ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushDepth() + push.ParseFromString(raw[5:5 + n]) + print(push) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[2], is_first_push=True).SerializeToString() + await ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 102): header, cmd, body_len(3), body + raw = await ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushDepth() + push.ParseFromString(raw[5:5 + n]) + print(push) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + // 1) subscribe (cmd 6) to start the feed: + const sub = SubscribeRequest.encode({ symbol: ["700.HK"], sub_type: [2], is_first_push: true }).finish() + const hdr = Buffer.from([0x01, 6]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([sub.length >> 16, sub.length >> 8, sub.length]) + ws.send(Buffer.concat([hdr, meta, len, sub])) // binary frame + + // 2) read push packets: + ws.on("message", (raw) => { + const n = (raw[2] << 16) | (raw[3] << 8) | raw[4] // push: header, cmd, body_len(3), body + const push = PushDepth.decode(raw.subarray(5, 5 + n)) + console.log(push) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + // 1) subscribe (cmd 6): + byte[] body = SubscribeRequest.newBuilder().addSymbol("700.HK").addSubType(SubType.forNumber(2)).setIsFirstPush(true).build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 6); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // 2) push: header, cmd, body_len(3), body + int n = ((raw[2] & 0xff) << 16) | ((raw[3] & 0xff) << 8) | (raw[4] & 0xff); + PushDepth push = PushDepth.parseFrom(Arrays.copyOfRange(raw, 5, 5 + n)); + System.out.println(push); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + // 1) subscribe (cmd 6): + let body = SubscribeRequest { symbol: vec!["700.HK".into()], sub_type: vec![2], is_first_push: true }.encode_to_vec(); + let mut pkt = vec![0x01u8, 6]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + // 2) read push packets (cmd 102): header, cmd, body_len(3), body + let raw = ws.next().await.unwrap()?.into_data(); + let n = ((raw[2] as usize) << 16) | ((raw[3] as usize) << 8) | raw[4] as usize; + let push = PushDepth::decode(&raw[5..5 + n])?; + println!("{:?}", push); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + // 1) subscribe (cmd 6): + SubscribeRequest req; req.add_symbol("700.HK"); req.add_sub_type((SubType)2); req.set_is_first_push(true); + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(6); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // 2) push: header, cmd, body_len(3), body + int n = ((uint8_t)raw[2] << 16) | ((uint8_t)raw[3] << 8) | (uint8_t)raw[4]; + PushDepth push; push.ParseFromString(raw.substr(5, n)); + std::cout << push.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + // 1) subscribe (cmd 6): + sub := "e.SubscribeRequest{Symbol: []string{"700.HK"}, SubType: []quote.SubType{2}, IsFirstPush: true} + body, _ := proto.Marshal(sub) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 6} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + // 2) read push packets (cmd 102): header, cmd, body_len(3), body + _, raw, _ := ws.ReadMessage() + n := int(raw[2])<<16 | int(raw[3])<<8 | int(raw[4]) + push := "e.PushDepth{} + proto.Unmarshal(raw[5:5+n], push) + fmt.Println(push) + x-response-example: | + { + "symbol": "700.HK", + "sequence": 160808750000000, + "ask": [ + { + "position": 1, + "price": "335.000", + "volume": 500, + "order_num": 1 + } + ], + "bid": [ + { + "position": 1, + "price": "334.800", + "volume": 69400, + "order_num": 13 + } + ] + } + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, for example: AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "sequence" + type: "int64" + required: false + description: "Sequence number" + x-description-zh: "序列号" + x-description-zh-hk: "序列號" + - name: "ask" + type: "object[]" + required: false + description: "Ask depth" + x-description-zh: "卖盘" + x-description-zh-hk: "賣盤" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ price" + type: "string" + required: false + description: "Price" + x-description-zh: "价格" + x-description-zh-hk: "價格" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "挂单量" + x-description-zh-hk: "掛單量" + - name: "└ order_num" + type: "int64" + required: false + description: "Number of orders" + x-description-zh: "订单数量" + x-description-zh-hk: "訂單數量" + - name: "bid" + type: "object[]" + required: false + description: "Bid depth" + x-description-zh: "买盘" + x-description-zh-hk: "買盤" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ price" + type: "string" + required: false + description: "Price" + x-description-zh: "价格" + x-description-zh-hk: "價格" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "挂单量" + x-description-zh-hk: "掛單量" + - name: "└ order_num" + type: "int64" + required: false + description: "Number of orders" + x-description-zh: "订单数量" + x-description-zh-hk: "訂單數量" + - id: ws-push-brokers + x-quote-command: brokers + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Brokers + x-name-zh: 实时经纪队列订阅 + x-name-zh-hk: 經紀隊列訂閱 + cmd: 103 + direction: push + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Real-time brokers data push of the subscribed security. + + ```protobuf + message PushBrokers { + string symbol = 1; + int64 sequence = 2; + repeated Brokers ask_brokers = 3; + repeated Brokers bid_brokers = 4; + } + ``` + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 已订阅标的的实时经纪队列数据推送。 + + ```protobuf + message PushBrokers { + string symbol = 1; + int64 sequence = 2; + repeated Brokers ask_brokers = 3; + repeated Brokers bid_brokers = 4; + } + ``` + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 訂閱標的的實時經紀隊列數據。 + + ```protobuf + message PushBrokers { + string symbol = 1; + int64 sequence = 2; + repeated Brokers ask_brokers = 3; + repeated Brokers bid_brokers = 4; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[3], is_first_push=True).SerializeToString() + ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 103): header, cmd, body_len(3), body + raw = ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushBrokers() + push.ParseFromString(raw[5:5 + n]) + print(push) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[3], is_first_push=True).SerializeToString() + await ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 103): header, cmd, body_len(3), body + raw = await ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushBrokers() + push.ParseFromString(raw[5:5 + n]) + print(push) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + // 1) subscribe (cmd 6) to start the feed: + const sub = SubscribeRequest.encode({ symbol: ["700.HK"], sub_type: [3], is_first_push: true }).finish() + const hdr = Buffer.from([0x01, 6]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([sub.length >> 16, sub.length >> 8, sub.length]) + ws.send(Buffer.concat([hdr, meta, len, sub])) // binary frame + + // 2) read push packets: + ws.on("message", (raw) => { + const n = (raw[2] << 16) | (raw[3] << 8) | raw[4] // push: header, cmd, body_len(3), body + const push = PushBrokers.decode(raw.subarray(5, 5 + n)) + console.log(push) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + // 1) subscribe (cmd 6): + byte[] body = SubscribeRequest.newBuilder().addSymbol("700.HK").addSubType(SubType.forNumber(3)).setIsFirstPush(true).build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 6); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // 2) push: header, cmd, body_len(3), body + int n = ((raw[2] & 0xff) << 16) | ((raw[3] & 0xff) << 8) | (raw[4] & 0xff); + PushBrokers push = PushBrokers.parseFrom(Arrays.copyOfRange(raw, 5, 5 + n)); + System.out.println(push); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + // 1) subscribe (cmd 6): + let body = SubscribeRequest { symbol: vec!["700.HK".into()], sub_type: vec![3], is_first_push: true }.encode_to_vec(); + let mut pkt = vec![0x01u8, 6]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + // 2) read push packets (cmd 103): header, cmd, body_len(3), body + let raw = ws.next().await.unwrap()?.into_data(); + let n = ((raw[2] as usize) << 16) | ((raw[3] as usize) << 8) | raw[4] as usize; + let push = PushBrokers::decode(&raw[5..5 + n])?; + println!("{:?}", push); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + // 1) subscribe (cmd 6): + SubscribeRequest req; req.add_symbol("700.HK"); req.add_sub_type((SubType)3); req.set_is_first_push(true); + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(6); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // 2) push: header, cmd, body_len(3), body + int n = ((uint8_t)raw[2] << 16) | ((uint8_t)raw[3] << 8) | (uint8_t)raw[4]; + PushBrokers push; push.ParseFromString(raw.substr(5, n)); + std::cout << push.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + // 1) subscribe (cmd 6): + sub := "e.SubscribeRequest{Symbol: []string{"700.HK"}, SubType: []quote.SubType{3}, IsFirstPush: true} + body, _ := proto.Marshal(sub) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 6} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + // 2) read push packets (cmd 103): header, cmd, body_len(3), body + _, raw, _ := ws.ReadMessage() + n := int(raw[2])<<16 | int(raw[3])<<8 | int(raw[4]) + push := "e.PushBrokers{} + proto.Unmarshal(raw[5:5+n], push) + fmt.Println(push) + x-response-example: | + { + "symbol": "700.HK", + "sequence": 160808750000000, + "ask_brokers": [ + { + "position": 1, + "broker_ids": [ + 7358, + 9057, + 9028, + 7364 + ] + } + ], + "bid_brokers": [ + { + "position": 1, + "broker_ids": [ + 6996, + 5465, + 8026, + 8304, + 4978 + ] + } + ] + } + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, for example: AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "sequence" + type: "int64" + required: false + description: "Sequence number" + x-description-zh: "序列号" + x-description-zh-hk: "序列號" + - name: "ask_brokers" + type: "object[]" + required: false + description: "Ask brokers" + x-description-zh: "卖盘经纪队列" + x-description-zh-hk: "賣盤經紀隊列" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ broker_ids" + type: "int32[]" + required: false + description: "Broker ID" + x-description-zh: "券商席位 Id" + x-description-zh-hk: "券商席位 Id" + - name: "bid_brokers" + type: "object[]" + required: false + description: "Bid brokers" + x-description-zh: "买盘经纪队列" + x-description-zh-hk: "買盤經紀隊列" + - name: "└ position" + type: "int32" + required: false + description: "Position" + x-description-zh: "档位" + x-description-zh-hk: "檔位" + - name: "└ broker_ids" + type: "int32[]" + required: false + description: "Broker ID" + x-description-zh: "券商席位 Id" + x-description-zh-hk: "券商席位 Id" + - id: ws-push-trade + x-quote-command: trades + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Trades + x-name-zh: 实时成交明细订阅 + x-name-zh-hk: 成交明細訂閱 + cmd: 104 + direction: push + description: | + Real-time trades data push of the subscribed security. + + ```protobuf + message PushTrade { + string symbol = 1; + int64 sequence = 2; + repeated Trade trade = 3; + } + ``` + x-description-zh: | + 已订阅的标的的实时逐笔成交明细推送。 + + ```protobuf + message PushTrade { + string symbol = 1; + int64 sequence = 2; + repeated Trade trade = 3; + } + ``` + x-description-zh-hk: | + 訂閱的標的的實時逐筆成交明細推送。 + + ```protobuf + message PushTrade { + string symbol = 1; + int64 sequence = 2; + repeated Trade trade = 3; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[4], is_first_push=True).SerializeToString() + ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 104): header, cmd, body_len(3), body + raw = ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushTrade() + push.ParseFromString(raw[5:5 + n]) + print(push) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # 1) subscribe (cmd 6) to start the feed: + sub = quote_pb2.SubscribeRequest(symbol=["700.HK"], sub_type=[4], is_first_push=True).SerializeToString() + await ws.send(bytes([0x01, 6]) + struct.pack(">IH", 1, 5000) + len(sub).to_bytes(3, "big") + sub) + + # 2) read push packets (cmd 104): header, cmd, body_len(3), body + raw = await ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushTrade() + push.ParseFromString(raw[5:5 + n]) + print(push) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + // 1) subscribe (cmd 6) to start the feed: + const sub = SubscribeRequest.encode({ symbol: ["700.HK"], sub_type: [4], is_first_push: true }).finish() + const hdr = Buffer.from([0x01, 6]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([sub.length >> 16, sub.length >> 8, sub.length]) + ws.send(Buffer.concat([hdr, meta, len, sub])) // binary frame + + // 2) read push packets: + ws.on("message", (raw) => { + const n = (raw[2] << 16) | (raw[3] << 8) | raw[4] // push: header, cmd, body_len(3), body + const push = PushTrade.decode(raw.subarray(5, 5 + n)) + console.log(push) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + // 1) subscribe (cmd 6): + byte[] body = SubscribeRequest.newBuilder().addSymbol("700.HK").addSubType(SubType.forNumber(4)).setIsFirstPush(true).build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 6); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // 2) push: header, cmd, body_len(3), body + int n = ((raw[2] & 0xff) << 16) | ((raw[3] & 0xff) << 8) | (raw[4] & 0xff); + PushTrade push = PushTrade.parseFrom(Arrays.copyOfRange(raw, 5, 5 + n)); + System.out.println(push); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + // 1) subscribe (cmd 6): + let body = SubscribeRequest { symbol: vec!["700.HK".into()], sub_type: vec![4], is_first_push: true }.encode_to_vec(); + let mut pkt = vec![0x01u8, 6]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + // 2) read push packets (cmd 104): header, cmd, body_len(3), body + let raw = ws.next().await.unwrap()?.into_data(); + let n = ((raw[2] as usize) << 16) | ((raw[3] as usize) << 8) | raw[4] as usize; + let push = PushTrade::decode(&raw[5..5 + n])?; + println!("{:?}", push); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + // 1) subscribe (cmd 6): + SubscribeRequest req; req.add_symbol("700.HK"); req.add_sub_type((SubType)4); req.set_is_first_push(true); + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(6); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // 2) push: header, cmd, body_len(3), body + int n = ((uint8_t)raw[2] << 16) | ((uint8_t)raw[3] << 8) | (uint8_t)raw[4]; + PushTrade push; push.ParseFromString(raw.substr(5, n)); + std::cout << push.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + // 1) subscribe (cmd 6): + sub := "e.SubscribeRequest{Symbol: []string{"700.HK"}, SubType: []quote.SubType{4}, IsFirstPush: true} + body, _ := proto.Marshal(sub) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 6} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + // 2) read push packets (cmd 104): header, cmd, body_len(3), body + _, raw, _ := ws.ReadMessage() + n := int(raw[2])<<16 | int(raw[3])<<8 | int(raw[4]) + push := "e.PushTrade{} + proto.Unmarshal(raw[5:5+n], push) + fmt.Println(push) + x-response-example: | + { + "symbol": "700.HK", + "sequence": 160808750000000, + "trade": [ + { + "price": "158.760", + "volume": 1, + "timestamp": 1651103979, + "trade_type": "I", + "direction": 0, + "trade_session": 2 + } + ] + } + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, for example: AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "sequence" + type: "int64" + required: false + description: "Sequence number" + x-description-zh: "序列号" + x-description-zh-hk: "序列號" + - name: "trades" + type: "object[]" + required: false + description: "Trades data" + x-description-zh: "逐笔明细数据" + x-description-zh-hk: "逐筆明細數據" + - name: "└ price" + type: "string" + required: false + description: "Price" + x-description-zh: "价格" + x-description-zh-hk: "價格" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ timestamp" + type: "int64" + required: false + description: "Time of trading" + x-description-zh: "成交时间" + x-description-zh-hk: "成交時間" + - name: "└ trade_type" + type: "string" + required: false + description: "Trade type" + x-description-zh: "交易类型说明" + x-description-zh-hk: "交易類型說明" + - name: "└ direction" + type: "int32" + required: false + description: "Trade direction: 0 neutral, 1 down, 2 up" + x-description-zh: "交易方向:0 neutral, 1 down, 2 up" + x-description-zh-hk: "交易方向:0 neutral, 1 down, 2 up" + - name: "└ trade_session" + type: "int32" + required: false + description: "Trade session, see TradeSession" + x-description-zh: "交易时段,详见 TradeSession" + x-description-zh-hk: "交易時段,詳見 TradeSession" + - id: ws-push-candlestick + x-quote-command: candlesticks + x-subgroup: Subscribe + x-subgroup-zh: 订阅 + x-subgroup-zh-hk: 訂閱 + name: Candlesticks + x-name-zh: K 线 + x-name-zh-hk: K 線 + cmd: 105 + direction: push + description: | + Real-time candlestick (K-line) data push for subscribed securities. The callback fires whenever the current candlestick updates (Realtime mode) or when a candlestick period closes (Confirmed mode). + + :::tip + This page covers the **push** API (`subscribe_candlesticks`). To pull historical candlestick data on demand, see [Candlestick - Pull](/quote/stocks/candlestick). + ::: + + ```protobuf + message PushCandlestick { + string symbol = 1; + Period period = 2; + Candlestick candlestick = 3; + } + ``` + x-description-zh: | + 已订阅标的的实时 K 线数据推送。回调在当前 K 线更新时触发(Realtime 模式)或在一根 K 线周期结束时触发(Confirmed 模式)。 + + :::tip + 本页介绍的是**推送** API(`subscribe_candlesticks`)。如需按需拉取历史 K 线数据,请参见 [K 线 - 拉取](/quote/stocks/candlestick)。 + ::: + + ```protobuf + message PushCandlestick { + string symbol = 1; + Period period = 2; + Candlestick candlestick = 3; + } + ``` + x-description-zh-hk: | + 訂閱的標的的實時 K 線數據推送。回調在當前 K 線更新時觸發(Realtime 模式)或在一根 K 線週期結束時觸發(Confirmed 模式)。 + + :::tip + 本頁介紹的是**推送** API(`subscribe_candlesticks`)。如需按需拉取歷史 K 線數據,請參見 [K 線 - 拉取](/quote/stocks/candlestick)。 + ::: + + ```protobuf + message PushCandlestick { + string symbol = 1; + Period period = 2; + Candlestick candlestick = 3; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # Subscribe candlesticks first (see "Candlestick (K-line)"), then read pushes. + # read push packets (cmd 105): header, cmd, body_len(3), body + raw = ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushCandlestick() + push.ParseFromString(raw[5:5 + n]) + print(push) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.quote import quote_pb2 # protobuf types generated from the .proto + + # Subscribe candlesticks first (see "Candlestick (K-line)"), then read pushes. + # read push packets (cmd 105): header, cmd, body_len(3), body + raw = await ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = quote_pb2.PushCandlestick() + push.ParseFromString(raw[5:5 + n]) + print(push) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + // Subscribe candlesticks first (see "Candlestick (K-line)"), then read pushes. + ws.on("message", (raw) => { + const n = (raw[2] << 16) | (raw[3] << 8) | raw[4] // push: header, cmd, body_len(3), body + const push = PushCandlestick.decode(raw.subarray(5, 5 + n)) + console.log(push) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + // Subscribe candlesticks first (see "Candlestick (K-line)"), then read pushes. + int n = ((raw[2] & 0xff) << 16) | ((raw[3] & 0xff) << 8) | (raw[4] & 0xff); + PushCandlestick push = PushCandlestick.parseFrom(Arrays.copyOfRange(raw, 5, 5 + n)); + System.out.println(push); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + // Subscribe candlesticks first (see "Candlestick (K-line)"), then read pushes. + let raw = ws.next().await.unwrap()?.into_data(); + let n = ((raw[2] as usize) << 16) | ((raw[3] as usize) << 8) | raw[4] as usize; + let push = PushCandlestick::decode(&raw[5..5 + n])?; + println!("{:?}", push); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + // Subscribe candlesticks first (see "Candlestick (K-line)"), then read pushes. + int n = ((uint8_t)raw[2] << 16) | ((uint8_t)raw[3] << 8) | (uint8_t)raw[4]; + PushCandlestick push; push.ParseFromString(raw.substr(5, n)); + std::cout << push.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + // Subscribe candlesticks first (see "Candlestick (K-line)"), then read pushes. + _, raw, _ := ws.ReadMessage() + n := int(raw[2])<<16 | int(raw[3])<<8 | int(raw[4]) + push := "e.PushCandlestick{} + proto.Unmarshal(raw[5:5+n], push) + fmt.Println(push) + x-response-example: | + { + "symbol": "700.HK", + "period": 1, + "candlestick": { + "close": "162.500", + "open": "160.000", + "high": "163.000", + "low": "159.800", + "volume": 123456, + "turnover": "19987654.000", + "timestamp": 1651103700, + "trade_session": 0 + } + } + x-response-fields: + - name: "symbol" + type: "string" + required: false + description: "Security code, for example: AAPL.US" + x-description-zh: "标的代码,例如:AAPL.US" + x-description-zh-hk: "標的代碼,例如:AAPL.US" + - name: "period" + type: "int32" + required: false + description: "Candlestick period, see Period" + x-description-zh: "K 线周期,详见 Period" + x-description-zh-hk: "K 線週期,詳見 Period" + - name: "candlestick" + type: "object" + required: false + description: "Candlestick data" + x-description-zh: "K 线数据" + x-description-zh-hk: "K 線數據" + - name: "└ close" + type: "string" + required: false + description: "Close price" + x-description-zh: "收盘价" + x-description-zh-hk: "收盤價" + - name: "└ open" + type: "string" + required: false + description: "Open price" + x-description-zh: "开盘价" + x-description-zh-hk: "開盤價" + - name: "└ high" + type: "string" + required: false + description: "High price" + x-description-zh: "最高价" + x-description-zh-hk: "最高價" + - name: "└ low" + type: "string" + required: false + description: "Low price" + x-description-zh: "最低价" + x-description-zh-hk: "最低價" + - name: "└ volume" + type: "int64" + required: false + description: "Volume" + x-description-zh: "成交量" + x-description-zh-hk: "成交量" + - name: "└ turnover" + type: "string" + required: false + description: "Turnover" + x-description-zh: "成交额" + x-description-zh-hk: "成交額" + - name: "└ timestamp" + type: "int64" + required: false + description: "Candlestick time (Unix timestamp)" + x-description-zh: "K 线时间(Unix 时间戳)" + x-description-zh-hk: "K 線時間(Unix 時間戳)" + - name: "└ trade_session" + type: "int32" + required: false + description: "Trade session, see TradeSession" + x-description-zh: "交易时段,详见 TradeSession" + x-description-zh-hk: "交易時段,詳見 TradeSession" + - name: Notification (WebSocket) + x-name-zh: 推送 (WebSocket) + x-name-zh-hk: 推送 (WebSocket) + x-tag: Trade + commands: + - id: ws-trade-sub + x-subgroup: Notification + x-subgroup-zh: 通知 + x-subgroup-zh-hk: 通知 + name: Subscribe (Trade) + x-name-zh: 订阅交易推送 + x-name-zh-hk: 訂閱交易推送 + cmd: 16 + direction: request + description: | + Subscribe trade-gateway topics. Topic `private` delivers order/execution notifications. The response reports success / fail / current topics. + + ```protobuf + message Sub { + repeated string topics = 1; + } + + message SubResponse { + repeated string success = 1; + repeated Fail fail = 2; + repeated string current = 3; + } + ``` + x-description-zh: | + 订阅交易网关主题。主题 `private` 推送订单/成交通知。响应返回成功/失败/当前订阅主题。 + + ```protobuf + message Sub { + repeated string topics = 1; + } + + message SubResponse { + repeated string success = 1; + repeated Fail fail = 2; + repeated string current = 3; + } + ``` + x-description-zh-hk: | + 訂閱交易閘道主題。主題 `private` 推送訂單/成交通知。響應返回成功/失敗/當前訂閱主題。 + + ```protobuf + message Sub { + repeated string topics = 1; + } + + message SubResponse { + repeated string success = 1; + repeated Fail fail = 2; + repeated string current = 3; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.trade import trade_pb2 # protobuf types generated from the .proto + + body = trade_pb2.Sub(topics=["private"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 16]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = trade_pb2.SubResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.trade import trade_pb2 # protobuf types generated from the .proto + + body = trade_pb2.Sub(topics=["private"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 16]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = trade_pb2.SubResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = Sub.encode({ topics: ["private"] }).finish() + const hdr = Buffer.from([0x01, 16]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = SubResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = Sub.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 16); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + SubResponse resp = SubResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = Sub { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 16]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = SubResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + Sub req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(16); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + SubResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := &trade.Sub{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 16} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := &trade.SubResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "success": [ + "private" + ], + "fail": [], + "current": [ + "private" + ] + } + x-fields: + - name: "topics" + type: "string[]" + required: false + description: "Topics to subscribe" + x-description-zh: "要订阅的 topic" + x-description-zh-hk: "要訂閱的 topic" + x-response-fields: + - name: "success" + type: "string[]" + required: false + description: "Success topics" + x-description-zh: "订阅成功" + x-description-zh-hk: "訂閱成功" + - name: "fail" + type: "object[]" + required: false + description: "Failed topics" + x-description-zh: "订阅失败" + x-description-zh-hk: "訂閱失敗" + - name: "└ topic" + type: "string" + required: false + description: "Topic" + x-description-zh: "主题" + x-description-zh-hk: "主題" + - name: "└ reason" + type: "string" + required: false + description: "Failure reason" + x-description-zh: "失败原因" + x-description-zh-hk: "失敗原因" + - name: "current" + type: "string[]" + required: false + description: "Current subscriptions after subscribe" + x-description-zh: "当前订阅" + x-description-zh-hk: "目前訂閱" + - id: ws-trade-unsub + x-subgroup: Notification + x-subgroup-zh: 通知 + x-subgroup-zh-hk: 通知 + name: Cancel Subscribe (Trade) + x-name-zh: 取消订阅交易推送 + x-name-zh-hk: 取消訂閱交易推送 + cmd: 17 + direction: request + description: | + Unsubscribe trade-gateway topics. The response returns the remaining subscribed topics. + + ```protobuf + message Unsub { + repeated string topics = 1; + } + + message UnsubResponse { + repeated string current = 3; + } + ``` + x-description-zh: | + 取消订阅交易网关主题。响应返回剩余订阅的主题。 + + ```protobuf + message Unsub { + repeated string topics = 1; + } + + message UnsubResponse { + repeated string current = 3; + } + ``` + x-description-zh-hk: | + 取消訂閱交易閘道主題。響應返回剩餘訂閱的主題。 + + ```protobuf + message Unsub { + repeated string topics = 1; + } + + message UnsubResponse { + repeated string current = 3; + } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.trade import trade_pb2 # protobuf types generated from the .proto + + body = trade_pb2.Unsub(topics=["private"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 17]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + ws.send(pkt) # binary frame + + raw = ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = trade_pb2.UnsubResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.trade import trade_pb2 # protobuf types generated from the .proto + + body = trade_pb2.Unsub(topics=["private"]).SerializeToString() + + # frame: type/verify/gzip/reserve(1) | cmd(1) | request_id(4) | timeout(2) | body_len(3) + pkt = bytes([0x01, 17]) + struct.pack(">IH", 1, 5000) + len(body).to_bytes(3, "big") + body + await ws.send(pkt) # binary frame + + raw = await ws.recv() # response: header, cmd, request_id(4), status, body_len(3), body + n = int.from_bytes(raw[7:10], "big") + resp = trade_pb2.UnsubResponse() + resp.ParseFromString(raw[10:10 + n]) + print(resp) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + const body = Unsub.encode({ topics: ["private"] }).finish() + const hdr = Buffer.from([0x01, 17]) + const meta = Buffer.alloc(6); meta.writeUInt32BE(1, 0); meta.writeUInt16BE(5000, 4) + const len = Buffer.from([body.length >> 16, body.length >> 8, body.length]) + ws.send(Buffer.concat([hdr, meta, len, body])) // binary frame + + ws.once("message", (raw) => { // response: header, cmd, request_id(4), status, body_len(3), body + const n = (raw[7] << 16) | (raw[8] << 8) | raw[9] + const resp = UnsubResponse.decode(raw.subarray(10, 10 + n)) + console.log(resp) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + byte[] body = Unsub.newBuilder()/* set fields per the Request table */.build().toByteArray(); + ByteBuffer buf = ByteBuffer.allocate(11 + body.length); + buf.put((byte) 0x01).put((byte) 17); // header, cmd + buf.putInt(1).putShort((short) 5000); // request_id, timeout + buf.put((byte)(body.length >> 16)).put((byte)(body.length >> 8)).put((byte) body.length); + buf.put(body); + ws.sendBinary(buf.array(), true); + + // raw = the received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((raw[7] & 0xff) << 16) | ((raw[8] & 0xff) << 8) | (raw[9] & 0xff); + UnsubResponse resp = UnsubResponse.parseFrom(Arrays.copyOfRange(raw, 10, 10 + n)); + System.out.println(resp); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + let body = Unsub { /* set fields per the Request table */ ..Default::default() }.encode_to_vec(); + let mut pkt = vec![0x01u8, 17]; + pkt.extend_from_slice(&1u32.to_be_bytes()); // request_id + pkt.extend_from_slice(&5000u16.to_be_bytes()); // timeout + let bl = body.len(); + pkt.extend_from_slice(&[(bl >> 16) as u8, (bl >> 8) as u8, bl as u8]); + pkt.extend_from_slice(&body); + ws.send(Message::Binary(pkt)).await?; + + let raw = ws.next().await.unwrap()?.into_data(); // header, cmd, request_id(4), status, body_len(3), body + let n = ((raw[7] as usize) << 16) | ((raw[8] as usize) << 8) | raw[9] as usize; + let resp = UnsubResponse::decode(&raw[10..10 + n])?; + println!("{:?}", resp); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + Unsub req; /* set fields per the Request table */ + std::string body; req.SerializeToString(&body); + std::string pkt; + pkt.push_back(0x01); pkt.push_back(17); // header, cmd + uint32_t rid = 1; uint16_t to = 5000; + pkt.append({(char)(rid>>24),(char)(rid>>16),(char)(rid>>8),(char)rid}); + pkt.append({(char)(to>>8),(char)to}); + size_t bl = body.size(); + pkt.append({(char)(bl>>16),(char)(bl>>8),(char)bl}); + pkt += body; + ws.sendBinary(pkt); + + // raw = received binary frame (header, cmd, request_id(4), status, body_len(3), body) + int n = ((uint8_t)raw[7] << 16) | ((uint8_t)raw[8] << 8) | (uint8_t)raw[9]; + UnsubResponse resp; resp.ParseFromString(raw.substr(10, n)); + std::cout << resp.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + req := &trade.Unsub{/* set fields per the Request table */} + body, _ := proto.Marshal(req) + // frame: header(1) cmd(1) request_id(4) timeout(2) body_len(3) body + pkt := []byte{0x01, 17} + pkt = binary.BigEndian.AppendUint32(pkt, 1) + pkt = binary.BigEndian.AppendUint16(pkt, 5000) + pkt = append(pkt, byte(len(body)>>16), byte(len(body)>>8), byte(len(body))) + pkt = append(pkt, body...) + ws.WriteMessage(websocket.BinaryMessage, pkt) + + _, raw, _ := ws.ReadMessage() // response: header, cmd, request_id(4), status, body_len(3), body + n := int(raw[7])<<16 | int(raw[8])<<8 | int(raw[9]) + resp := &trade.UnsubResponse{} + proto.Unmarshal(raw[10:10+n], resp) + fmt.Println(resp) + x-response-example: | + { + "current": [] + } + x-fields: + - name: "topics" + type: "string[]" + required: false + description: "Topics to unsubscribe" + x-description-zh: "要取消订阅的 topic" + x-description-zh-hk: "要取消訂閱的 topic" + x-response-fields: + - name: "current" + type: "string[]" + required: false + description: "Current subscriptions after cancel subscribe" + x-description-zh: "当前订阅" + x-description-zh-hk: "目前訂閱" + - id: ws-trade-notify + x-subgroup: Notification + x-subgroup-zh: 通知 + x-subgroup-zh-hk: 通知 + name: Notification (Trade) + x-name-zh: 交易通知推送 + x-name-zh-hk: 交易通知推送 + cmd: 18 + direction: push + description: | + Order/execution push on the `private` topic. The Notification wrapper carries a JSON payload (event `order_changed_lb`; grid orders use `gridtrading_order`). + + ```protobuf + message Notification { + string topic = 1; + ContentType content_type = 2; + DispatchType dispatch_type = 3; + bytes data = 4; + } + + enum ContentType { CONTENT_UNDEFINED = 0; CONTENT_JSON = 1; CONTENT_PROTO = 2; } + ``` + x-description-zh: | + `private` 主题上的订单/成交推送。Notification 外层携带 JSON 负载(事件 `order_changed_lb`;网格订单为 `gridtrading_order`)。 + + ```protobuf + message Notification { + string topic = 1; + ContentType content_type = 2; + DispatchType dispatch_type = 3; + bytes data = 4; + } + + enum ContentType { CONTENT_UNDEFINED = 0; CONTENT_JSON = 1; CONTENT_PROTO = 2; } + ``` + x-description-zh-hk: | + `private` 主題上的訂單/成交推送。Notification 外層攜帶 JSON 負載(事件 `order_changed_lb`;網格訂單為 `gridtrading_order`)。 + + ```protobuf + message Notification { + string topic = 1; + ContentType content_type = 2; + DispatchType dispatch_type = 3; + bytes data = 4; + } + + enum ContentType { CONTENT_UNDEFINED = 0; CONTENT_JSON = 1; CONTENT_PROTO = 2; } + ``` + x-request-examples: + - lang: Python + label: "Python" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct + from longport.trade import trade_pb2 # protobuf types generated from the .proto + + # Subscribe the "private" topic first (cmd 16), then read pushes. + # read push packets (cmd 18): header, cmd, body_len(3), body + raw = ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = trade_pb2.() + push.ParseFromString(raw[5:5 + n]) + print(push) + - lang: Python + label: "Python (async)" + source: | + # authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + import struct, asyncio + from longport.trade import trade_pb2 # protobuf types generated from the .proto + + # Subscribe the "private" topic first (cmd 16), then read pushes. + # read push packets (cmd 18): header, cmd, body_len(3), body + raw = await ws.recv() + n = int.from_bytes(raw[2:5], "big") + push = trade_pb2.() + push.ParseFromString(raw[5:5 + n]) + print(push) # inside: async def main(): ... asyncio.run(main()) + - lang: JavaScript + label: "Node.js" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for OTP connect + auth) + // message types from protobufjs (loaded from the .proto) + + // Subscribe the "private" topic first (cmd 16), then read pushes. + ws.on("message", (raw) => { + const n = (raw[2] << 16) | (raw[3] << 8) | raw[4] // push: header, cmd, body_len(3), body + const push = .decode(raw.subarray(5, 5 + n)) + console.log(push) + }) + - lang: Java + label: "Java" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto; java.nio.ByteBuffer for framing + + // Subscribe the "private" topic first (cmd 16), then read pushes. + int n = ((raw[2] & 0xff) << 16) | ((raw[3] & 0xff) << 8) | (raw[4] & 0xff); + push = .parseFrom(Arrays.copyOfRange(raw, 5, 5 + n)); + System.out.println(push); + - lang: Rust + label: "Rust" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // prost messages; tokio-tungstenite Message::Binary + + // Subscribe the "private" topic first (cmd 16), then read pushes. + let raw = ws.next().await.unwrap()?.into_data(); + let n = ((raw[2] as usize) << 16) | ((raw[3] as usize) << 8) | raw[4] as usize; + let push = ::decode(&raw[5..5 + n])?; + println!("{:?}", push); + - lang: C++ + label: "C++" + source: | + // authed WebSocket `ws` (see Real-Time Market Data for connect + auth) + // protobuf classes generated from the .proto + + // Subscribe the "private" topic first (cmd 16), then read pushes. + int n = ((uint8_t)raw[2] << 16) | ((uint8_t)raw[3] << 8) | (uint8_t)raw[4]; + push; push.ParseFromString(raw.substr(5, n)); + std::cout << push.DebugString(); + - lang: Go + label: "Go" + source: | + // authed *websocket.Conn `ws` (see Real-Time Market Data for connect + auth) + // import "encoding/binary"; protobuf messages via google.golang.org/protobuf/proto + + // Subscribe the "private" topic first (cmd 16), then read pushes. + _, raw, _ := ws.ReadMessage() + n := int(raw[2])<<16 | int(raw[3])<<8 | int(raw[4]) + push := &trade.{} + proto.Unmarshal(raw[5:5+n], push) + fmt.Println(push) + x-response-example: | + { + "event": "order_changed_lb", + "data": { + "side": "Buy", + "stock_name": "Tencent Holdings Ltd.", + "submitted_quantity": "1000", + "symbol": "700.HK", + "order_type": "LO", + "submitted_price": "213.2", + "executed_quantity": "1000", + "executed_price": "213.2", + "order_id": "27", + "currency": "HKD", + "status": "NewStatus", + "submitted_at": "1562761893", + "updated_at": "1562761893" + } + } + x-response-fields: + - name: "topic" + type: "string" + required: false + description: "Topic" + x-description-zh: "主题" + x-description-zh-hk: "主題" + - name: "content_type" + type: "int32" + required: false + description: "Content type (JSON/PROTO)" + x-description-zh: "内容类型 (JSON/PROTO)" + x-description-zh-hk: "內容類型 (JSON/PROTO)" + - name: "dispatch_type" + type: "int32" + required: false + description: "Dispatch type" + x-description-zh: "分发类型" + x-description-zh-hk: "分發類型" + - name: "data" + type: "bytes" + required: false + description: "Payload data (order-changed)" + x-description-zh: "推送数据(订单变更)" + x-description-zh-hk: "推播數據(訂單變更)" + - name: "└ side" + type: "string" + required: false + description: "Order side (Buy/Sell)" + x-description-zh: "买卖方向 (Buy/Sell)" + x-description-zh-hk: "買賣方向 (Buy/Sell)" + - name: "└ stock_name" + type: "string" + required: false + description: "Stock name" + x-description-zh: "公司名称" + x-description-zh-hk: "公司名稱" + - name: "└ submitted_quantity" + type: "string" + required: false + description: "Submitted quantity" + x-description-zh: "委托数量" + x-description-zh-hk: "委託數量" + - name: "└ symbol" + type: "string" + required: false + description: "Order symbol" + x-description-zh: "订单标的" + x-description-zh-hk: "訂單標的" + - name: "└ order_type" + type: "string" + required: false + description: "Order type" + x-description-zh: "订单类型" + x-description-zh-hk: "訂單類型" + - name: "└ submitted_price" + type: "string" + required: false + description: "Submitted price" + x-description-zh: "委托价格" + x-description-zh-hk: "委託價格" + - name: "└ executed_quantity" + type: "string" + required: false + description: "Executed quantity" + x-description-zh: "成交数量" + x-description-zh-hk: "成交數量" + - name: "└ executed_price" + type: "string" + required: false + description: "Executed price" + x-description-zh: "成交价格" + x-description-zh-hk: "成交價格" + - name: "└ order_id" + type: "string" + required: false + description: "Order id" + x-description-zh: "订单 id" + x-description-zh-hk: "訂單 id" + - name: "└ currency" + type: "string" + required: false + description: "Currency" + x-description-zh: "结算货币" + x-description-zh-hk: "結算貨幣" + - name: "└ status" + type: "string" + required: false + description: "Order status" + x-description-zh: "订单状态" + x-description-zh-hk: "訂單狀態" + - name: "└ submitted_at" + type: "string" + required: false + description: "Submitted time (timestamp, second)" + x-description-zh: "下单时间,格式为时间戳 (秒)" + x-description-zh-hk: "下單時間,格式為時間戳 (秒)" + - name: "└ updated_at" + type: "string" + required: false + description: "Last updated time" + x-description-zh: "最近更新时间" + x-description-zh-hk: "最近更新時間" + - name: "└ trigger_price" + type: "string" + required: false + description: "LIT / MIT order trigger price" + x-description-zh: "触发价格" + x-description-zh-hk: "觸發價格" + - name: "└ msg" + type: "string" + required: false + description: "Rejected message or remark" + x-description-zh: "拒绝理由,备注信息" + x-description-zh-hk: "拒絕理由,備注信息" + - name: "└ tag" + type: "string" + required: false + description: "Order tag (Normal/GTC/Grey)" + x-description-zh: "订单标记 (Normal/GTC/Grey)" + x-description-zh-hk: "訂單標記 (Normal/GTC/Grey)" + - name: "└ trigger_status" + type: "string" + required: false + description: "Conditional order trigger status" + x-description-zh: "条件单触发状态" + x-description-zh-hk: "條件單觸發狀態" + - name: "└ trigger_at" + type: "string" + required: false + description: "Conditional order trigger time (timestamp, second)" + x-description-zh: "触发时间" + x-description-zh-hk: "觸發時間" + - name: "└ trailing_amount" + type: "string" + required: false + description: "TSLPAMT order trailing amount" + x-description-zh: "条件单跟踪金额" + x-description-zh-hk: "條件單跟蹤金額" + - name: "└ trailing_percent" + type: "string" + required: false + description: "TSLPPCT order trailing percent" + x-description-zh: "条件单跟踪涨跌幅" + x-description-zh-hk: "條件單跟蹤漲跌幅" + - name: "└ limit_offset" + type: "string" + required: false + description: "TSLPAMT / TSLPPCT order limit offset amount" + x-description-zh: "指定价差" + x-description-zh-hk: "指定價差" + - name: "└ account_no" + type: "string" + required: false + description: "Account no" + x-description-zh: "用户端账号" + x-description-zh-hk: "用戶端賬號" + - name: "└ remark" + type: "string" + required: false + description: "Remark message" + x-description-zh: "备注" + x-description-zh-hk: "備注" + - name: "└ last_share" + type: "string" + required: false + description: "Last share" + x-description-zh: "最新成交数量" + x-description-zh-hk: "最新成交數量" + - name: "└ last_price" + type: "string" + required: false + description: "Last price" + x-description-zh: "最新成交价格" + x-description-zh-hk: "最新成交價格" + - name: "└ multi_leg" + type: "object" + required: false + description: "Multi-leg strategy information, only pushed for multi-leg option combination orders" + x-description-zh: "多腿策略信息,仅多腿期权组合订单推送" + x-description-zh-hk: "多腿策略信息,僅多腿期權組合訂單推送" + - name: " └ strategy" + type: "string" + required: false + description: "Multi-leg strategy" + x-description-zh: "多腿策略" + x-description-zh-hk: "多腿策略" + - name: " └ strategy_name" + type: "string" + required: false + description: "Strategy name" + x-description-zh: "策略名称" + x-description-zh-hk: "策略名稱" + - name: " └ multileg_id" + type: "string" + required: false + description: "Multi-leg combination ID" + x-description-zh: "多腿组合 ID" + x-description-zh-hk: "多腿組合 ID" + - name: " └ code" + type: "string" + required: false + description: "Multi-leg combination code" + x-description-zh: "多腿组合代码" + x-description-zh-hk: "多腿組合代碼" + - name: " └ legs" + type: "object[]" + required: false + description: "Legs of the combination order" + x-description-zh: "组合订单的各腿" + x-description-zh-hk: "組合訂單的各腿" + - name: "  └ symbol" + type: "string" + required: false + description: "Option symbol, use ticker.region format" + x-description-zh: "期权 symbol,使用 ticker.region 格式" + x-description-zh-hk: "期權 symbol,使用 ticker.region 格式" + - name: "  └ side" + type: "string" + required: false + description: "Order side (Buy/Sell)" + x-description-zh: "买卖方向 (Buy/Sell)" + x-description-zh-hk: "買賣方向 (Buy/Sell)" + - name: "  └ position" + type: "string" + required: false + description: "Position direction (LONG/SHORT)" + x-description-zh: "持仓方向 (LONG/SHORT)" + x-description-zh-hk: "持倉方向 (LONG/SHORT)" + - name: "  └ ratio_quantity" + type: "string" + required: false + description: "Leg ratio quantity" + x-description-zh: "该腿比例数量" + x-description-zh-hk: "該腿比例數量" + - name: "  └ strike_price" + type: "string" + required: false + description: "Strike price" + x-description-zh: "行权价" + x-description-zh-hk: "行權價" + - name: "  └ expire_date" + type: "string" + required: false + description: "Option expiry date, format YYYYMMDD" + x-description-zh: "期权到期日,格式:YYYYMMDD" + x-description-zh-hk: "期權到期日,格式:YYYYMMDD" + - name: "  └ contract_direction" + type: "string" + required: false + description: "Contract type (C=Call/P=Put)" + x-description-zh: "合约类型 (C=看涨/P=看跌)" + x-description-zh-hk: "合約類型 (C=看漲/P=看跌)" +servers: + - url: https://openapi.longbridge.com description: Global - url: https://openapi.longbridge.cn description: China mainland tags: - - name: Watchlist Management - x-name-zh: 自选股管理 - description: Manage user watchlist groups (list/create/update/delete) and query the securities contained in each group. Supports adding, removing, or replacing securities within a group. - - name: Market Temperature - x-name-zh: 市场情绪 - description: Query the Market Temperature indicator (a fear-gauge-like sentiment thermometer, scored 0–100 where higher means more bullish) across equity markets. Supports fetching the current snapshot and historical time series for sentiment measurement, trend visualization, and comparative analysis. - - name: Portfolio & Cash - x-name-zh: 持仓与资金 - description: Provides read access to account asset and cash information, including fund holdings, stock holdings, account cash balance, and cash flow history for portfolio overview, position display, and reconciliation analysis. - - name: News & Filings - x-name-zh: 新闻与公告 - description: Query regulatory filings, news articles, and community discussion topics for a given symbol. Suitable for investment research, news monitoring, and content aggregation. - - name: Community - x-name-zh: 社区互动 - description: Interact with the Longbridge community. Supports listing your published topics with pagination and type filtering, and creating new topics (long-form articles or short posts) with associated tickers, hashtags, and license settings. - - name: Trade Execution & Order Management - x-name-zh: 交易与订单管理 - description: Full order lifecycle management including order detail queries, today's and historical order queries, today's and historical execution reports, order modification, order cancellation, and pre-order maximum purchasable quantity estimation. - - name: Grid Trading - x-name-zh: 网格交易 - description: Automated grid strategy orders. Submit, modify, list, and inspect grid orders; query trigger history; suspend, restart, or cancel a running grid; read the per-symbol grid info (lot size, last price, authorization); and record the one-time strategy risk-disclosure consent. - - name: Statement - x-name-zh: 结单 - description: Query and download account statements (daily or monthly). Statements contain detailed breakdowns of assets, holdings, trades, fees, and corporate actions. + - name: Quote + x-name-zh: 行情 + x-name-zh-hk: 行情 + description: Real-time and static quotes, subscriptions, options, warrants, market analytics, and watchlists. + x-subgroups: + - name: Stocks + x-name-zh: 个股行情 + x-name-zh-hk: 個股 + - name: Options + x-name-zh: 期权 + x-name-zh-hk: 期權 + - name: Analytics + x-name-zh: 数据分析 + x-name-zh-hk: 數據分析 + - name: Watchlist + x-name-zh: 自选股 + x-name-zh-hk: 自選股 + - name: Fundamental + x-name-zh: 基本面 + x-name-zh-hk: 基本面 + description: Company fundamentals, financials, valuation, ratings, shareholders, and market data. + x-subgroups: + - name: Fundamentals + x-name-zh: 基本面数据 + x-name-zh-hk: 基本面數據 + - name: Market Data + x-name-zh: 市场数据 + x-name-zh-hk: 市場數據 + - name: Market + x-name-zh: 市场 + x-name-zh-hk: 市場 + description: Market status, temperature, rankings, and financial calendars. + x-subgroups: + - name: Market Status + x-name-zh: 市场状态 + x-name-zh-hk: 市場狀態 + - name: Financial Calendar + x-name-zh: 财经日历 + x-name-zh-hk: 財經日曆 + - name: News & Contents + x-name-zh: 资讯与社区 + x-name-zh-hk: 資訊與社區 + description: News, filings, and community topics and sharelists. + x-subgroups: + - name: News + x-name-zh: 资讯 + x-name-zh-hk: 資訊 + - name: Topics + x-name-zh: 话题 + x-name-zh-hk: 話題 + - name: Sharelist + x-name-zh: 股单 + x-name-zh-hk: 股單 + - name: Screener + x-name-zh: 选股 + x-name-zh-hk: 選股 + description: Screen securities by indicators and strategies. + - name: Trade + x-name-zh: 交易 + x-name-zh-hk: 交易 + description: Orders, executions, grid strategies, and account assets. + x-subgroups: + - name: Order + x-name-zh: 订单 + x-name-zh-hk: 訂單 + - name: Grid Trading + x-name-zh: 网格交易 + x-name-zh-hk: 網格交易 + - name: Execution + x-name-zh: 成交 + x-name-zh-hk: 成交 + - name: Assets + x-name-zh: 资产 + x-name-zh-hk: 資產 + - name: Account + x-name-zh: 账户 + x-name-zh-hk: 帳戶 + description: Recurring investment (DCA) plans, portfolio, statements, and alert reminders. + x-subgroups: + - name: Portfolio + x-name-zh: 投资组合 + x-name-zh-hk: 投資組合 + - name: Alerts + x-name-zh: 股价提醒 + x-name-zh-hk: 股價提醒 + - name: DCA + x-name-zh: 定投 + x-name-zh-hk: 定投 + - name: AI Agent + x-name-zh: AI Agent + x-name-zh-hk: AI Agent + description: AI agents, workspaces, and conversations. + x-subgroups: + - name: Workspace + x-name-zh: 工作空间 + x-name-zh-hk: 工作空間 + - name: Conversation + x-name-zh: 对话 + x-name-zh-hk: 對話 security: - oauth2: - openapi paths: + /v1/quote/ai/screener/search: + post: + operationId: screener_search + summary: Screener Search + x-summary-zh: 选股筛选 + x-summary-zh-hk: 選股篩選 + description: | + Filter stocks by strategy ID or custom indicator conditions, with pagination support. + x-description-zh: | + 按策略 ID 或自定义指标条件筛选股票,支持分页。 + x-description-zh-hk: | + 按策略 ID 或自定義指標條件篩選股票,支持分頁。 + tags: + - Screener + x-parameters: + - name: market + in: body + type: string + required: true + description: Market to screen, e.g. `US`, `HK`, `CN`. When a strategy is used, taken from the strategy. + x-description-zh: 选股市场,如 `US`、`HK`、`CN`。使用策略时取自策略。 + x-description-zh-hk: 選股市場,如 `US`、`HK`、`CN`。使用策略時取自策略。 + - name: filters + in: body + type: array + required: true + description: 'Filter conditions. Each item is `{ "key": "filter_pettm", "min": "10", "max": "30", "tech_values": {} }`.' + x-description-zh: '筛选条件。每项为 `{ "key": "filter_pettm", "min": "10", "max": "30", "tech_values": {} }`。' + x-description-zh-hk: '篩選條件。每項為 `{ "key": "filter_pettm", "min": "10", "max": "30", "tech_values": {} }`。' + - name: returns + in: body + type: array + required: true + description: Indicator keys to return for each match, e.g. `filter_pettm`. + x-description-zh: 每个匹配项需返回的指标 key 列表,如 `filter_pettm`。 + x-description-zh-hk: 每個匹配項需返回的指標 key 列表,如 `filter_pettm`。 + - name: page + in: body + type: integer + required: true + description: Page number (0-indexed). + x-description-zh: 页码(从 0 开始)。 + x-description-zh-hk: 頁碼(從 0 開始)。 + - name: size + in: body + type: integer + required: true + description: Page size. + x-description-zh: 每页数量。 + x-description-zh-hk: 每頁數量。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge screener search --strategy-id 42 + longbridge screener search --market HK --filter marketcap:100:1000 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/quote/ai/screener/search' \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{"market":"","filters":[],"returns":[],"page":0,"size":0}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/quote/ai/screener/search", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"market": "", "filters": [], "returns": [], "page": 0, "size": 0}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/quote/ai/screener/search", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"market": "", "filters": [], "returns": [], "page": 0, "size": 0}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/ai/screener/search", { + method: "POST", + headers: { + "Authorization": "Bearer ", + "Content-Type": "application/json", + }, + body: JSON.stringify({ market: "", filters: [], returns: [], page: 0, size: 0 }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ai/screener/search")) + .header("Authorization", "Bearer ") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"market\":\"\",\"filters\":[],\"returns\":[],\"page\":0,\"size\":0}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/quote/ai/screener/search") + .header("Authorization", "Bearer ") + .json(&serde_json::json!({ "market": "" , "filters": [] , "returns": [] , "page": 0 , "size": 0 })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ai/screener/search"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"market\":\"\",\"filters\":[],\"returns\":[],\"page\":0,\"size\":0}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/quote/ai/screener/search\", strings.NewReader(`{\"market\":\"\",\"filters\":[],\"returns\":[],\"page\":0,\"size\":0}`))\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: total + type: integer + required: false + description: Total number of matching stocks. + x-description-zh: 满足条件的股票总数。 + x-description-zh-hk: 滿足條件的股票總數。 + - name: items + type: object[] + required: false + description: Matched stock list. + x-description-zh: 匹配的股票列表。 + x-description-zh-hk: 匹配的股票列表。 + - name: └ symbol + type: string + required: false + description: Security symbol, e.g. `WXT.US`. + x-description-zh: 标的代码,如 `WXT.US`。 + x-description-zh-hk: 標的代碼,如 `WXT.US`。 + - name: └ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ indicators + type: object[] + required: false + description: Indicator values for this stock. + x-description-zh: 该股票的指标值。 + x-description-zh-hk: 該股票的指標值。 + - name: └ ∟ key + type: string + required: false + description: Indicator key, e.g. `filter_pettm`. + x-description-zh: 指标键名,如 `filter_pettm`。 + x-description-zh-hk: 指標鍵名,如 `filter_pettm`。 + - name: └ ∟ name + type: string + required: false + description: Indicator display name. + x-description-zh: 指标显示名称。 + x-description-zh-hk: 指標顯示名稱。 + - name: └ ∟ value + type: string + required: false + description: Indicator value as a string; empty when unavailable. + x-description-zh: 指标值(字符串),无数据时为空。 + x-description-zh-hk: 指標值(字符串),無數據時為空。 + - name: └ ∟ unit + type: string + required: false + description: Value unit, e.g. `%`, `亿`. + x-description-zh: 数值单位,如 `%`、` 亿`。 + x-description-zh-hk: 數值單位,如 `%`、` 億`。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + total: 88 + page: 0 + market: US + items: + - symbol: AAPL.US + name: Apple Inc. + prevchg: 0.62 + marketcap: 3241500000000 + pettm: 32.15 + pbmrq: 50.21 + salesgrowthyoy: 8.04 + - symbol: MSFT.US + name: Microsoft + prevchg: 1.05 + marketcap: 3085000000000 + pettm: 35.42 + pbmrq: 12.87 + salesgrowthyoy: 12.61 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/market/stock-events: + post: + operationId: top_movers + summary: Top Movers + x-summary-zh: 异动股票(Top Movers) + x-summary-zh-hk: 異動股票(Top Movers) + description: | + Get stocks whose price movement exceeds the 20-trading-day standard deviation, with automatically correlated news to explain the move. + x-description-zh: | + 获取价格波动超过近 20 个交易日标准差的异动股票,系统自动关联相关新闻解读异动原因。 + x-description-zh-hk: | + 獲取價格波動超過近 20 個交易日標準差的異動股票,系統自動關聯相關新聞解讀異動原因。 + x-subgroup: Market Status + x-subgroup-zh: 市场状态 + x-subgroup-zh-hk: 市場狀態 + tags: + - Market + x-parameters: + - name: markets + in: body + type: array + required: false + description: 'Market list: `HK`, `US`, `CN`, `SG`; returns all markets if omitted' + x-description-zh: 市场列表:`HK`、`US`、`CN`、`SG`;不传返回所有市场 + x-description-zh-hk: 市場列表:`HK`、`US`、`CN`、`SG`;不傳返回所有市場 + - name: sort + in: body + type: integer + required: false + description: 'Sort order: `0`=time (newest first), `1`=price change, `2`=hotness (default)' + x-description-zh: 排序方式:`0`=时间(最新优先),`1`=涨跌幅,`2`=热度(默认) + x-description-zh-hk: 排序方式:`0`=時間(最新優先),`1`=漲跌幅,`2`=熱度(默認) + - name: date + in: body + type: string + required: false + description: Target date in `YYYY-MM-DD` format; returns latest data if omitted + x-description-zh: 指定日期,格式 `YYYY-MM-DD`;不传返回最新数据 + x-description-zh-hk: 指定日期,格式 `YYYY-MM-DD`;不傳返回最新數據 + - name: limit + in: body + type: integer + required: false + description: Number of results to return, default 20 + x-description-zh: 返回条数,默认 20 + x-description-zh-hk: 返回條數,默認 20 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge top-movers + longbridge top-movers --market HK --sort time + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/quote/market/stock-events' \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{"markets":[],"sort":0,"date":"","limit":0}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/quote/market/stock-events", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"markets": [], "sort": 0, "date": "", "limit": 0}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/quote/market/stock-events", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"markets": [], "sort": 0, "date": "", "limit": 0}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/market/stock-events", { + method: "POST", + headers: { + "Authorization": "Bearer ", + "Content-Type": "application/json", + }, + body: JSON.stringify({ markets: [], sort: 0, date: "", limit: 0 }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/market/stock-events")) + .header("Authorization", "Bearer ") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"markets\":[],\"sort\":0,\"date\":\"\",\"limit\":0}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/quote/market/stock-events") + .header("Authorization", "Bearer ") + .json(&serde_json::json!({ "markets": [] , "sort": 0 , "date": "" , "limit": 0 })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/market/stock-events"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"markets\":[],\"sort\":0,\"date\":\"\",\"limit\":0}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/quote/market/stock-events\", strings.NewReader(`{\"markets\":[],\"sort\":0,\"date\":\"\",\"limit\":0}`))\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: events + type: object[] + required: false + description: List of moving stocks + x-description-zh: 异动股票列表 + x-description-zh-hk: 異動股票列表 + - name: └ stock + type: object + required: false + description: Basic stock information + x-description-zh: 股票基本信息 + x-description-zh-hk: 股票基本信息 + - name: └ ∟ code + type: string + required: false + description: Ticker code (e.g. `TSLA`) + x-description-zh: 股票代码(如 `TSLA`) + x-description-zh-hk: 股票代碼(如 `TSLA`) + - name: └ ∟ name + type: string + required: false + description: Security name + x-description-zh: 证券名称 + x-description-zh-hk: 證券名稱 + - name: └ ∟ change + type: string + required: false + description: Price change ratio (e.g. `-0.0388`) + x-description-zh: 涨跌幅(如 `-0.0388`) + x-description-zh-hk: 漲跌幅(如 `-0.0388`) + - name: └ ∟ last_done + type: string + required: false + description: Latest trade price + x-description-zh: 最新成交价 + x-description-zh-hk: 最新成交價 + - name: └ ∟ market + type: string + required: false + description: 'Market: `US`, `HK`, `CN`, `SG`' + x-description-zh: 市场:`US`、`HK`、`CN`、`SG` + x-description-zh-hk: 市場:`US`、`HK`、`CN`、`SG` + - name: └ ∟ labels + type: string[] + required: false + description: Industry / theme tags + x-description-zh: 行业 / 主题标签 + x-description-zh-hk: 行業 / 主題標籤 + - name: └ ∟ logo + type: string + required: false + description: Logo image URL + x-description-zh: Logo 图片 URL + x-description-zh-hk: Logo 圖片 URL + - name: └ ∟ trade_status + type: integer + required: false + description: Trading status code + x-description-zh: 交易状态码 + x-description-zh-hk: 交易狀態碼 + - name: └ timestamp + type: string + required: false + description: Event time (Unix seconds as string) + x-description-zh: 异动时间(Unix 秒,字符串格式) + x-description-zh-hk: 異動時間(Unix 秒,字符串格式) + - name: └ alert_reason + type: string + required: false + description: Description of the move reason + x-description-zh: 异动原因描述 + x-description-zh-hk: 異動原因描述 + - name: └ alert_type + type: integer + required: false + description: Move type code + x-description-zh: 异动类型代码 + x-description-zh-hk: 異動類型代碼 + - name: └ post + type: object + required: false + description: Associated news article (complex object with `title`, `description_html`, `published_at` and other fields; `null` when no article is linked) + x-description-zh: 关联新闻文章(复杂对象,包含 `title`、`description_html`、`published_at` 等字段;无关联新闻时为 `null`) + x-description-zh-hk: 關聯新聞文章(複雜對象,包含 `title`、`description_html`、`published_at` 等字段;無關聯新聞時為 `null`) + - name: next_params + type: object + required: false + description: Pagination cursor object; pass to the next request to get the next page + x-description-zh: 翻页参数对象,传入下次请求以获取下一页 + x-description-zh-hk: 翻頁參數對象,傳入下次請求以獲取下一頁 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + events: + - stock: + code: TSLA + symbol: TSLA.US + name: 特斯拉 + change: '-0.0388' + last_done: '404.110' + market: US + labels: + - 汽车制造商 + logo: https://assets.lbkrs.com/ticker/ST/US/TSLA.png + trade_status: 0 + timestamp: '1779202097' + alert_reason: 波动超 20 日均值 + alert_type: 11 + post: null + next_params: + visited: + - '11098290' + - '11098478' + - '11099705' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/watchlist/pinned: + post: + operationId: watchlist_pinned + summary: Update Pinned + x-summary-zh: 更新置顶 + x-summary-zh-hk: 更新置頂 + description: | + Pin or unpin a security within a watchlist group to control its display order. + x-description-zh: | + 在自选股分组中置顶或取消置顶指定证券,以控制显示顺序。 + x-description-zh-hk: | + 在自選股分組中置頂或取消置頂指定證券,以控制顯示順序。 + x-subgroup: Watchlist + x-subgroup-zh: 自选股 + x-subgroup-zh-hk: 自選股 + tags: + - Quote + x-parameters: + - name: mode + in: body + type: string + required: true + description: 'Operation mode: `add` (pin to top) or `remove` (unpin)' + x-description-zh: '操作模式:`add`(置顶) 或 `remove`(取消置顶)' + x-description-zh-hk: '操作模式:`add`(置頂) 或 `remove`(取消置頂)' + - name: securities + in: body + type: array + required: true + description: 'Security symbols to pin/unpin, e.g. `["AAPL.US", "700.HK"]` (must already be in the watchlist)' + x-description-zh: '要置顶/取消的证券代码,如 `["AAPL.US", "700.HK"]`(须已在自选中)' + x-description-zh-hk: '要置頂/取消的證券代碼,如 `["AAPL.US", "700.HK"]`(須已在自選中)' + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/watchlist/pinned' \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{"id":"","symbol":"","is_pinned":false}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/watchlist/pinned", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"id": "", "symbol": "", "is_pinned": False}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/watchlist/pinned", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"id": "", "symbol": "", "is_pinned": False}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/watchlist/pinned", { + method: "POST", + headers: { + "Authorization": "Bearer ", + "Content-Type": "application/json", + }, + body: JSON.stringify({ id: "", symbol: "", is_pinned: false }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/watchlist/pinned")) + .header("Authorization", "Bearer ") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"id\":\"\",\"symbol\":\"\",\"is_pinned\":false}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/watchlist/pinned") + .header("Authorization", "Bearer ") + .json(&serde_json::json!({ "id": "" , "symbol": "" , "is_pinned": false })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/watchlist/pinned"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"id\":\"\",\"symbol\":\"\",\"is_pinned\":false}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/watchlist/pinned\", strings.NewReader(`{\"id\":\"\",\"symbol\":\"\",\"is_pinned\":false}`))\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/ai/agents/{agent_id}/conversations: + post: + operationId: ai_conversation + summary: Start Conversation + x-summary-zh: 发起对话 + x-summary-zh-hk: 發起對話 + description: | + Ask a question to the specified Agent. You can start a brand-new conversation, or pass the `chat_uid` of an existing one to continue asking in the same conversation. + + The Agent generates the answer using capabilities such as market data and account access. When the Agent needs more information or confirmation from you, the run is **interrupted** (`status` is `interrupted`) — send your answers via [Continue Conversation](/docs/ai/chat/continue) to resume it. + + Choose the response mode with the `Accept` header: `text/event-stream` streams the run progress and answer via SSE; any other value returns a blocking response with the aggregated final result. + + :::tip How to find an Agent's UID + Besides querying the Agents in Workspace endpoint, you can also get it directly from the Longbridge website: open the Agent's chat page — the URL looks like `https://longbridge.com/en/ai/agents/chatbot/chat`, where `chatbot` is the Agent's UID. + ::: + x-description-zh: | + 向指定 Agent 提问。可以开启一个全新会话,也可以传入已有会话的 `chat_uid` 在同一会话中追加提问。 + + Agent 会结合行情、账户等能力生成回答。当 Agent 需要你补充信息或确认时,本次运行会**中断**(`status` 为 `interrupted`),此时需通过 [继续对话](/zh-CN/docs/ai/chat/continue) 回传答案后才能继续。 + + 通过请求头 `Accept` 选择响应模式:`text/event-stream` 为 SSE 流式,逐步推送运行过程与回答;其他值为阻塞式,一次性返回聚合后的最终结果。 + + :::tip 如何获取 Agent 的 UID + 除通过 Workspace 下的 Agent 接口查询外,也可以直接从 Longbridge 网页端获取:打开 Agent 的对话页,URL 形如 `https://longbridge.com/zh-CN/ai/agents/chatbot/chat`,其中 `chatbot` 即为该 Agent 的 UID。 + ::: + x-description-zh-hk: | + 向指定 Agent 提問。可以開啟一個全新會話,也可以傳入已有會話的 `chat_uid` 在同一會話中追加提問。 + + Agent 會結合行情、賬戶等能力生成回答。當 Agent 需要你補充信息或確認時,本次運行會**中斷**(`status` 為 `interrupted`),此時需通過 [繼續對話](/zh-HK/docs/ai/chat/continue) 回傳答案後才能繼續。 + + 通過請求頭 `Accept` 選擇響應模式:`text/event-stream` 為 SSE 流式,逐步推送運行過程與回答;其他值為阻塞式,一次性返回聚合後的最終結果。 + + :::tip 如何獲取 Agent 的 UID + 除通過 Workspace 下的 Agent 接口查詢外,也可以直接從 Longbridge 網頁端獲取:打開 Agent 的對話頁,URL 形如 `https://longbridge.com/zh-HK/ai/agents/chatbot/chat`,其中 `chatbot` 即為該 Agent 的 UID。 + ::: + x-subgroup: Conversation + x-subgroup-zh: 对话 + x-subgroup-zh-hk: 對話 + tags: + - AI Agent + x-parameters: + - name: agent_id + in: path + type: string + required: true + description: UID of the target Agent; must be a published Agent + x-description-zh: 目标 Agent 的 UID,需为已发布 Agent + x-description-zh-hk: 目標 Agent 的 UID,需為已發佈 Agent + - name: query + in: body + type: string + required: true + description: The user question; must not be empty + x-description-zh: 用户问题,不能为空 + x-description-zh-hk: 用戶問題,不能為空 + - name: chat_uid + in: body + type: string + required: false + description: Identifier of an existing conversation. Pass it to continue that conversation; omit to start a new one + x-description-zh: 已有会话标识。传入则在该会话中继续提问,不传则新建会话 + x-description-zh-hk: 已有會話標識。傳入則在該會話中繼續提問,不傳則新建會話 + - name: parent_message_id + in: body + type: string + required: false + description: Parent message ID, taken from the `message_id` in the previous response. Pass it when asking a follow-up in an existing conversation to attach the new message after the specified one, keeping the message stream in order. Only valid together with `chat_uid`, and the parent message must belong to that conversation; must not be set for a new conversation + x-description-zh: 父消息 ID,取上一轮响应中的 `message_id`。在已有会话中追加提问时传入,将本轮消息挂在指定消息之后,保证消息流顺序。仅在传入 `chat_uid` 时有效,且父消息必须属于该会话;新会话不可传 + x-description-zh-hk: 父消息 ID,取上一輪響應中的 `message_id`。在已有會話中追加提問時傳入,將本輪消息掛在指定消息之後,保證消息流順序。僅在傳入 `chat_uid` 時有效,且父消息必須屬於該會話;新會話不可傳 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/ai/agents//conversations' \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{"query":"","chat_uid":"","parent_message_id":""}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/ai/agents//conversations", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"query": "", "chat_uid": "", "parent_message_id": ""}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/ai/agents//conversations", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"query": "", "chat_uid": "", "parent_message_id": ""}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/ai/agents//conversations", { + method: "POST", + headers: { + "Authorization": "Bearer ", + "Content-Type": "application/json", + }, + body: JSON.stringify({ query: "", chat_uid: "", parent_message_id: "" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/ai/agents//conversations")) + .header("Authorization", "Bearer ") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"query\":\"\",\"chat_uid\":\"\",\"parent_message_id\":\"\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/ai/agents//conversations") + .header("Authorization", "Bearer ") + .json(&serde_json::json!({ "query": "" , "chat_uid": "" , "parent_message_id": "" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/ai/agents//conversations"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"query\":\"\",\"chat_uid\":\"\",\"parent_message_id\":\"\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/ai/agents//conversations\", strings.NewReader(`{\"query\":\"\",\"chat_uid\":\"\",\"parent_message_id\":\"\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: chat_uid + type: string + required: true + description: Conversation identifier, used for follow-up questions and troubleshooting + x-description-zh: 会话标识,追加提问或排查问题时使用 + x-description-zh-hk: 會話標識,追加提問或排查問題時使用 + - name: message_id + type: string + required: true + description: Message ID of this round (as a string) + x-description-zh: 本轮消息 ID(字符串形式) + x-description-zh-hk: 本輪消息 ID(字符串形式) + - name: status + type: string + required: true + description: 'Final run status: `succeeded` / `interrupted` / `failed` / `stopped`' + x-description-zh: 运行终态:`succeeded` / `interrupted` / `failed` / `stopped` + x-description-zh-hk: 運行終態:`succeeded` / `interrupted` / `failed` / `stopped` + - name: answer + type: string + required: false + description: Final answer text; valid when `status` is `succeeded` + x-description-zh: 最终回答文本,`status` 为 `succeeded` 时有效 + x-description-zh-hk: 最終回答文本,`status` 為 `succeeded` 時有效 + - name: references + type: object[] + required: false + description: Sources referenced by the answer; `null` if none + x-description-zh: 回答引用的资料来源,无引用时为 `null` + x-description-zh-hk: 回答引用的資料來源,無引用時為 `null` + - name: elapsed_time + type: number + required: false + description: Run duration in seconds + x-description-zh: 运行耗时(秒) + x-description-zh-hk: 運行耗時(秒) + - name: interrupt + type: object + required: false + description: Present only when `status` is `interrupted` + x-description-zh: 仅当 `status` 为 `interrupted` 时出现 + x-description-zh-hk: 僅當 `status` 為 `interrupted` 時出現 + - name: └ node_id + type: string + required: true + description: ID of the node that triggered the interrupt + x-description-zh: 触发中断的节点 ID + x-description-zh-hk: 觸發中斷的節點 ID + - name: └ tool_call_id + type: string + required: true + description: Tool call ID of this inquiry; used as the answer key when continuing + x-description-zh: 本次询问对应的工具调用 ID,继续对话时作为答案的 key + x-description-zh-hk: 本次詢問對應的工具調用 ID,繼續對話時作為答案的 key + - name: └ questions + type: object[] + required: true + description: Questions you need to answer + x-description-zh: 需要你回答的问题列表 + x-description-zh-hk: 需要你回答的問題列表 + - name: └ ∟ question + type: string + required: true + description: Question text + x-description-zh: 问题文本 + x-description-zh-hk: 問題文本 + - name: └ ∟ options + type: object[] + required: false + description: Options; empty means free-form answer + x-description-zh: 可选项,为空表示自由作答 + x-description-zh-hk: 可選項,為空表示自由作答 + - name: └ ∟∟ description + type: string + required: false + description: Option text + x-description-zh: 选项文本 + x-description-zh-hk: 選項文本 + - name: └ ∟ multi_select + type: boolean + required: false + description: Whether multiple options may be selected + x-description-zh: 是否允许多选 + x-description-zh-hk: 是否允許多選 + - name: └ message_id + type: int64 + required: false + description: ID of the paused message + x-description-zh: 被暂停的消息 ID + x-description-zh-hk: 被暫停的消息 ID + - name: └ chat_id + type: int64 + required: false + description: ID of the owning conversation + x-description-zh: 所属会话 ID + x-description-zh-hk: 所屬會話 ID + - name: error + type: object + required: false + description: Present only when the run failed + x-description-zh: 仅当运行出错时出现 + x-description-zh-hk: 僅當運行出錯時出現 + - name: └ code + type: int32 + required: false + description: Error code + x-description-zh: 错误码 + x-description-zh-hk: 錯誤碼 + - name: └ message + type: string + required: false + description: Error message + x-description-zh: 错误信息 + x-description-zh-hk: 錯誤信息 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + chat_uid: ct_9f2c1a5b + message_id: '42' + status: succeeded + answer: Tesla (TSLA.US) recently... + references: + - index: 1 + title: ... + url: ... + elapsed_time: 3.21 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/ai/agents/{agent_id}/conversations/{chat_uid}/messages/{message_id}/continue: + post: + operationId: ai_continue + summary: Continue Conversation + x-summary-zh: 继续对话 + x-summary-zh-hk: 繼續對話 + description: | + When [Start Conversation](/docs/ai/chat/conversation) returns `status = interrupted`, the Agent is waiting for more information from you. Send your answers via this endpoint and the paused run resumes from the interrupt point, until it succeeds, gets interrupted again, or fails. + + A single round of conversation may be interrupted multiple times: if the run returns `interrupted` again after continuing, call this endpoint again with the new `interrupt`. + + As with Start Conversation, choose blocking / SSE streaming response mode with the `Accept` header. + x-description-zh: | + 当 [发起对话](/zh-CN/docs/ai/chat/conversation) 返回 `status = interrupted` 时,Agent 正等待你补充信息。通过本接口回传答案,暂停的运行会从中断处继续执行,直到成功、再次中断或失败。 + + 同一轮对话可能发生多次中断:若继续后再次返回 `interrupted`,按新的 `interrupt` 重复调用本接口即可。 + + 与发起对话一致,通过请求头 `Accept` 选择阻塞式 / SSE 流式响应。 + x-description-zh-hk: | + 當 [發起對話](/zh-HK/docs/ai/chat/conversation) 返回 `status = interrupted` 時,Agent 正等待你補充信息。通過本接口回傳答案,暫停的運行會從中斷處繼續執行,直到成功、再次中斷或失敗。 + + 同一輪對話可能發生多次中斷:若繼續後再次返回 `interrupted`,按新的 `interrupt` 重複調用本接口即可。 + + 與發起對話一致,通過請求頭 `Accept` 選擇阻塞式 / SSE 流式響應。 + x-subgroup: Conversation + x-subgroup-zh: 对话 + x-subgroup-zh-hk: 對話 + tags: + - AI Agent + x-parameters: + - name: agent_id + in: path + type: string + required: true + description: Agent UID. + x-description-zh: Agent UID. + x-description-zh-hk: Agent UID. + - name: chat_uid + in: path + type: string + required: true + description: Conversation UID of the interrupted run. + x-description-zh: 被中断会话的 UID。 + x-description-zh-hk: 被中斷會話的 UID。 + - name: message_id + in: path + type: string + required: true + description: ID of the interrupted message to continue. + x-description-zh: 要继续的被中断消息 ID。 + x-description-zh-hk: 要繼續的被中斷消息 ID。 + - name: answers_by_tool_call + in: body + type: object + required: true + description: Answers keyed by tool-call ID, providing the information the Agent asked for so the interrupted run can resume. + x-description-zh: 按工具调用 ID 组织的回答,提供 Agent 所需信息以恢复被中断的运行。 + x-description-zh-hk: 按工具調用 ID 組織的回答,提供 Agent 所需信息以恢復被中斷的運行。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue' \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{"answers_by_tool_call":{}}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"answers_by_tool_call": {}}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"answers_by_tool_call": {}}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue", { + method: "POST", + headers: { + "Authorization": "Bearer ", + "Content-Type": "application/json", + }, + body: JSON.stringify({ answers_by_tool_call: {} }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue")) + .header("Authorization", "Bearer ") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"answers_by_tool_call\":{}}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue") + .header("Authorization", "Bearer ") + .json(&serde_json::json!({ "answers_by_tool_call": {} })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"answers_by_tool_call\":{}}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/ai/agents//conversations//messages//continue\", strings.NewReader(`{\"answers_by_tool_call\":{}}`))\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: chat_uid + type: string + required: true + description: Conversation UID. + x-description-zh: 会话 UID。 + x-description-zh-hk: 會話 UID。 + - name: message_id + type: string + required: true + description: Message ID. + x-description-zh: 消息 ID。 + x-description-zh-hk: 消息 ID。 + - name: status + type: string + required: true + description: Run status, e.g. `succeeded`, `interrupted`. + x-description-zh: 运行状态,如 `succeeded`、`interrupted`。 + x-description-zh-hk: 運行狀態,如 `succeeded`、`interrupted`。 + - name: answer + type: string + required: false + description: The Agent's answer text. + x-description-zh: Agent 的回答文本。 + x-description-zh-hk: Agent 的回答文本。 + - name: references + type: array + required: false + description: Reference sources cited in the answer. + x-description-zh: 回答引用的参考来源。 + x-description-zh-hk: 回答引用的參考來源。 + - name: elapsed_time + type: number + required: false + description: Elapsed run time in seconds. + x-description-zh: 运行耗时(秒)。 + x-description-zh-hk: 運行耗時(秒)。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + chat_uid: ct_9f2c1a5b + message_id: '43' + status: succeeded + answer: Over the past month, Tesla (TSLA.US)... + references: [] + elapsed_time: 2.74 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/notify/reminders: + post: + operationId: create_alert + summary: Create Alert + x-summary-zh: 创建股价提醒 + x-summary-zh-hk: 創建股價提醒 + description: | + Create a new price alert for a security when it rises above or falls below a target price. + x-description-zh: | + 为指定证券创建股价提醒,当价格高于或低于目标价时触发通知。 + x-description-zh-hk: | + 為指定證券創建股價提醒,當價格高於或低於目標價時觸發通知。 + x-subgroup: Alerts + x-subgroup-zh: 股价提醒 + x-subgroup-zh-hk: 股價提醒 + tags: + - Account + x-parameters: + - name: symbol + in: body + type: string + required: true + description: Security symbol, e.g. `TSLA.US` + x-description-zh: 证券代码,例如 `TSLA.US` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` + - name: price + in: body + type: string + required: true + description: Target price + x-description-zh: 目标价格 + x-description-zh-hk: 目標價格 + - name: direction + in: body + type: string + required: true + description: 'Alert direction: `rise` or `fall`' + x-description-zh: 提醒方向:`rise`(上涨)或 `fall`(下跌) + x-description-zh-hk: 提醒方向:`rise`(上漲)或 `fall`(下跌) + - name: frequency + in: body + type: string + required: false + description: 'Trigger frequency: `once` (default) or `every`' + x-description-zh: 触发频率:`once`(仅一次,默认)或 `every`(每次) + x-description-zh-hk: 觸發頻率:`once`(僅一次,默認)或 `every`(每次) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge alert add TSLA.US --price 300 --direction rise + longbridge alert add AAPL.US --price 150 --direction fall + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/notify/reminders' \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{"symbol":"","price":"","direction":"","frequency":""}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/notify/reminders", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"symbol": "", "price": "", "direction": "", "frequency": ""}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/notify/reminders", + headers={"Authorization": "Bearer ", "Content-Type": "application/json"}, + json={"symbol": "", "price": "", "direction": "", "frequency": ""}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/notify/reminders", { + method: "POST", + headers: { + "Authorization": "Bearer ", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbol: "", price: "", direction: "", frequency: "" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/notify/reminders")) + .header("Authorization", "Bearer ") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbol\":\"\",\"price\":\"\",\"direction\":\"\",\"frequency\":\"\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/notify/reminders") + .header("Authorization", "Bearer ") + .json(&serde_json::json!({ "symbol": "" , "price": "" , "direction": "" , "frequency": "" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/notify/reminders"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbol\":\"\",\"price\":\"\",\"direction\":\"\",\"frequency\":\"\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/notify/reminders\", strings.NewReader(`{\"symbol\":\"\",\"price\":\"\",\"direction\":\"\",\"frequency\":\"\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: id + type: int64 + required: true + description: ID of the newly created alert + x-description-zh: 新创建提醒的 ID + x-description-zh-hk: 新創建提醒的 ID + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + id: 486469 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + get: + operationId: list_alerts + summary: List Alerts + x-summary-zh: 获取股价提醒列表 + x-summary-zh-hk: 獲取股價提醒列表 + description: | + Get all price alerts for the current user, with optional filtering by symbol. + x-description-zh: | + 获取当前用户的所有股价提醒,支持按标的筛选。 + x-description-zh-hk: | + 獲取當前用戶的所有股價提醒,支持按標的篩選。 + x-subgroup: Alerts + x-subgroup-zh: 股价提醒 + x-subgroup-zh-hk: 股價提醒 + tags: + - Account + x-parameters: + - name: symbol + in: query + type: string + required: false + description: Filter by security symbol, e.g. `TSLA.US` + x-description-zh: 按证券代码筛选,例如 `TSLA.US` + x-description-zh-hk: 按證券代碼篩選,例如 `TSLA.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge alert + longbridge alert TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/notify/reminders' \ + --header 'Authorization: Bearer ' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/notify/reminders", + headers={"Authorization": "Bearer "}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/notify/reminders", + headers={"Authorization": "Bearer "}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/notify/reminders", { + method: "GET", + headers: { + "Authorization": "Bearer ", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/notify/reminders")) + .header("Authorization", "Bearer ") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/notify/reminders") + .header("Authorization", "Bearer ") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/notify/reminders"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/notify/reminders\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: lists + type: object[] + required: false + description: Alert groups per security + x-description-zh: 按标的分组的提醒列表,见 AlertSymbolGroup + x-description-zh-hk: 按標的分組的提醒列表,見 AlertSymbolGroup + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ chg + type: string + required: false + description: Day change amount + x-description-zh: 当日涨跌额 + x-description-zh-hk: 當日漲跌額 + - name: └ price + type: string + required: false + description: Latest price + x-description-zh: 最新价 + x-description-zh-hk: 最新價 + - name: └ p_chg + type: string + required: false + description: Day change percentage + x-description-zh: 当日涨跌幅 + x-description-zh-hk: 當日漲跌幅 + - name: └ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ market + type: string + required: false + description: Market + x-description-zh: 市场 + x-description-zh-hk: 市場 + - name: └ code + type: string + required: false + description: Ticker code + x-description-zh: 股票代码 + x-description-zh-hk: 股票代碼 + - name: └ indicators + type: object[] + required: false + description: Alert indicators + x-description-zh: 股价提醒列表,见 AlertItem + x-description-zh-hk: 股價提醒列表,見 AlertItem + - name: └ ∟ id + type: string + required: false + description: Alert ID + x-description-zh: 提醒 ID + x-description-zh-hk: 提醒 ID + - name: └ ∟ indicator_id + type: string + required: false + description: 'Condition: `1`=price_rise, `2`=price_fall, `3`=pct_rise, `4`=pct_fall' + x-description-zh: 条件:`1`=价格上涨,`2`=价格下跌,`3`=涨幅,`4`=跌幅 + x-description-zh-hk: 條件:`1`=價格上漲,`2`=價格下跌,`3`=漲幅,`4`=跌幅 + - name: └ ∟ frequency + type: integer + required: false + description: 'Frequency: `1`=daily, `2`=every_time, `3`=once' + x-description-zh: 触发频率:`1`=每日,`2`=每次,`3`=一次 + x-description-zh-hk: 觸發頻率:`1`=每日,`2`=每次,`3`=一次 + - name: └ ∟ enabled + type: boolean + required: false + description: Whether the alert is active + x-description-zh: 是否启用 + x-description-zh-hk: 是否啟用 + - name: └ ∟ text + type: string + required: false + description: Display text + x-description-zh: 显示文本 + x-description-zh-hk: 顯示文本 + - name: └ ∟ scope + type: integer + required: false + description: Scope + x-description-zh: 范围 + x-description-zh-hk: 範圍 + - name: └ ∟ value_map + type: object + required: false + description: Trigger value (e.g. `{"price":"400"}` or `{"chg":"5"}`) + x-description-zh: 触发值(如 `{"price":"400"}` 或 `{"chg":"5"}`) + x-description-zh-hk: 觸發值(如 `{"price":"400"}` 或 `{"chg":"5"}`) + - name: └ ∟ ∟ price + type: string + required: false + description: Latest price + x-description-zh: 最新价 + x-description-zh-hk: 最新價 + - name: └ ∟ state + type: array + required: false + description: Trigger state flags + x-description-zh: 触发状态标志 + x-description-zh-hk: 觸發狀態標誌 + - name: └ product + type: string + required: false + description: Product type + x-description-zh: 产品类型 + x-description-zh-hk: 產品類型 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + lists: + - symbol: AAPL.US + code: AAPL + market: US + name: Apple + price: '298.87' + chg: '4.07' + p_chg: '1.38' + product: stock + indicators: + - id: '514050' + indicator_id: '1' + enabled: true + frequency: 2 + scope: 0 + text: 价格涨到 400 + state: + - 1 + value_map: + price: '400' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + operationId: delete_alert + summary: Delete Alert + x-summary-zh: 删除股价提醒 + x-summary-zh-hk: 刪除股價提醒 + description: | + Delete a price alert by its ID. + x-description-zh: | + 根据 ID 删除指定的股价提醒。 + x-description-zh-hk: | + 根據 ID 刪除指定的股價提醒。 + x-subgroup: Alerts + x-subgroup-zh: 股价提醒 + x-subgroup-zh-hk: 股價提醒 + tags: + - Account + x-parameters: + - name: id + in: query + type: integer + required: true + description: Alert ID (path parameter) + x-description-zh: 提醒 ID(路径参数) + x-description-zh-hk: 提醒 ID(路徑參數) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge alert delete 486469 + longbridge alert delete 112326 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request DELETE \ + --url 'https://openapi.longbridge.com/v1/notify/reminders?id=' \ + --header 'Authorization: Bearer ' + - lang: Python + label: Python + source: | + import requests + + resp = requests.delete( + "https://openapi.longbridge.com/v1/notify/reminders", + headers={"Authorization": "Bearer "}, + params={"id": ""}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.delete( + "https://openapi.longbridge.com/v1/notify/reminders", + headers={"Authorization": "Bearer "}, + params={"id": ""}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/notify/reminders") + url.searchParams.set("id", "") + + const resp = await fetch(url, { + method: "DELETE", + headers: { + "Authorization": "Bearer ", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/notify/reminders?id=")) + .header("Authorization", "Bearer ") + .method("DELETE", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::DELETE, "https://openapi.longbridge.com/v1/notify/reminders") + .header("Authorization", "Bearer ") + .query(&[("id", "")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer "); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/notify/reminders?id="); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"DELETE\", \"https://openapi.longbridge.com/v1/notify/reminders?id=\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer \")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/content/topics: + post: + operationId: create_topic + summary: Create Topic + x-summary-zh: 创建讨论 + x-summary-zh-hk: 創建討論 + description: | + Create a new community topic on [Topics](https://longbridge.com/topics). Two content types are supported: + + | Type | `title` | `body` format | Notes | + | ---------------- | ------------ | --------------- | ---------------------------------------------------------------------------------------------------------------------- | + | `post` (default) | Optional | Plain text only | Markdown syntax (e.g. `**bold**`, `# heading`) is NOT rendered — it appears as literal characters, similar to a tweet. | + | `article` | **Required** | Markdown | The server converts Markdown to HTML for display. Supports headers, tables, bold, code blocks, etc. | + + Only users who have opened a **Longbridge account and hold assets** are allowed to publish community topics and replies via Longbridge Developers API or CLI. Returns `403` otherwise. + + :::tip Tip + Stock symbols mentioned in the body (e.g. `700.HK`, `TSLA.US`) are automatically recognized and linked as related stocks by the platform. Use `tickers` to associate additional symbols not explicitly mentioned in the body. + + ⚠️ Do not abuse symbol linking to associate unrelated stocks. Content moderation may restrict publishing or mute the account. + ::: + + **Rate limit:** Max 3 topics per user per minute and 10 per 24 hours. Exceeding the limit returns `429`. + + :::warning + ⚠️ Rate limit thresholds are for reference only and may be adjusted by the platform at any time. + ::: + x-description-zh: | + 在 [社区](https://longbridge.com/topics) 创建一篇新讨论。支持两种内容类型: + + | 类型 | `title` | `body` 格式 | 说明 | + |------|---------|-------------|------| + | `post`(默认) | 可选 | 纯文本 | Markdown 语法(如 `**加粗**`、`# 标题`)**不会渲染**,将作为字面字符显示,类似发推文。 | + | `article` | **必填** | Markdown | 服务端将 Markdown 转为 HTML 展示,支持标题、表格、加粗、代码块等。 | + + 仅限 **Longbridge 开户且持有资产** 的用户才允许通过 Longbridge Developers 的 API 或 CLI 发布社区讨论和回复。否则返回 `403`。 + + :::tip Tip + 正文中提到的标的代码(如 `700.HK`、`TSLA.US`)会被平台自动识别并关联为相关标的。`tickers` 字段用于补充正文中未显式提及的标的。 + + ⚠️ 请勿滥用此功能关联与内容无关的标的,否则后台内容运营可能会限制发布,甚至有可能禁言。 + ::: + + **频率限制:** 同一用户每分钟最多创建 3 篇,24 小时内最多 10 篇,超出返回 `429`。 + + :::warning + ⚠️ 以上频率限制规则仅供参考,平台可能随时进行内部调整。 + ::: + x-description-zh-hk: | + 在 [社區](https://longbridge.com/topics) 創建一篇新討論。支持兩種內容類型: + + | 類型 | `title` | `body` 格式 | 說明 | + |------|---------|-------------|------| + | `post`(默認) | 可選 | 純文本 | Markdown 語法(如 `**加粗**`、`# 標題`)**不會渲染**,將作為字面字符顯示,類似發推文。 | + | `article` | **必填** | Markdown | 服務端將 Markdown 轉為 HTML 展示,支持標題、表格、加粗、代碼塊等。 | + + 僅限 **Longbridge 開戶且持有資產** 的用戶才允許通過 Longbridge Developers 的 API 或 CLI 發布社區討論和回覆。否則返回 `403`。 + + :::tip Tip + 正文中提到的標的代碼(如 `700.HK`、`TSLA.US`)會被平台自動識別並關聯為相關標的。`tickers` 字段用於補充正文中未顯式提及的標的。 + + ⚠️ 請勿濫用此功能關聯與內容無關的標的,否則後台內容運營可能會限制發布,甚至有可能禁言。 + ::: + + **頻率限制:** 同一用戶每分鐘最多創建 3 篇,24 小時內最多 10 篇,超出返回 `429`。 + + :::warning + ⚠️ 以上頻率限制規則僅供參考,平台可能隨時進行內部調整。 + ::: + x-subgroup: Topics + x-subgroup-zh: 话题 + x-subgroup-zh-hk: 話題 + tags: + - News & Contents + x-parameters: + - name: title + in: body + type: string + required: false + description: Topic title. Required when `topic_type` is `article`; optional for `post`. + x-description-zh: 标题。`topic_type` 为 `article` 时必填,`post` 时可省略。 + x-description-zh-hk: 標題。`topic_type` 為 `article` 時必填,`post` 時可省略。 + - name: body + in: body + type: string + required: true + description: 'Topic body. - For `post`: plain text only — Markdown is not rendered. - For `article`: Markdown is supported.' + x-description-zh: 正文。`post` 类型为纯文本,Markdown 不渲染;`article` 类型支持 Markdown。 + x-description-zh-hk: 正文。`post` 類型為純文本,Markdown 不渲染;`article` 類型支持 Markdown。 + - name: topic_type + in: body + type: string + required: false + description: 'Content type: `post` (plain text, default) or `article` (Markdown).' + x-description-zh: 内容类型:`post`(纯文本,默认)或 `article`(Markdown) + x-description-zh-hk: 內容類型:`post`(純文本,默認)或 `article`(Markdown) + - name: tickers + in: body + type: array + required: false + description: Related security symbols, format `{symbol}.{market}` (e.g. `["AAPL.US", "700.HK"]`). Maximum 10. **Note:** Symbols mentioned in the body (e.g. `700.HK`, `TSLA.US`) are automatically recognized and linked by the platform. Use `tickers` to associate additional symbols not explicitly mentioned in the body. + x-description-zh: 关联标的代码,格式 `{symbol}.{market}`,如 `["AAPL.US", "700.HK"]`,最多 10 个。**注意:** 正文中提到的标的代码(如 `700.HK`、`TSLA.US`)会被平台自动识别并关联,`tickers` 用于补充正文中未显式提及的标的。 + x-description-zh-hk: 關聯標的代碼,格式 `{symbol}.{market}`,如 `["AAPL.US", "700.HK"]`,最多 10 個。**注意:** 正文中提到的標的代碼(如 `700.HK`、`TSLA.US`)會被平台自動識別並關聯,`tickers` 用於補充正文中未顯式提及的標的。 + - name: hashtags + in: body + type: array + required: false + description: Hashtag names (e.g. `["earnings", "fed"]`). Maximum 1. + x-description-zh: 讨论标签名称列表,如 `["earnings", "fed"]`,最多 1 个 + x-description-zh-hk: 討論標籤名稱列表,如 `["earnings", "fed"]`,最多 1 個 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/content/topics' \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{"title":"","body":"<body>","topic_type":"<topic_type>","tickers":[],"hashtags":[]}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/content/topics", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"title": "<title>", "body": "<body>", "topic_type": "<topic_type>", "tickers": [], "hashtags": []}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/content/topics", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"title": "<title>", "body": "<body>", "topic_type": "<topic_type>", "tickers": [], "hashtags": []}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/content/topics", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ title: "<title>", body: "<body>", topic_type: "<topic_type>", tickers: [], hashtags: [] }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/content/topics")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"title\":\"<title>\",\"body\":\"<body>\",\"topic_type\":\"<topic_type>\",\"tickers\":[],\"hashtags\":[]}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/content/topics") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "title": "<title>" , "body": "<body>" , "topic_type": "<topic_type>" , "tickers": [] , "hashtags": [] })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/content/topics"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"title\":\"<title>\",\"body\":\"<body>\",\"topic_type\":\"<topic_type>\",\"tickers\":[],\"hashtags\":[]}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/content/topics\", strings.NewReader(`{\"title\":\"<title>\",\"body\":\"<body>\",\"topic_type\":\"<topic_type>\",\"tickers\":[],\"hashtags\":[]}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-codeSamples: + - lang: Shell + label: CLI + source: | + # publish a topic for Tesla + longbridge topic create --body "Tesla Q1 earnings analysis" --tickers TSLA.US + # publish a topic for Apple + longbridge topic create --body "Apple WWDC preview" --tickers AAPL.US + x-response-properties: + - name: item + type: object + required: true + description: Newly created topic details + x-description-zh: 新建讨论详情 + x-description-zh-hk: 新建討論詳情 + - name: └ id + type: string + required: true + description: Topic ID + x-description-zh: 讨论 ID + x-description-zh-hk: 討論 ID + - name: └ title + type: string + required: false + description: Topic title + x-description-zh: 标题 + x-description-zh-hk: 標題 + - name: └ description + type: string + required: false + description: Plain-text summary (auto-generated from body) + x-description-zh: 纯文本摘要(由正文自动截取) + x-description-zh-hk: 純文本摘要(由正文自動截取) + - name: └ body + type: string + required: false + description: Full body text (Markdown for `article`) + x-description-zh: 完整正文(`article` 类型为 Markdown) + x-description-zh-hk: 完整正文(`article` 類型為 Markdown) + - name: └ topic_type + type: string + required: false + description: Topic type. One of `article`, `post` + x-description-zh: 内容类型,`article` 或 `post` + x-description-zh-hk: 內容類型,`article` 或 `post` + - name: └ tickers + type: string[] + required: false + description: Associated security symbols + x-description-zh: 关联标的代码 + x-description-zh-hk: 關聯標的代碼 + - name: └ hashtags + type: string[] + required: false + description: Associated hashtag names + x-description-zh: 讨论标签名称列表 + x-description-zh-hk: 討論標籤名稱列表 + - name: └ images + type: object[] + required: false + description: Image list + x-description-zh: 附图列表 + x-description-zh-hk: 附圖列表 + - name: └ ∟ url + type: string + required: false + description: Original image URL + x-description-zh: 原始图片 URL + x-description-zh-hk: 原始圖片 URL + - name: └ ∟ sm + type: string + required: false + description: Small thumbnail URL + x-description-zh: 小缩略图 URL + x-description-zh-hk: 小縮略圖 URL + - name: └ ∟ lg + type: string + required: false + description: Large thumbnail URL + x-description-zh: 大缩略图 URL + x-description-zh-hk: 大縮略圖 URL + - name: └ likes_count + type: int32 + required: false + description: Number of likes + x-description-zh: 点赞数 + x-description-zh-hk: 點讚數 + - name: └ comments_count + type: int32 + required: false + description: Number of replies + x-description-zh: 回复数 + x-description-zh-hk: 回覆數 + - name: └ views_count + type: int32 + required: false + description: Number of views + x-description-zh: 浏览数 + x-description-zh-hk: 瀏覽數 + - name: └ shares_count + type: int32 + required: false + description: Number of shares + x-description-zh: 分享数 + x-description-zh-hk: 分享數 + - name: └ detail_url + type: string + required: false + description: Direct URL to the topic + x-description-zh: 讨论页面直链 + x-description-zh-hk: 討論頁面直鏈 + - name: └ author + type: object + required: false + description: Author information + x-description-zh: 作者信息 + x-description-zh-hk: 作者信息 + - name: └ ∟ member_id + type: string + required: false + description: Author member ID + x-description-zh: 作者 member ID + x-description-zh-hk: 作者 member ID + - name: └ ∟ name + type: string + required: false + description: Author display name + x-description-zh: 作者昵称 + x-description-zh-hk: 作者暱稱 + - name: └ ∟ avatar + type: string + required: false + description: Author avatar URL + x-description-zh: 作者头像 URL + x-description-zh-hk: 作者頭像 URL + - name: └ created_at + type: string + required: true + description: Unix timestamp (seconds) when the topic was created + x-description-zh: 创建时间,Unix 时间戳(秒) + x-description-zh-hk: 創建時間,Unix 時間戳(秒) + - name: └ updated_at + type: string + required: false + description: Unix timestamp (seconds) of last update + x-description-zh: 最近更新时间,Unix 时间戳(秒) + x-description-zh-hk: 最近更新時間,Unix 時間戳(秒) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + item: + id: '39304657' + title: My View on AAPL + topic_type: article + tickers: + - AAPL.US + hashtags: + - earnings + created_at: '1742000000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/content/topics/{topic_id}/comments: + post: + operationId: create_topic_reply + summary: Create Topic Reply + x-summary-zh: 创建讨论回复 + x-summary-zh-hk: 創建討論回覆 + description: | + Post a reply to a community topic. Supports nesting under an existing reply. Browse the community on [Topics](https://longbridge.com/topics). + + Only users who have opened a **[Longbridge account](https://longbridge.com/hk/download) and hold assets** are allowed to publish community topics and replies via Longbridge Developers API or CLI. Returns `403` otherwise. + + **Body format:** Plain text only — HTML and Markdown are **not** rendered. + + :::tip Tip + Stock symbols mentioned in the body (e.g. `700.HK`, `TSLA.US`) are automatically recognized and linked as related stocks by the platform. + + ⚠️ Do not abuse symbol linking to associate unrelated stocks. Content moderation may restrict publishing or mute the account. + ::: + + **Rate limit:** The first 3 replies per user per topic have no wait requirement. After that, each subsequent reply must wait an incrementally longer interval since the previous one: + + | Reply # (after 3rd) | Required wait | + | ------------------- | ------------- | + | 4th | 3 s | + | 5th | 5 s | + | 6th | 8 s | + | 7th | 13 s | + | 8th | 21 s | + | 9th | 34 s | + | 10th+ | 55 s (cap) | + + Exceeding the limit returns `429`. + + :::warning + ⚠️ Rate limit thresholds are for reference only and may be adjusted by the platform at any time. + ::: + x-description-zh: | + 在指定讨论下发布回复,支持嵌套回复已有回复。完整社区讨论可访问 [社区](https://longbridge.com/topics)。 + + 仅限 **[Longbridge 开户](https://longbridge.com/hk/download) 且持有资产** 的用户才允许通过 Longbridge Developers 的 API 或 CLI 发布社区讨论和回复。否则返回 `403`。 + + **正文格式:** 仅支持纯文本,不支持 HTML 或 Markdown。 + + :::tip Tip + 正文中提到的标的代码(如 `700.HK`、`TSLA.US`)会被平台自动识别并关联为相关标的。 + + ⚠️ 请勿滥用此功能关联与内容无关的标的,否则后台内容运营可能会限制发布,甚至有可能禁言。 + ::: + + **频率限制:** 同一用户在同一讨论下,前 3 条无间隔限制;此后每条须与上一条保持递增间隔(3 s → 5 s → 8 s → 13 s → 21 s → 34 s → 55 s 封顶),超出限制返回 `429`。 + + :::warning + ⚠️ 以上频率限制规则仅供参考,平台可能随时进行内部调整。 + ::: + x-description-zh-hk: | + 在指定討論下發布回覆,支持嵌套回覆已有回覆。完整社區討論可瀏覽 [社區](https://longbridge.com/topics)。 + + 僅限 **[Longbridge 開戶](https://longbridge.com/hk/download) 且持有資產** 的用戶才允許通過 Longbridge Developers 的 API 或 CLI 發布社區討論和回覆。否則返回 `403`。 + + **正文格式:** 僅支持純文本,不支持 HTML 或 Markdown。 + + :::tip Tip + 正文中提到的標的代碼(如 `700.HK`、`TSLA.US`)會被平台自動識別並關聯為相關標的。 + + ⚠️ 請勿濫用此功能關聯與內容無關的標的,否則後台內容運營可能會限制發布,甚至有可能禁言。 + ::: + + **頻率限制:** 同一用戶在同一討論下,前 3 條無間隔限制;此後每條須與上一條保持遞增間隔(3 s → 5 s → 8 s → 13 s → 21 s → 34 s → 55 s 封頂),超出限制返回 `429`。 + + :::warning + ⚠️ 以上頻率限制規則僅供參考,平台可能隨時進行內部調整。 + ::: + x-subgroup: Topics + x-subgroup-zh: 话题 + x-subgroup-zh-hk: 話題 + tags: + - News & Contents + x-parameters: + - name: topic_id + in: path + type: string + required: true + description: Topic ID (e.g. `6993508780031016960`) + x-description-zh: 讨论 ID,如 `6993508780031016960` + x-description-zh-hk: 討論 ID,如 `6993508780031016960` + - name: body + in: body + type: string + required: true + description: Reply body. Plain text only — Markdown is not rendered. Symbols mentioned in the body are auto-linked by the platform. + x-description-zh: 回复正文,仅支持纯文本。正文中提到的标的代码会被平台自动识别并关联。 + x-description-zh-hk: 回覆正文,僅支持純文本。正文中提到的標的代碼會被平台自動識別並關聯。 + - name: reply_to_id + in: body + type: string + required: false + description: ID of the reply to nest under. Omit or set to `"0"` for a top-level reply. + x-description-zh: 被回复的回复 ID;不填或填 `"0"` 表示发顶层回复,填入有效 ID 则嵌套在该回复下。 + x-description-zh-hk: 被回覆的回覆 ID;不填或填 `"0"` 表示發頂層回覆,填入有效 ID 則嵌套在該回覆下。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"body":"<body>","reply_to_id":"<reply_to_id>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"body": "<body>", "reply_to_id": "<reply_to_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"body": "<body>", "reply_to_id": "<reply_to_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ body: "<body>", reply_to_id: "<reply_to_id>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"body\":\"<body>\",\"reply_to_id\":\"<reply_to_id>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "body": "<body>" , "reply_to_id": "<reply_to_id>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"body\":\"<body>\",\"reply_to_id\":\"<reply_to_id>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/content/topics/<topic_id>/comments\", strings.NewReader(`{\"body\":\"<body>\",\"reply_to_id\":\"<reply_to_id>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge topic create-reply 6993508780031016960 --body "Great analysis!" + x-response-properties: + - name: item + type: object + required: true + description: Created reply details + x-description-zh: 新建回复详情 + x-description-zh-hk: 新建回覆詳情 + - name: └ id + type: string + required: true + description: Reply ID + x-description-zh: 回复 ID + x-description-zh-hk: 回覆 ID + - name: └ topic_id + type: string + required: true + description: Parent topic ID + x-description-zh: 所属讨论 ID + x-description-zh-hk: 所屬討論 ID + - name: └ body + type: string + required: false + description: Reply body (plain text) + x-description-zh: 回复正文(纯文本) + x-description-zh-hk: 回覆正文(純文本) + - name: └ reply_to_id + type: string + required: false + description: Parent reply ID; `"0"` = top-level reply + x-description-zh: 父回复 ID,`"0"` 表示顶层回复 + x-description-zh-hk: 父回覆 ID,`"0"` 表示頂層回覆 + - name: └ author + type: object + required: false + description: Author info + x-description-zh: 作者信息 + x-description-zh-hk: 作者信息 + - name: └ ∟ member_id + type: string + required: false + description: Author member ID + x-description-zh: 作者 member ID + x-description-zh-hk: 作者 member ID + - name: └ ∟ name + type: string + required: false + description: Author display name + x-description-zh: 作者昵称 + x-description-zh-hk: 作者暱稱 + - name: └ ∟ avatar + type: string + required: false + description: Author avatar URL + x-description-zh: 作者头像 URL + x-description-zh-hk: 作者頭像 URL + - name: └ images + type: object[] + required: false + description: Attached images + x-description-zh: 附图列表 + x-description-zh-hk: 附圖列表 + - name: └ ∟ url + type: string + required: false + description: Original image URL + x-description-zh: 原始图片 URL + x-description-zh-hk: 原始圖片 URL + - name: └ ∟ sm + type: string + required: false + description: Small thumbnail URL + x-description-zh: 小缩略图 URL + x-description-zh-hk: 小縮略圖 URL + - name: └ ∟ lg + type: string + required: false + description: Large image URL + x-description-zh: 大缩略图 URL + x-description-zh-hk: 大縮略圖 URL + - name: └ likes_count + type: int32 + required: false + description: Likes count + x-description-zh: 点赞数 + x-description-zh-hk: 點讚數 + - name: └ comments_count + type: int32 + required: false + description: Nested replies count + x-description-zh: 嵌套回复数 + x-description-zh-hk: 嵌套回覆數 + - name: └ created_at + type: string + required: true + description: Creation time as Unix timestamp (seconds) + x-description-zh: 创建时间,Unix 时间戳(秒) + x-description-zh-hk: 創建時間,Unix 時間戳(秒) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + item: + id: '7001234567890123460' + topic_id: '6993508780031016960' + body: Great analysis! + reply_to_id: '0' + author: + member_id: '10086' + name: Jane Doe + avatar: https://example.com/avatar.jpg + images: [] + likes_count: 0 + comments_count: 0 + created_at: '1742002000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/sharelists: + post: + operationId: create_sharelist + summary: Create Sharelist + x-summary-zh: 创建股单 + x-summary-zh-hk: 創建股單 + description: | + Create a new community stock list with an optional initial set of securities. + x-description-zh: | + 创建新的社区自选股列表,可选择预设初始证券。 + x-description-zh-hk: | + 創建新的社區自選股列表,可選擇預設初始證券。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 + tags: + - News & Contents + x-parameters: + - name: name + in: body + type: string + required: true + description: 'Sharelist name (required)' + x-description-zh: '股单名称 (必填)' + x-description-zh-hk: '股單名稱 (必填)' + - name: cover + in: body + type: string + required: true + description: 'Cover image URL (required)' + x-description-zh: '封面图 URL(必填)' + x-description-zh-hk: '封面圖 URL(必填)' + - name: description + in: body + type: string + required: true + description: Description + x-description-zh: 描述 + x-description-zh-hk: 描述 + - name: securities + in: body + type: array + required: false + description: Initial list of security symbols, e.g. `["AAPL.US", "NVDA.US"]` + x-description-zh: 初始证券代码列表,例如 `["AAPL.US", "NVDA.US"]` + x-description-zh-hk: 初始證券代碼列表,例如 `["AAPL.US", "NVDA.US"]` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge sharelist create --name "AI Picks" --description "Top AI infrastructure stocks" + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/sharelists' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"description":"<description>","securities":[]}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/sharelists", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"description": "<description>", "securities": []}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/sharelists", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"description": "<description>", "securities": []}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/sharelists", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ description: "<description>", securities: [] }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"description\":\"<description>\",\"securities\":[]}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/sharelists") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "description": "<description>" , "securities": [] })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"description\":\"<description>\",\"securities\":[]}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/sharelists\", strings.NewReader(`{\"description\":\"<description>\",\"securities\":[]}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: id + type: int64 + required: true + description: ID of the newly created sharelist + x-description-zh: 新创建股单的 ID + x-description-zh-hk: 新創建股單的 ID + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + id: 15922 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + get: + operationId: list_sharelists + summary: List Sharelists + x-summary-zh: 股单列表 + x-summary-zh-hk: 股單列表 + description: | + Get all community stock lists (sharelists) created by or subscribed to by the current user. + x-description-zh: | + 获取当前用户创建的或订阅的所有社区自选股列表。 + x-description-zh-hk: | + 獲取當前用戶創建的或訂閱的所有社區自選股列表。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 + tags: + - News & Contents + x-parameters: + - name: type + in: query + type: string + required: false + description: 'Filter: `mine` or `subscribed`. Omit for both.' + x-description-zh: 筛选:`mine`(我创建的)或 `subscribed`(我订阅的),不传则返回两者 + x-description-zh-hk: 篩選:`mine`(我創建的)或 `subscribed`(我訂閱的),不傳則返回兩者 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge sharelist + longbridge sharelist --format json + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/sharelists' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/sharelists", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/sharelists", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/sharelists", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/sharelists") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/sharelists\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: sharelists + type: object[] + required: false + description: User's own sharelists + x-description-zh: 用户自建股单列表,见 SharelistInfo + x-description-zh-hk: 用戶自建股單列表,見 SharelistInfo + - name: subscribed_sharelists + type: object[] + required: false + description: Subscribed sharelists + x-description-zh: 已订阅股单列表,见 SharelistInfo + x-description-zh-hk: 已訂閱股單列表,見 SharelistInfo + - name: tail_mark + type: string + required: false + description: Pagination cursor for subscribed list + x-description-zh: 已订阅列表的分页游标 + x-description-zh-hk: 已訂閱列表的分頁遊標 + - name: id + type: integer + required: true + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID + - name: description + type: string + required: false + description: Description + x-description-zh: 描述 + x-description-zh-hk: 描述 + - name: cover + type: string + required: false + description: Cover image URL + x-description-zh: 封面图片 URL + x-description-zh-hk: 封面圖片 URL + - name: subscribers_count + type: integer + required: false + description: Number of subscribers + x-description-zh: 订阅人数 + x-description-zh-hk: 訂閱人數 + - name: chg + type: string + required: false + description: Day change percentage + x-description-zh: 日涨跌幅 + x-description-zh-hk: 日漲跌幅 + - name: this_year_chg + type: string + required: false + description: Year-to-date change percentage + x-description-zh: 今年以来涨跌幅 + x-description-zh-hk: 今年以來漲跌幅 + - name: subscribed + type: boolean + required: false + description: Whether the current user is subscribed + x-description-zh: 当前用户是否已订阅 + x-description-zh-hk: 當前用戶是否已訂閱 + - name: sharelist_type + type: integer + required: false + description: 'Type: `0`=regular, `3`=official, `4`=industry' + x-description-zh: 类型:`0`=普通,`3`=官方,`4`=行业 + x-description-zh-hk: 類型:`0`=普通,`3`=官方,`4`=行業 + - name: industry_code + type: string + required: false + description: Industry code (for industry sharelists) + x-description-zh: 行业代码(行业股单适用) + x-description-zh-hk: 行業代碼(行業股單適用) + - name: symbol + type: string + required: true + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: code + type: string + required: false + description: Ticker code + x-description-zh: 股票代码 + x-description-zh-hk: 股票代碼 + - name: market + type: string + required: false + description: Market + x-description-zh: 市场 + x-description-zh-hk: 市場 + - name: intro + type: string + required: false + description: Brief description + x-description-zh: 简介 + x-description-zh-hk: 簡介 + - name: last_done + type: string + required: false + description: Latest price + x-description-zh: 最新价格 + x-description-zh-hk: 最新價格 + - name: change + type: string + required: false + description: Day change percentage + x-description-zh: 日涨跌幅 + x-description-zh-hk: 日漲跌幅 + - name: trade_status + type: integer + required: false + description: Trade status code + x-description-zh: 交易状态码 + x-description-zh-hk: 交易狀態碼 + - name: latency + type: boolean + required: false + description: Whether quote data is delayed + x-description-zh: 是否为延迟行情数据 + x-description-zh-hk: 是否為延遲行情數據 + - name: unread_change_log_category + type: string + required: false + description: Unread change log category + x-description-zh: 未读变更日志分类 + x-description-zh-hk: 未讀變更日誌分類 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + mine: + - id: 15921 + name: AI Picks + type: Regular + day_change: '-0.40' + ytd_change: '6.64' + subscribers: 500 + subscribed: [] + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/sharelists/{id}/items: + post: + operationId: sharelist_add_securities + summary: Add Securities to Sharelist + x-summary-zh: 添加标的到股单 + x-summary-zh-hk: 新增標的到股單 + description: | + Add one or more securities to a sharelist. + x-description-zh: | + 向股单中添加一个或多个标的。 + x-description-zh-hk: | + 向股單中新增一個或多個標的。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 + tags: + - News & Contents + x-parameters: + - name: id + in: path + type: integer + required: true + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID + - name: symbols + in: body + type: string + required: true + description: 'Comma-separated security symbols, e.g. `AAPL.US,700.HK` (NOT a JSON array)' + x-description-zh: '逗号分隔的证券代码,如 `AAPL.US,700.HK`(不是 JSON 数组)' + x-description-zh-hk: '逗號分隔的證券代碼,如 `AAPL.US,700.HK`(不是 JSON 陣列)' + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge sharelist add 123 TSLA.US AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/sharelists/<id>/items' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbols":[]}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/sharelists/<id>/items", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbols": []}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/sharelists/<id>/items", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbols": []}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/sharelists/<id>/items", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbols: [] }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists/<id>/items")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbols\":[]}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/sharelists/<id>/items") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbols": [] })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists/<id>/items"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbols\":[]}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/sharelists/<id>/items\", strings.NewReader(`{\"symbols\":[]}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + operationId: sharelist_remove_securities + summary: Remove Securities from Sharelist + x-summary-zh: 从股单移除标的 + x-summary-zh-hk: 從股單移除標的 + description: | + Remove one or more securities from a sharelist. + x-description-zh: | + 从股单中移除一个或多个标的。 + x-description-zh-hk: | + 從股單中移除一個或多個標的。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 + tags: + - News & Contents + x-parameters: + - name: id + in: path + type: integer + required: true + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID + - name: symbols + in: query + type: array + required: true + description: Security symbols to remove + x-description-zh: 待移除的标的代码 + x-description-zh-hk: 待移除的標的代碼 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge sharelist remove 123 TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request DELETE \ + --url 'https://openapi.longbridge.com/v1/sharelists/<id>/items?symbols=<symbols>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.delete( + "https://openapi.longbridge.com/v1/sharelists/<id>/items", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbols": "<symbols>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.delete( + "https://openapi.longbridge.com/v1/sharelists/<id>/items", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbols": "<symbols>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/sharelists/<id>/items") + url.searchParams.set("symbols", "<symbols>") + + const resp = await fetch(url, { + method: "DELETE", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists/<id>/items?symbols=<symbols>")) + .header("Authorization", "Bearer <access_token>") + .method("DELETE", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::DELETE, "https://openapi.longbridge.com/v1/sharelists/<id>/items") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbols", "<symbols>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists/<id>/items?symbols=<symbols>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"DELETE\", \"https://openapi.longbridge.com/v1/sharelists/<id>/items?symbols=<symbols>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/sharelists/{id}/items/sort: + post: + operationId: sharelist_sort_securities + summary: Sort Securities in Sharelist + x-summary-zh: 股单标的排序 + x-summary-zh-hk: 股單標的排序 + description: | + Reorder the securities in a sharelist. The symbols list defines the new order. + x-description-zh: | + 对股单中的标的重新排序。传入的标的代码列表即为新顺序。 + x-description-zh-hk: | + 對股單中的標的重新排序。傳入的標的代碼列表即為新順序。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 + tags: + - News & Contents + x-parameters: + - name: id + in: path + type: integer + required: true + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID + - name: symbols + in: body + type: string + required: true + description: 'Comma-separated security symbols, e.g. `AAPL.US,700.HK` (NOT a JSON array)' + x-description-zh: '逗号分隔的证券代码,如 `AAPL.US,700.HK`(不是 JSON 数组)' + x-description-zh-hk: '逗號分隔的證券代碼,如 `AAPL.US,700.HK`(不是 JSON 陣列)' + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge sharelist sort 123 TSLA.US AAPL.US 700.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/sharelists/<id>/items/sort' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbols":[]}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/sharelists/<id>/items/sort", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbols": []}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/sharelists/<id>/items/sort", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbols": []}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/sharelists/<id>/items/sort", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbols: [] }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists/<id>/items/sort")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbols\":[]}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/sharelists/<id>/items/sort") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbols": [] })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists/<id>/items/sort"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbols\":[]}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/sharelists/<id>/items/sort\", strings.NewReader(`{\"symbols\":[]}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/trade/order: + post: + operationId: submit_order + summary: Submit Order + x-summary-zh: 委托下单 + x-summary-zh-hk: 委託下單 + description: | + This API is used to submit order for HK and US stocks, warrant and option. + x-description-zh: | + 该接口用于港美股,窝轮,期权的委托下单。 + x-description-zh-hk: | + 該接口用於港美股,窩輪,期權的委託下單。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 + tags: + - Trade + x-parameters: + - name: symbol + in: body + type: string + required: true + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: order_type + in: body + type: string + required: true + description: 'Order Type. One of: `LO`, `ELO`, `MO`, `AO`, `ALO`, `ODD`, `LIT`, `MIT`, `TSLPAMT`, `TSLPPCT`, `SLO`.' + x-description-zh: 订单类型。可选值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + x-description-zh-hk: 訂單類型。可選值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + - name: submitted_price + in: body + type: string + required: false + description: 'Submitted price, example: `388.5`. `LO` / `ELO` / `ALO` / `ODD` / `LIT` Order Required' + x-description-zh: 下单价格,例如:`388.5`. `LO` / `ELO` / `ALO` / `ODD` / `LIT` 订单必填 + x-description-zh-hk: 下單價格,例如:`388.5`. `LO` / `ELO` / `ALO` / `ODD` / `LIT` 訂單必填 + - name: submitted_quantity + in: body + type: string + required: true + description: 'Submitted quantity, example: `100`' + x-description-zh: 下单数量,例如:`100` + x-description-zh-hk: 下單數量,例如:`100` + - name: trigger_price + in: body + type: string + required: false + description: 'Trigger price, example: `388.5`. `LIT` / `MIT` Order Required' + x-description-zh: 触发价格,例如:`388.5`. `LIT` / `MIT` 订单必填 + x-description-zh-hk: 觸發價格,例如:`388.5`. `LIT` / `MIT` 訂單必填 + - name: limit_offset + in: body + type: string + required: false + description: Limit offset amount. `TSLPAMT` / `TSLPPCT` Order Required when`limit_depth_level` is set to 0 + x-description-zh: 指定价差,例如 "1.2" 表示价差 1.2 USD (如果是美股). `TSLPAMT` / `TSLPPCT` 订单在 `limit_depth_level` 为 0 时必填 + x-description-zh-hk: 指定價差。`TSLPAMT` / `TSLPPCT` 訂單在 `limit_depth_level` 為 0 時必填 + - name: trailing_amount + in: body + type: string + required: false + description: Trailing amount. `TSLPAMT` Order Required + x-description-zh: 跟踪金额。`TSLPAMT` 订单必填 + x-description-zh-hk: 跟蹤金額。`TSLPAMT` 訂單必填 + - name: trailing_percent + in: body + type: string + required: false + description: Trailing percent. `TSLPPCT` Order Required + x-description-zh: 跟踪涨跌幅,单位为百分比,例如 "2.5" 表示 "2.5%". `TSLPPCT` 订单必填 + x-description-zh-hk: 跟蹤漲跌幅。`TSLPPCT` 訂單必填 + - name: expire_date + in: body + type: string + required: false + description: 'Long term order expire date, format `YYYY-MM-DD`, example: `2022-12-05`. Required when `time_in_force` is `GTD`' + x-description-zh: 长期单过期时间,格式为 `YYYY-MM-DD`, 例如:`2022-12-05`. time_in_force 为 `GTD` 时必填 + x-description-zh-hk: 長期單過期時間,格式為 `YYYY-MM-DD`, 例如:`2022-12-05`. time_in_force 為 `GTD` 時必填 + - name: side + in: body + type: string + required: true + description: Order Side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: outside_rth + in: body + type: string + required: false + description: Enable or disable outside regular trading hours. **Enum Value:**, `RTH_ONLY` - regular trading hour only, `ANY_TIME` - any time, `OVERNIGHT` - Overnight, `OPTION_PRE_MARKET` - Overnight option + x-description-zh: 是否允许盘前盘后,美股必填。**可选值:**, `RTH_ONLY` - 不允许盘前盘后,`ANY_TIME` - 允许盘前盘后,`OVERNIGHT` - 夜盘,`OPTION_PRE_MARKET` - 夜盘期权 + x-description-zh-hk: 是否允許盤前盤後,美股必填。**可選值:**, `RTH_ONLY` - 不允許盤前盤後,`ANY_TIME` - 允許盤前盤後,`OVERNIGHT` - 夜盤,`OPTION_PRE_MARKET` - 夜盤期權 + - name: time_in_force + in: body + type: string + required: true + description: Time in force Type. **Enum Value:**, `Day` - Day Order, `GTC` - Good Til Canceled Order, `GTD` - Good Til Date Order + x-description-zh: 订单有效期类型。**可选值:**, `Day` - 当日有效,`GTC` - 撤单前有效,`GTD` - 到期前有效 + x-description-zh-hk: 訂單有效期類型。**可選值:**, `Day` - 當日有效,`GTC` - 撤單前有效,`GTD` - 到期前有效 + - name: remark + in: body + type: string + required: false + description: remark (Maximum 255 characters) + x-description-zh: 备注 (最大 64 字符) + x-description-zh-hk: 備註 (最大 64 字符) + - name: limit_depth_level + in: body + type: integer + required: false + description: Specifies the bid/ask depth level. Value range is -5 ~ 0 ~ 5. Negative numbers indicate bid levels (e.g. -1 means best bid level 1), positive numbers indicate ask levels (e.g. 1 means best ask level 1). When set to 0, the `limit_offset` parameter takes effect. Valid for `TSLPAMT` / `TSLPPCT` orders + x-description-zh: 指定买卖档位,取值范围为 -5 ~ 0 ~ 5,负数代表买盘档位(如 -1 表示买一),, 正数代表卖盘档位(如 1 表示卖一),为 0 时 limit_offset 参数生效,`TSLPAMT` / `TSLPPCT` 订单有效 + x-description-zh-hk: 指定買賣檔位,取值範圍為 -5 ~ 0 ~ 5,負數代表買盤檔位(例如 -1 表示買一),, 正數代表賣盤檔位(例如 1 表示賣一),當為 0 時 limit_offset 參數生效,`TSLPAMT` / `TSLPPCT` 訂單有效 + - name: monitor_price + in: body + type: string + required: false + description: Monitoring price. Monitoring starts only after reaching this price, updating the reference price. Valid for `TSLPAMT` / `TSLPPCT` orders + x-description-zh: 监控价格,需要达到该价格才会开始监控,更新参考价,`TSLPAMT` / `TSLPPCT` 订单有效 + x-description-zh-hk: 監控價格,需要達到該價格才會開始監控,更新參考價,`TSLPAMT` / `TSLPPCT` 訂單有效 + - name: trigger_count + in: body + type: integer + required: false + description: Number of triggers. Value range is 0 ~ 3. Specifies that within 1 minute, the order will only be placed after being triggered multiple times. Valid for `LIT` / `MIT` / `TSLPAMT` / `TSLPPCT` orders + x-description-zh: 触发次数,取值范围 0 ~ 3, 表示在 1 分钟内触发多次才会触发订单,`LIT` / `MIT` / `TSLPAMT` / `TSLPPCT` 订单有效 + x-description-zh-hk: 觸發次數,取值範圍 0 ~ 3,表示在 1 分鐘內觸發多次才會觸發訂單,, `LIT` / `MIT` / `TSLPAMT` / `TSLPPCT` 訂單有效 + - name: client_request_id + in: body + type: string + required: false + description: Idempotent request ID for preventing duplicate order submissions. The server caches this request ID for 10 minutes. If a request with the same ID is received within this period, it returns the same response without creating a duplicate order. Must be a unique identifier (e.g., UUID). + x-description-zh: 幂等性请求 ID,用于防止重复下单。服务器会缓存该请求 ID 10 分钟。在此期间内如果收到相同 ID 的请求,将返回原始响应而不创建重复订单。必须是唯一标识符(如 UUID)。 + x-description-zh-hk: 冪等性請求 ID,用於防止重複下單。服務器會快取該請求 ID 10 分鐘。在此期間內如果收到相同 ID 的請求,將返回原始響應而不建立重複訂單。必須是唯一標識符(如 UUID)。 + - name: attached_params + in: body + type: object + required: false + description: Attached order parameters (take-profit / stop-loss) + x-description-zh: 附加单参数(止盈止损) + x-description-zh-hk: 附加單參數(止盈止損) + - name: attached_params.attached_order_type + in: body + type: string + required: false + description: Attached order type. **Enum Value:**, `PROFIT_TAKER` - Take Profit, `STOP_LOSS` - Stop Loss, `BRACKET` - Bracket Order + x-description-zh: 附加单订单类型。**可选值:**, `PROFIT_TAKER` - 止盈,`STOP_LOSS` - 止损,`BRACKET` - 括号单 + x-description-zh-hk: 附加單訂單類型。**可選值:**, `PROFIT_TAKER` - 止盈,`STOP_LOSS` - 止損,`BRACKET` - 括號單 + - name: attached_params.profit_taker_price + in: body + type: string + required: false + description: Take-profit trigger price + x-description-zh: 止盈触发价格 + x-description-zh-hk: 止盈觸發價格 + - name: attached_params.stop_loss_price + in: body + type: string + required: false + description: Stop-loss trigger price + x-description-zh: 止损触发价格 + x-description-zh-hk: 止損觸發價格 + - name: attached_params.time_in_force + in: body + type: string + required: false + description: Attached order time in force type. **Enum Value:**, `Day` - Day Order, `GTC` - Good Til Canceled Order, `GTD` - Good Til Date Order (inherits the main order's `expire_date` in this case) + x-description-zh: 附加单有效期类型。**可选值:**, `Day` - 当日有效,`GTC` - 撤单前有效,`GTD` - 到期前有效(此时继承主单 expire_date) + x-description-zh-hk: 附加單有效期類型。**可選值:**, `Day` - 當日有效,`GTC` - 撤單前有效,`GTD` - 到期前有效(此時繼承主單 expire_date) + - name: attached_params.expire_time + in: body + type: integer + required: false + description: Expire time (Unix timestamp, in seconds) + x-description-zh: 到期时间(Unix 时间戳,单位秒) + x-description-zh-hk: 到期時間(Unix 時間戳,單位秒) + - name: attached_params.activate_order_type + in: body + type: string + required: false + description: Order type submitted after triggering, e.g. `LIT` (limit-if-touched) or `MIT` (market-if-touched) + x-description-zh: 触发后提交的订单类型,例如 `LIT`(限价单)或 `MIT`(市价单) + x-description-zh-hk: 觸發後提交的訂單類型,例如 `LIT`(限價單)或 `MIT`(市價單) + - name: attached_params.profit_taker_submit_price + in: body + type: string + required: false + description: Take-profit limit order submitted price, required when `activate_order_type` is `LIT` + x-description-zh: 止盈限价委托价格,`activate_order_type` 为 `LIT` 时必填 + x-description-zh-hk: 止盈限價委託價格,`activate_order_type` 為 `LIT` 時必填 + - name: attached_params.stop_loss_submit_price + in: body + type: string + required: false + description: Stop-loss limit order submitted price, required when `activate_order_type` is `LIT` + x-description-zh: 止损限价委托价格,`activate_order_type` 为 `LIT` 时必填 + x-description-zh-hk: 止損限價委託價格,`activate_order_type` 為 `LIT` 時必填 + - name: attached_params.activate_rth + in: body + type: string + required: false + description: Whether the order submitted after triggering allows pre/post market trading. **Enum Value:**, `RTH_ONLY` - Regular trading hours only, `ANY_TIME` - Any time + x-description-zh: 触发后提交的订单是否允许盘前盘后。**可选值:**, `RTH_ONLY` - 不允许盘前盘后,`ANY_TIME` - 允许盘前盘后 + x-description-zh-hk: 觸發後提交的訂單是否允許盤前盤後。**可選值:**, `RTH_ONLY` - 不允許盤前盤後,`ANY_TIME` - 允許盤前盤後 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge order buy TSLA.US 100 --price 250.00 + longbridge order sell TSLA.US 100 --price 260.00 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/trade/order' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbol":"<symbol>","order_type":"<order_type>","submitted_price":"<submitted_price>","submitted_quantity":"<submitted_quantity>","trigger_price":"<trigger_price>","limit_offset":"<limit_offset>","trailing_amount":"<trailing_amount>","trailing_percent":"<trailing_percent>","expire_date":"<expire_date>","side":"<side>","outside_rth":"<outside_rth>","time_in_force":"<time_in_force>","remark":"<remark>","limit_depth_level":0,"monitor_price":"<monitor_price>","trigger_count":0,"client_request_id":"<client_request_id>","attached_params":{"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"}}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/trade/order", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "order_type": "<order_type>", "submitted_price": "<submitted_price>", "submitted_quantity": "<submitted_quantity>", "trigger_price": "<trigger_price>", "limit_offset": "<limit_offset>", "trailing_amount": "<trailing_amount>", "trailing_percent": "<trailing_percent>", "expire_date": "<expire_date>", "side": "<side>", "outside_rth": "<outside_rth>", "time_in_force": "<time_in_force>", "remark": "<remark>", "limit_depth_level": 0, "monitor_price": "<monitor_price>", "trigger_count": 0, "client_request_id": "<client_request_id>", "attached_params": {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"}}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/trade/order", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "order_type": "<order_type>", "submitted_price": "<submitted_price>", "submitted_quantity": "<submitted_quantity>", "trigger_price": "<trigger_price>", "limit_offset": "<limit_offset>", "trailing_amount": "<trailing_amount>", "trailing_percent": "<trailing_percent>", "expire_date": "<expire_date>", "side": "<side>", "outside_rth": "<outside_rth>", "time_in_force": "<time_in_force>", "remark": "<remark>", "limit_depth_level": 0, "monitor_price": "<monitor_price>", "trigger_count": 0, "client_request_id": "<client_request_id>", "attached_params": {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"}}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/trade/order", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbol: "<symbol>", order_type: "<order_type>", submitted_price: "<submitted_price>", submitted_quantity: "<submitted_quantity>", trigger_price: "<trigger_price>", limit_offset: "<limit_offset>", trailing_amount: "<trailing_amount>", trailing_percent: "<trailing_percent>", expire_date: "<expire_date>", side: "<side>", outside_rth: "<outside_rth>", time_in_force: "<time_in_force>", remark: "<remark>", limit_depth_level: 0, monitor_price: "<monitor_price>", trigger_count: 0, client_request_id: "<client_request_id>", attached_params: {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"} }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/order")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbol\":\"<symbol>\",\"order_type\":\"<order_type>\",\"submitted_price\":\"<submitted_price>\",\"submitted_quantity\":\"<submitted_quantity>\",\"trigger_price\":\"<trigger_price>\",\"limit_offset\":\"<limit_offset>\",\"trailing_amount\":\"<trailing_amount>\",\"trailing_percent\":\"<trailing_percent>\",\"expire_date\":\"<expire_date>\",\"side\":\"<side>\",\"outside_rth\":\"<outside_rth>\",\"time_in_force\":\"<time_in_force>\",\"remark\":\"<remark>\",\"limit_depth_level\":0,\"monitor_price\":\"<monitor_price>\",\"trigger_count\":0,\"client_request_id\":\"<client_request_id>\",\"attached_params\":{\"attached_order_type\":\"<attached_order_type>\",\"profit_taker_price\":\"<profit_taker_price>\",\"stop_loss_price\":\"<stop_loss_price>\",\"time_in_force\":\"<time_in_force>\",\"expire_time\":0,\"activate_order_type\":\"<activate_order_type>\",\"profit_taker_submit_price\":\"<profit_taker_submit_price>\",\"stop_loss_submit_price\":\"<stop_loss_submit_price>\",\"activate_rth\":\"<activate_rth>\"}}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/trade/order") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbol": "<symbol>" , "order_type": "<order_type>" , "submitted_price": "<submitted_price>" , "submitted_quantity": "<submitted_quantity>" , "trigger_price": "<trigger_price>" , "limit_offset": "<limit_offset>" , "trailing_amount": "<trailing_amount>" , "trailing_percent": "<trailing_percent>" , "expire_date": "<expire_date>" , "side": "<side>" , "outside_rth": "<outside_rth>" , "time_in_force": "<time_in_force>" , "remark": "<remark>" , "limit_depth_level": 0 , "monitor_price": "<monitor_price>" , "trigger_count": 0 , "client_request_id": "<client_request_id>" , "attached_params": {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"} })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/order"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbol\":\"<symbol>\",\"order_type\":\"<order_type>\",\"submitted_price\":\"<submitted_price>\",\"submitted_quantity\":\"<submitted_quantity>\",\"trigger_price\":\"<trigger_price>\",\"limit_offset\":\"<limit_offset>\",\"trailing_amount\":\"<trailing_amount>\",\"trailing_percent\":\"<trailing_percent>\",\"expire_date\":\"<expire_date>\",\"side\":\"<side>\",\"outside_rth\":\"<outside_rth>\",\"time_in_force\":\"<time_in_force>\",\"remark\":\"<remark>\",\"limit_depth_level\":0,\"monitor_price\":\"<monitor_price>\",\"trigger_count\":0,\"client_request_id\":\"<client_request_id>\",\"attached_params\":{\"attached_order_type\":\"<attached_order_type>\",\"profit_taker_price\":\"<profit_taker_price>\",\"stop_loss_price\":\"<stop_loss_price>\",\"time_in_force\":\"<time_in_force>\",\"expire_time\":0,\"activate_order_type\":\"<activate_order_type>\",\"profit_taker_submit_price\":\"<profit_taker_submit_price>\",\"stop_loss_submit_price\":\"<stop_loss_submit_price>\",\"activate_rth\":\"<activate_rth>\"}}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/trade/order\", strings.NewReader(`{\"symbol\":\"<symbol>\",\"order_type\":\"<order_type>\",\"submitted_price\":\"<submitted_price>\",\"submitted_quantity\":\"<submitted_quantity>\",\"trigger_price\":\"<trigger_price>\",\"limit_offset\":\"<limit_offset>\",\"trailing_amount\":\"<trailing_amount>\",\"trailing_percent\":\"<trailing_percent>\",\"expire_date\":\"<expire_date>\",\"side\":\"<side>\",\"outside_rth\":\"<outside_rth>\",\"time_in_force\":\"<time_in_force>\",\"remark\":\"<remark>\",\"limit_depth_level\":0,\"monitor_price\":\"<monitor_price>\",\"trigger_count\":0,\"client_request_id\":\"<client_request_id>\",\"attached_params\":{\"attached_order_type\":\"<attached_order_type>\",\"profit_taker_price\":\"<profit_taker_price>\",\"stop_loss_price\":\"<stop_loss_price>\",\"time_in_force\":\"<time_in_force>\",\"expire_time\":0,\"activate_order_type\":\"<activate_order_type>\",\"profit_taker_submit_price\":\"<profit_taker_submit_price>\",\"stop_loss_submit_price\":\"<stop_loss_submit_price>\",\"activate_rth\":\"<activate_rth>\"}}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + order_id: 683615454870679600 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + operationId: replace_order + summary: Replace Order + x-summary-zh: 修改订单 + x-summary-zh-hk: 修改訂單 + description: | + This API is used to replace order, modify quantity or price. + x-description-zh: | + 该接口用于修改订单的价格,数量。 + x-description-zh-hk: | + 該接口用於修改訂單的價格,數量。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 + tags: + - Trade + x-parameters: + - name: order_id + in: body + type: string + required: true + description: Order ID + x-description-zh: 订单 ID + x-description-zh-hk: 訂單 ID + - name: quantity + in: body + type: string + required: true + description: 'Replaced quantity, example: `100`' + x-description-zh: 改单数量,例如:`200` + x-description-zh-hk: 改單數量,例如:`200` + - name: price + in: body + type: string + required: false + description: 'Replaced price, example: `388.5`. `LO` / `ELO` / `ALO` / `ODD` / `LIT` Order Required' + x-description-zh: 改单价格,例如:`388.5`. `LO` / `ELO` / `ALO` / `ODD` / `LIT` 订单必填 + x-description-zh-hk: 改單價格,例如:`388.5`. `LO` / `ELO` / `ALO` / `ODD` / `LIT` 訂單必填 + - name: trigger_price + in: body + type: string + required: false + description: 'Trigger price, example: `388.5`. `LIT` / `MIT` Order Required' + x-description-zh: 触发价格,例如:`388.5`. `LIT` / `MIT` 订单必填 + x-description-zh-hk: 觸發價格,例如:`388.5`. `LIT` / `MIT` 訂單必填 + - name: limit_offset + in: body + type: string + required: false + description: Limit offset amount. `TSLPAMT` / `TSLPPCT` Order Required when`limit_depth_level` is set to 0 + x-description-zh: 指定价差。`TSLPAMT` / `TSLPPCT` 订单在 `limit_depth_level` 为 0 时必填 + x-description-zh-hk: 指定價差。`TSLPAMT` / `TSLPPCT` 訂單在 `limit_depth_level` 為 0 時必填 + - name: trailing_amount + in: body + type: string + required: false + description: Trailing amount. `TSLPAMT` Order Required + x-description-zh: 跟踪金额。`TSLPAMT` 订单必填 + x-description-zh-hk: 跟蹤金額。`TSLPAMT` 訂單必填 + - name: trailing_percent + in: body + type: string + required: false + description: Trailing percent. `TSLPPCT` Order Required + x-description-zh: 跟踪涨跌幅。`TSLPPCT` 订单必填 + x-description-zh-hk: 跟蹤漲跌幅。`TSLPPCT` 訂單必填 + - name: remark + in: body + type: string + required: false + description: Remark (Maximum 64 characters) + x-description-zh: 备注 (最大 64 字符) + x-description-zh-hk: 備註 (最大 64 字符) + - name: limit_depth_level + in: body + type: integer + required: false + description: Specifies the bid/ask depth level. `TSLPAMT` / `TSLPPCT` Order Required + x-description-zh: 指定买卖档位,`TSLPAMT` / `TSLPPCT` 订单必填 + x-description-zh-hk: 指定買賣檔位,`TSLPAMT` / `TSLPPCT` 訂單必填 + - name: monitor_price + in: body + type: string + required: false + description: Monitoring price. `TSLPAMT` / `TSLPPCT` Order Required + x-description-zh: 监控价格,`TSLPAMT` / `TSLPPCT` 订单必填 + x-description-zh-hk: 監控價格,`TSLPAMT` / `TSLPPCT` 訂單必填 + - name: trigger_count + in: body + type: integer + required: false + description: Number of triggers. `LIT` / `MIT` / `TSLPAMT` / `TSLPPCT` Order Required + x-description-zh: 触发次数,`LIT` / `MIT` / `TSLPAMT` / `TSLPPCT` 订单必填 + x-description-zh-hk: 觸發次數,`LIT` / `MIT` / `TSLPAMT` / `TSLPPCT` 訂單必填 + - name: attached_params + in: body + type: object + required: false + description: Attached order parameters (take-profit / stop-loss) + x-description-zh: 附加单参数(止盈止损) + x-description-zh-hk: 附加單參數(止盈止損) + - name: attached_params.attached_order_type + in: body + type: string + required: false + description: Attached order type. **Enum Value:**, `PROFIT_TAKER` - Take Profit, `STOP_LOSS` - Stop Loss, `BRACKET` - Bracket Order + x-description-zh: 附加单订单类型。**可选值:**, `PROFIT_TAKER` - 止盈,`STOP_LOSS` - 止损,`BRACKET` - 括号单 + x-description-zh-hk: 附加單訂單類型。**可選值:**, `PROFIT_TAKER` - 止盈,`STOP_LOSS` - 止損,`BRACKET` - 括號單 + - name: attached_params.profit_taker_price + in: body + type: string + required: false + description: Take-profit trigger price + x-description-zh: 止盈触发价格 + x-description-zh-hk: 止盈觸發價格 + - name: attached_params.stop_loss_price + in: body + type: string + required: false + description: Stop-loss trigger price + x-description-zh: 止损触发价格 + x-description-zh-hk: 止損觸發價格 + - name: attached_params.time_in_force + in: body + type: string + required: false + description: Attached order time in force type. **Enum Value:**, `Day` - Day Order, `GTC` - Good Til Canceled Order, `GTD` - Good Til Date Order (inherits the main order's `expire_date` in this case) + x-description-zh: 附加单有效期类型。**可选值:**, `Day` - 当日有效,`GTC` - 撤单前有效,`GTD` - 到期前有效(此时继承主单 expire_date) + x-description-zh-hk: 附加單有效期類型。**可選值:**, `Day` - 當日有效,`GTC` - 撤單前有效,`GTD` - 到期前有效(此時繼承主單 expire_date) + - name: attached_params.expire_time + in: body + type: integer + required: false + description: Expire time (Unix timestamp, in seconds) + x-description-zh: 到期时间(Unix 时间戳,单位秒) + x-description-zh-hk: 到期時間(Unix 時間戳,單位秒) + - name: attached_params.profit_taker_id + in: body + type: integer + required: false + description: Take-profit order ID, fill in when modifying an existing take-profit order + x-description-zh: 止盈单 ID,修改现有止盈单时填写 + x-description-zh-hk: 止盈單 ID,修改現有止盈單時填寫 + - name: attached_params.stop_loss_id + in: body + type: integer + required: false + description: Stop-loss order ID, fill in when modifying an existing stop-loss order + x-description-zh: 止损单 ID,修改现有止损单时填写 + x-description-zh-hk: 止損單 ID,修改現有止損單時填寫 + - name: attached_params.cancel_all_attached + in: body + type: boolean + required: false + description: Whether to cancel all attached orders + x-description-zh: 是否取消所有附加单 + x-description-zh-hk: 是否取消所有附加單 + - name: attached_params.main_id + in: body + type: integer + required: false + description: Main order ID + x-description-zh: 主单 ID + x-description-zh-hk: 主單 ID + - name: attached_params.quantity + in: body + type: string + required: false + description: Attached order quantity + x-description-zh: 附加单数量 + x-description-zh-hk: 附加單數量 + - name: attached_params.market_price + in: body + type: string + required: false + description: Market price + x-description-zh: 市价 + x-description-zh-hk: 市價 + - name: attached_params.activate_order_type + in: body + type: string + required: false + description: Order type submitted after triggering, e.g. `LIT` (limit-if-touched) or `MIT` (market-if-touched) + x-description-zh: 触发后提交的订单类型,例如 `LIT`(限价单)或 `MIT`(市价单) + x-description-zh-hk: 觸發後提交的訂單類型,例如 `LIT`(限價單)或 `MIT`(市價單) + - name: attached_params.profit_taker_submit_price + in: body + type: string + required: false + description: Take-profit limit order submitted price, required when `activate_order_type` is `LIT` + x-description-zh: 止盈限价委托价格,`activate_order_type` 为 `LIT` 时必填 + x-description-zh-hk: 止盈限價委託價格,`activate_order_type` 為 `LIT` 時必填 + - name: attached_params.stop_loss_submit_price + in: body + type: string + required: false + description: Stop-loss limit order submitted price, required when `activate_order_type` is `LIT` + x-description-zh: 止损限价委托价格,`activate_order_type` 为 `LIT` 时必填 + x-description-zh-hk: 止損限價委託價格,`activate_order_type` 為 `LIT` 時必填 + - name: attached_params.activate_rth + in: body + type: string + required: false + description: Whether the order submitted after triggering allows pre/post market trading. **Enum Value:**, `RTH_ONLY` - Regular trading hours only, `ANY_TIME` - Any time + x-description-zh: 触发后提交的订单是否允许盘前盘后。**可选值:**, `RTH_ONLY` - 不允许盘前盘后,`ANY_TIME` - 允许盘前盘后 + x-description-zh-hk: 觸發後提交的訂單是否允許盤前盤後。**可選值:**, `RTH_ONLY` - 不允許盤前盤後,`ANY_TIME` - 允許盤前盤後 + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Replace the order ID below with your actual order ID + longbridge order replace 693664675163312128 --qty 200 --price 255.00 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request PUT \ + --url 'https://openapi.longbridge.com/v1/trade/order' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"order_id":"<order_id>","quantity":"<quantity>","price":"<price>","trigger_price":"<trigger_price>","limit_offset":"<limit_offset>","trailing_amount":"<trailing_amount>","trailing_percent":"<trailing_percent>","remark":"<remark>","limit_depth_level":0,"monitor_price":"<monitor_price>","trigger_count":0,"attached_params":{"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"profit_taker_id":0,"stop_loss_id":0,"cancel_all_attached":false,"main_id":0,"quantity":"<quantity>","market_price":"<market_price>","activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"}}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.put( + "https://openapi.longbridge.com/v1/trade/order", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>", "quantity": "<quantity>", "price": "<price>", "trigger_price": "<trigger_price>", "limit_offset": "<limit_offset>", "trailing_amount": "<trailing_amount>", "trailing_percent": "<trailing_percent>", "remark": "<remark>", "limit_depth_level": 0, "monitor_price": "<monitor_price>", "trigger_count": 0, "attached_params": {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"profit_taker_id":0,"stop_loss_id":0,"cancel_all_attached":false,"main_id":0,"quantity":"<quantity>","market_price":"<market_price>","activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"}}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.put( + "https://openapi.longbridge.com/v1/trade/order", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>", "quantity": "<quantity>", "price": "<price>", "trigger_price": "<trigger_price>", "limit_offset": "<limit_offset>", "trailing_amount": "<trailing_amount>", "trailing_percent": "<trailing_percent>", "remark": "<remark>", "limit_depth_level": 0, "monitor_price": "<monitor_price>", "trigger_count": 0, "attached_params": {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"profit_taker_id":0,"stop_loss_id":0,"cancel_all_attached":false,"main_id":0,"quantity":"<quantity>","market_price":"<market_price>","activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"}}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/trade/order", { + method: "PUT", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ order_id: "<order_id>", quantity: "<quantity>", price: "<price>", trigger_price: "<trigger_price>", limit_offset: "<limit_offset>", trailing_amount: "<trailing_amount>", trailing_percent: "<trailing_percent>", remark: "<remark>", limit_depth_level: 0, monitor_price: "<monitor_price>", trigger_count: 0, attached_params: {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"profit_taker_id":0,"stop_loss_id":0,"cancel_all_attached":false,"main_id":0,"quantity":"<quantity>","market_price":"<market_price>","activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"} }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/order")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("PUT", HttpRequest.BodyPublishers.ofString("{\"order_id\":\"<order_id>\",\"quantity\":\"<quantity>\",\"price\":\"<price>\",\"trigger_price\":\"<trigger_price>\",\"limit_offset\":\"<limit_offset>\",\"trailing_amount\":\"<trailing_amount>\",\"trailing_percent\":\"<trailing_percent>\",\"remark\":\"<remark>\",\"limit_depth_level\":0,\"monitor_price\":\"<monitor_price>\",\"trigger_count\":0,\"attached_params\":{\"attached_order_type\":\"<attached_order_type>\",\"profit_taker_price\":\"<profit_taker_price>\",\"stop_loss_price\":\"<stop_loss_price>\",\"time_in_force\":\"<time_in_force>\",\"expire_time\":0,\"profit_taker_id\":0,\"stop_loss_id\":0,\"cancel_all_attached\":false,\"main_id\":0,\"quantity\":\"<quantity>\",\"market_price\":\"<market_price>\",\"activate_order_type\":\"<activate_order_type>\",\"profit_taker_submit_price\":\"<profit_taker_submit_price>\",\"stop_loss_submit_price\":\"<stop_loss_submit_price>\",\"activate_rth\":\"<activate_rth>\"}}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::PUT, "https://openapi.longbridge.com/v1/trade/order") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "order_id": "<order_id>" , "quantity": "<quantity>" , "price": "<price>" , "trigger_price": "<trigger_price>" , "limit_offset": "<limit_offset>" , "trailing_amount": "<trailing_amount>" , "trailing_percent": "<trailing_percent>" , "remark": "<remark>" , "limit_depth_level": 0 , "monitor_price": "<monitor_price>" , "trigger_count": 0 , "attached_params": {"attached_order_type":"<attached_order_type>","profit_taker_price":"<profit_taker_price>","stop_loss_price":"<stop_loss_price>","time_in_force":"<time_in_force>","expire_time":0,"profit_taker_id":0,"stop_loss_id":0,"cancel_all_attached":false,"main_id":0,"quantity":"<quantity>","market_price":"<market_price>","activate_order_type":"<activate_order_type>","profit_taker_submit_price":"<profit_taker_submit_price>","stop_loss_submit_price":"<stop_loss_submit_price>","activate_rth":"<activate_rth>"} })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/order"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"order_id\":\"<order_id>\",\"quantity\":\"<quantity>\",\"price\":\"<price>\",\"trigger_price\":\"<trigger_price>\",\"limit_offset\":\"<limit_offset>\",\"trailing_amount\":\"<trailing_amount>\",\"trailing_percent\":\"<trailing_percent>\",\"remark\":\"<remark>\",\"limit_depth_level\":0,\"monitor_price\":\"<monitor_price>\",\"trigger_count\":0,\"attached_params\":{\"attached_order_type\":\"<attached_order_type>\",\"profit_taker_price\":\"<profit_taker_price>\",\"stop_loss_price\":\"<stop_loss_price>\",\"time_in_force\":\"<time_in_force>\",\"expire_time\":0,\"profit_taker_id\":0,\"stop_loss_id\":0,\"cancel_all_attached\":false,\"main_id\":0,\"quantity\":\"<quantity>\",\"market_price\":\"<market_price>\",\"activate_order_type\":\"<activate_order_type>\",\"profit_taker_submit_price\":\"<profit_taker_submit_price>\",\"stop_loss_submit_price\":\"<stop_loss_submit_price>\",\"activate_rth\":\"<activate_rth>\"}}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"PUT\", \"https://openapi.longbridge.com/v1/trade/order\", strings.NewReader(`{\"order_id\":\"<order_id>\",\"quantity\":\"<quantity>\",\"price\":\"<price>\",\"trigger_price\":\"<trigger_price>\",\"limit_offset\":\"<limit_offset>\",\"trailing_amount\":\"<trailing_amount>\",\"trailing_percent\":\"<trailing_percent>\",\"remark\":\"<remark>\",\"limit_depth_level\":0,\"monitor_price\":\"<monitor_price>\",\"trigger_count\":0,\"attached_params\":{\"attached_order_type\":\"<attached_order_type>\",\"profit_taker_price\":\"<profit_taker_price>\",\"stop_loss_price\":\"<stop_loss_price>\",\"time_in_force\":\"<time_in_force>\",\"expire_time\":0,\"profit_taker_id\":0,\"stop_loss_id\":0,\"cancel_all_attached\":false,\"main_id\":0,\"quantity\":\"<quantity>\",\"market_price\":\"<market_price>\",\"activate_order_type\":\"<activate_order_type>\",\"profit_taker_submit_price\":\"<profit_taker_submit_price>\",\"stop_loss_submit_price\":\"<stop_loss_submit_price>\",\"activate_rth\":\"<activate_rth>\"}}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/trade/order/multileg: + post: + operationId: submit_multileg + summary: Submit Multi-leg Order + x-summary-zh: 多腿期权下单 + x-summary-zh-hk: 多腿期權下單 + description: | + This API is used to submit a multi-leg option combination order (such as vertical spreads, straddles, strangles, collars, etc.). All legs are submitted together as a single strategy order. + x-description-zh: | + 该接口用于提交多腿期权组合订单(如垂直价差、跨式、宽跨式、领口等)。各腿作为一个策略订单一起提交。 + x-description-zh-hk: | + 該接口用於提交多腿期權組合訂單(如垂直價差、跨式、寬跨式、領口等)。各腿作為一個策略訂單一起提交。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 + tags: + - Trade + x-parameters: + - name: side + in: body + type: string + required: true + description: Order Side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy`, `Sell` + x-description-zh-hk: 買賣方向。**可選值:**, `Buy`, `Sell` + - name: order_type + in: body + type: string + required: true + description: 'Order Type. One of: `LO`, `ELO`, `MO`, `AO`, `ALO`, `ODD`, `LIT`, `MIT`, `TSLPAMT`, `TSLPPCT`, `SLO`.' + x-description-zh: 订单类型。可选值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + x-description-zh-hk: 訂單類型。可選值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + - name: submitted_price + in: body + type: string + required: false + description: 'Submitted price, example: `1.5`. Required for limit order types such as `LO`' + x-description-zh: 下单价格,例如:`1.5`. `LO` 等限价类型订单必填 + x-description-zh-hk: 下單價格,例如:`1.5`. `LO` 等限價類型訂單必填 + - name: submitted_quantity + in: body + type: string + required: true + description: 'Submitted quantity (number of combinations), example: `1`' + x-description-zh: 下单数量(组合数量),例如:`1` + x-description-zh-hk: 下單數量(組合數量),例如:`1` + - name: strategy + in: body + type: string + required: true + description: Multi-leg strategy. **Enum Value:**, `CoveredCall` - Covered stock, `CoveredPut` - Covered stock, `VerticalCallSpread` - Vertical spread, `VerticalPutSpread` - Vertical spread, `Collar` - Collar, `Straddle` - Straddle, `Strangle` - Strangle + x-description-zh: 多腿策略。**可选值:**, `CoveredCall` - 股票担保,`CoveredPut` - 股票担保,`VerticalCallSpread` - 垂直策略,`VerticalPutSpread` - 垂直策略,`Collar` - 领式策略,`Straddle` - 跨式策略,`Strangle` - 宽跨式策略 + x-description-zh-hk: 多腿策略。**可選值:**, `CoveredCall` - 股票擔保,`CoveredPut` - 股票擔保,`VerticalCallSpread` - 跨價期權,`VerticalPutSpread` - 跨價期權,`Collar` - 領式策略,`Straddle` - 馬鞍式策略,`Strangle` - 勒束式策略 + - name: legs + in: body + type: array + required: true + description: Legs of the combination order + x-description-zh: 组合订单的各腿 + x-description-zh-hk: 組合訂單的各腿 + - name: ∟ symbol + in: body + type: string + required: true + description: 'Option symbol, use `ticker.region` format, example: `QQQ260731C764000.US`' + x-description-zh: 期权 symbol,使用 `ticker.region` 格式,例如:`QQQ260731C764000.US` + x-description-zh-hk: 期權 symbol,使用 `ticker.region` 格式,例如:`QQQ260731C764000.US` + - name: ∟ ratio_quantity + in: body + type: string + required: true + description: 'Leg ratio quantity, example: `1`' + x-description-zh: 该腿比例数量,例如:`1` + x-description-zh-hk: 該腿比例數量,例如:`1` + - name: remark + in: body + type: string + required: false + description: Remark (Maximum 255 characters) + x-description-zh: 备注(最多 255 字符) + x-description-zh-hk: 備註(最多 255 字符) + - name: client_request_id + in: body + type: string + required: false + description: Client request ID, used for idempotency (Maximum 64 characters) + x-description-zh: 客户端请求 ID,用于幂等(最多 64 字符) + x-description-zh-hk: 客戶端請求 ID,用於冪等(最多 64 字符) + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/trade/order/multileg' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"side":"<side>","order_type":"<order_type>","submitted_price":"<submitted_price>","submitted_quantity":"<submitted_quantity>","strategy":"<strategy>","legs":[],"∟ symbol":"<∟ symbol>","∟ ratio_quantity":"<∟ ratio_quantity>","remark":"<remark>","client_request_id":"<client_request_id>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/trade/order/multileg", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"side": "<side>", "order_type": "<order_type>", "submitted_price": "<submitted_price>", "submitted_quantity": "<submitted_quantity>", "strategy": "<strategy>", "legs": [], "∟ symbol": "<∟ symbol>", "∟ ratio_quantity": "<∟ ratio_quantity>", "remark": "<remark>", "client_request_id": "<client_request_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/trade/order/multileg", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"side": "<side>", "order_type": "<order_type>", "submitted_price": "<submitted_price>", "submitted_quantity": "<submitted_quantity>", "strategy": "<strategy>", "legs": [], "∟ symbol": "<∟ symbol>", "∟ ratio_quantity": "<∟ ratio_quantity>", "remark": "<remark>", "client_request_id": "<client_request_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/trade/order/multileg", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ side: "<side>", order_type: "<order_type>", submitted_price: "<submitted_price>", submitted_quantity: "<submitted_quantity>", strategy: "<strategy>", legs: [], ∟ symbol: "<∟ symbol>", ∟ ratio_quantity: "<∟ ratio_quantity>", remark: "<remark>", client_request_id: "<client_request_id>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/order/multileg")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"side\":\"<side>\",\"order_type\":\"<order_type>\",\"submitted_price\":\"<submitted_price>\",\"submitted_quantity\":\"<submitted_quantity>\",\"strategy\":\"<strategy>\",\"legs\":[],\"∟ symbol\":\"<∟ symbol>\",\"∟ ratio_quantity\":\"<∟ ratio_quantity>\",\"remark\":\"<remark>\",\"client_request_id\":\"<client_request_id>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/trade/order/multileg") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "side": "<side>" , "order_type": "<order_type>" , "submitted_price": "<submitted_price>" , "submitted_quantity": "<submitted_quantity>" , "strategy": "<strategy>" , "legs": [] , "∟ symbol": "<∟ symbol>" , "∟ ratio_quantity": "<∟ ratio_quantity>" , "remark": "<remark>" , "client_request_id": "<client_request_id>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/order/multileg"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"side\":\"<side>\",\"order_type\":\"<order_type>\",\"submitted_price\":\"<submitted_price>\",\"submitted_quantity\":\"<submitted_quantity>\",\"strategy\":\"<strategy>\",\"legs\":[],\"∟ symbol\":\"<∟ symbol>\",\"∟ ratio_quantity\":\"<∟ ratio_quantity>\",\"remark\":\"<remark>\",\"client_request_id\":\"<client_request_id>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/trade/order/multileg\", strings.NewReader(`{\"side\":\"<side>\",\"order_type\":\"<order_type>\",\"submitted_price\":\"<submitted_price>\",\"submitted_quantity\":\"<submitted_quantity>\",\"strategy\":\"<strategy>\",\"legs\":[],\"∟ symbol\":\"<∟ symbol>\",\"∟ ratio_quantity\":\"<∟ ratio_quantity>\",\"remark\":\"<remark>\",\"client_request_id\":\"<client_request_id>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + order_id: '683615454870679600' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/orders/query: + post: + operationId: us_query_orders + summary: US Order History + x-summary-zh: 美股历史委托 + x-summary-zh-hk: 美股歷史委託 + description: | + :::warning Longbridge US Accounts + This method is only available for Longbridge US data-center accounts. + ::: + + It is **not** available to accounts in other data centers (such as HK or SG), even when those accounts can trade US symbols. It is also **not** available to paper accounts (`enable_papertrading = true`): **the Longbridge US desk (US DC) does not provide paper accounts at all**, so the entire US region — every US-specific API, not just this one — is unavailable in a paper environment. + + Calling it from an unsupported account returns an error rather than an empty result — do not treat the failure as "this account has no orders". For a paper environment, use an AP account with the generic trade APIs instead. + + Query historical and pending orders for US accounts with pagination and filtering. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于 Longbridge 美国数据中心账户。 + ::: + + 其他数据中心的账户(如香港、新加坡)**不支持**该方法,即便这些账户可以交易美股;模拟账户(`enable_papertrading = true`)同样**不支持**:**Longbridge US 柜台(US DC)未提供模拟账户功能**,因此整个 US region——所有美股专用接口,而不只是本接口——在模拟环境下均不可用。 + + 使用不受支持的账户调用时会返回错误,而不是空结果——请勿将该失败理解为“此账户没有委托”。如需模拟环境,请改用 AP 账户与通用交易接口。 + + 查询美股账户的历史委托和待成交委托,支持分页和筛选。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於 Longbridge 美國數據中心賬戶。 + ::: + + 其他數據中心的賬戶(如香港、新加坡)**不支持**該方法,即便這些賬戶可以交易美股;模擬賬戶(`enable_papertrading = true`)同樣**不支持**:**Longbridge US 櫃檯(US DC)未提供模擬賬戶功能**,因此整個 US region——所有美股專用接口,而不只是本接口——在模擬環境下均不可用。 + + 使用不受支持的賬戶調用時會返回錯誤,而不是空結果——請勿將該失敗理解為「此賬戶沒有委託」。如需模擬環境,請改用 AP 賬戶與通用交易接口。 + + 查詢美股賬戶的歷史委託和待成交委託,支持分頁和篩選。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 + tags: + - Trade + x-parameters: + - name: symbol + in: body + type: string + required: false + description: Filter by symbol, e.g. `AAPL.US` + x-description-zh: 按标的筛选,例如 `AAPL.US` + x-description-zh-hk: 按標的篩選,例如 `AAPL.US` + - name: action + in: body + type: integer + required: false + description: 'Direction filter: `0`=all, `1`=buy, `2`=sell (default: `0`)' + x-description-zh: 方向筛选:`0`=全部,`1`=买入,`2`=卖出(默认:`0`) + x-description-zh-hk: 方向篩選:`0`=全部,`1`=買入,`2`=賣出(默認:`0`) + - name: start_at + in: body + type: integer + required: false + description: Start time (Unix seconds); `0` = last 90 days + x-description-zh: 开始时间(Unix 秒);`0` = 最近 90 天 + x-description-zh-hk: 開始時間(Unix 秒);`0` = 最近 90 天 + - name: end_at + in: body + type: integer + required: false + description: End time (Unix seconds); `0` = now + x-description-zh: 结束时间(Unix 秒);`0` = 当前时间 + x-description-zh-hk: 結束時間(Unix 秒);`0` = 當前時間 + - name: query_type + in: body + type: integer + required: false + description: '`0`=all (incl. rejected), `1`=pending, `2`=filled only (default: `0`)' + x-description-zh: 0=全部,1=待成交,2=已成交(默认:0) + x-description-zh-hk: 0=全部,1=待成交,2=已成交(默認:0) + - name: page + in: body + type: integer + required: false + description: 'Page number, 1-based (default: `1`)' + x-description-zh: 页码,从 1 开始(默认:1) + x-description-zh-hk: 頁碼,從 1 開始(默認:1) + - name: limit + in: body + type: integer + required: false + description: 'Page size (default: `20`)' + x-description-zh: 每页数量(默认:20) + x-description-zh-hk: 每頁數量(默認:20) + x-codeSamples: + - lang: Shell + label: CLI + source: | + # List US orders + longbridge order + # Filter pending orders + longbridge order --status pending + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/us/orders/query' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbol":"<symbol>","action":0,"start_at":0,"end_at":0,"query_type":0,"page":0,"limit":0}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/us/orders/query", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "action": 0, "start_at": 0, "end_at": 0, "query_type": 0, "page": 0, "limit": 0}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/us/orders/query", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "action": 0, "start_at": 0, "end_at": 0, "query_type": 0, "page": 0, "limit": 0}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/us/orders/query", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbol: "<symbol>", action: 0, start_at: 0, end_at: 0, query_type: 0, page: 0, limit: 0 }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/orders/query")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbol\":\"<symbol>\",\"action\":0,\"start_at\":0,\"end_at\":0,\"query_type\":0,\"page\":0,\"limit\":0}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/us/orders/query") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbol": "<symbol>" , "action": 0 , "start_at": 0 , "end_at": 0 , "query_type": 0 , "page": 0 , "limit": 0 })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/orders/query"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbol\":\"<symbol>\",\"action\":0,\"start_at\":0,\"end_at\":0,\"query_type\":0,\"page\":0,\"limit\":0}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/us/orders/query\", strings.NewReader(`{\"symbol\":\"<symbol>\",\"action\":0,\"start_at\":0,\"end_at\":0,\"query_type\":0,\"page\":0,\"limit\":0}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: orders + type: USOrder[] + required: true + description: List of orders matching the filter + x-description-zh: 符合筛选条件的委托列表 + x-description-zh-hk: 符合篩選條件的委託列表 + - name: total_count + type: int + required: true + description: Total number of matching orders + x-description-zh: 满足条件的委托总数 + x-description-zh-hk: 滿足條件的委託總數 + responses: + '200': + description: Successful response + content: + application/json: + example: + orders: + - id: '701276261045858304' + symbol: AAPL.US + action: Buy + order_type: LO + status: Filled + price: '185.00' + quantity: '10' + submitted_at: 1751866334 + updated_at: 1751866400 + total_count: 1 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/gridtrading/submit: + post: + operationId: grid_submit + summary: Submit Grid Order + x-summary-zh: 提交网格订单 + x-summary-zh-hk: 提交網格訂單 + description: | + Submit a grid strategy order. The grid places buy orders as the price falls and sell orders as it rises, within the `[lower_limit_price, upper_limit_price]` band anchored to `submitted_base_price`. + + Before using grid trading you must record the strategy risk-disclosure consent once — see Submit Strategy Questionnaire. + x-description-zh: | + 提交网格策略订单。网格以 `submitted_base_price` 为基准价,在 `[lower_limit_price, upper_limit_price]` 区间内,价格下跌时挂买单、价格上涨时挂卖单。 + + 使用网格交易前,你需要先记录一次策略风险揭示的同意确认,详见提交策略问卷。 + x-description-zh-hk: | + 提交網格策略訂單。網格以 `submitted_base_price` 為基準價,在 `[lower_limit_price, upper_limit_price]` 區間內,價格下跌時掛買單、價格上漲時掛賣單。 + + 使用網格交易前,你需要先記錄一次策略風險揭示的同意確認,詳見提交策略問卷。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 + tags: + - Trade + x-parameters: + - name: symbol + in: body + type: string + required: true + description: 'Security symbol, `ticker.region` format, example: `700.HK`' + x-description-zh: 标的代码,`ticker.region` 格式,例如:`700.HK` + x-description-zh-hk: 標的代碼,`ticker.region` 格式,例如:`700.HK` + - name: settlement_currency + in: body + type: string + required: true + description: 'Settlement currency, example: `HKD`' + x-description-zh: 结算货币,例如:`HKD` + x-description-zh-hk: 結算貨幣,例如:`HKD` + - name: grid_trading_rule + in: body + type: object + required: true + description: The grid rule. Fields below. + x-description-zh: 网格规则,字段见下方。 + x-description-zh-hk: 網格規則,欄位見下方。 + - name: grid_trading_rule.submitted_base_price + in: body + type: string + required: true + description: Base price the grid is anchored to + x-description-zh: 网格锚定的基准价 + x-description-zh-hk: 網格錨定的基準價 + - name: grid_trading_rule.upper_limit_price + in: body + type: string + required: true + description: Upper price bound + x-description-zh: 价格上边界 + x-description-zh-hk: 價格上邊界 + - name: grid_trading_rule.lower_limit_price + in: body + type: string + required: true + description: Lower price bound + x-description-zh: 价格下边界 + x-description-zh-hk: 價格下邊界 + - name: grid_trading_rule.trigger_price_type + in: body + type: integer + required: true + description: How trigger thresholds are interpreted. **Enum Value:**, `1` - spread (absolute), `2` - percent + x-description-zh: 触发阈值的解释方式。**可选值:**, `1` - spread(绝对价差), `2` - percent(百分比) + x-description-zh-hk: 觸發閾值的解釋方式。**可選值:**, `1` - spread(絕對價差), `2` - percent(百分比) + - name: grid_trading_rule.trigger_spread_up + in: body + type: string + required: false + description: Upward trigger spread, required when `trigger_price_type` is `1` + x-description-zh: 上涨触发价差,`trigger_price_type` 为 `1` 时必填 + x-description-zh-hk: 上漲觸發價差,`trigger_price_type` 為 `1` 時必填 + - name: grid_trading_rule.trigger_spread_down + in: body + type: string + required: false + description: Downward trigger spread, required when `trigger_price_type` is `1` + x-description-zh: 下跌触发价差,`trigger_price_type` 为 `1` 时必填 + x-description-zh-hk: 下跌觸發價差,`trigger_price_type` 為 `1` 時必填 + - name: grid_trading_rule.trigger_percent_up + in: body + type: string + required: false + description: Upward trigger percent, required when `trigger_price_type` is `2` + x-description-zh: 上涨触发百分比,`trigger_price_type` 为 `2` 时必填 + x-description-zh-hk: 上漲觸發百分比,`trigger_price_type` 為 `2` 時必填 + - name: grid_trading_rule.trigger_percent_down + in: body + type: string + required: false + description: Downward trigger percent, required when `trigger_price_type` is `2` + x-description-zh: 下跌触发百分比,`trigger_price_type` 为 `2` 时必填 + x-description-zh-hk: 下跌觸發百分比,`trigger_price_type` 為 `2` 時必填 + - name: grid_trading_rule.trigger_buy_quantity + in: body + type: string + required: true + description: 'Buy quantity per trigger' + x-description-zh: '每次触发买入数量' + x-description-zh-hk: '每次觸發買入數量' + - name: grid_trading_rule.trigger_sell_quantity + in: body + type: string + required: true + description: 'Sell quantity per trigger' + x-description-zh: '每次触发卖出数量' + x-description-zh-hk: '每次觸發賣出數量' + - name: grid_trading_rule.time_in_force + in: body + type: integer + required: true + description: Time in force. **Enum Value:**, `0` - Day, `1` - GTC (Good-Til-Canceled), `6` - GTD (Good-Til-Date) + x-description-zh: 订单有效期。**可选值:**, `0` - Day(当日有效), `1` - GTC(撤单前有效), `6` - GTD(到期前有效) + x-description-zh-hk: 訂單有效期。**可選值:**, `0` - Day(當日有效), `1` - GTC(撤單前有效), `6` - GTD(到期前有效) + - name: grid_trading_rule.expire_time + in: body + type: integer + required: false + description: Expiry time (Unix timestamp, in seconds), required when `time_in_force` is `6` (GTD) + x-description-zh: 到期时间(Unix 时间戳,单位秒),`time_in_force` 为 `6`(GTD)时必填 + x-description-zh-hk: 到期時間(Unix 時間戳,單位秒),`time_in_force` 為 `6`(GTD)時必填 + - name: grid_trading_rule.upper_limit_event + in: body + type: integer + required: false + description: Action when the upper bound is reached. **Enum Value:**, `1` - ignore (keep running), `2` - close position at last price + x-description-zh: 达到上边界时的动作。**可选值:**, `1` - ignore(继续运行), `2` - 按最新价平仓 + x-description-zh-hk: 達到上邊界時的動作。**可選值:**, `1` - ignore(繼續運行), `2` - 按最新價平倉 + - name: grid_trading_rule.lower_limit_event + in: body + type: integer + required: false + description: Action when the lower bound is reached. **Enum Value:**, `1` - ignore (keep running), `2` - close position at last price + x-description-zh: 达到下边界时的动作。**可选值:**, `1` - ignore(继续运行), `2` - 按最新价平仓 + x-description-zh-hk: 達到下邊界時的動作。**可選值:**, `1` - ignore(繼續運行), `2` - 按最新價平倉 + - name: grid_trading_rule.trigger_sell_depth + in: body + type: integer + required: false + description: Sell-side order-book depth (-5 ~ 5). `0` = use `grid_order_type_up` instead of a depth level + x-description-zh: 卖盘盘口档位(-5 ~ 5)。`0` 表示改用 `grid_order_type_up` 而非档位 + x-description-zh-hk: 賣盤盤口檔位(-5 ~ 5)。`0` 表示改用 `grid_order_type_up` 而非檔位 + - name: grid_trading_rule.trigger_buy_depth + in: body + type: integer + required: false + description: Buy-side order-book depth (-5 ~ 5). `0` = use `grid_order_type_down` instead of a depth level + x-description-zh: 买盘盘口档位(-5 ~ 5)。`0` 表示改用 `grid_order_type_down` 而非档位 + x-description-zh-hk: 買盤盤口檔位(-5 ~ 5)。`0` 表示改用 `grid_order_type_down` 而非檔位 + - name: grid_trading_rule.grid_order_type_up + in: body + type: string + required: false + description: Sell-side order type when `trigger_sell_depth` is `0`. **Enum Value:**, `GMO` / `GLO` / `GTG` + x-description-zh: '`trigger_sell_depth` 为 `0` 时的卖盘订单类型。**可选值:**, `GMO` / `GLO` / `GTG`' + x-description-zh-hk: '`trigger_sell_depth` 為 `0` 時的賣盤訂單類型。**可選值:**, `GMO` / `GLO` / `GTG`' + - name: grid_trading_rule.grid_order_type_down + in: body + type: string + required: false + description: Buy-side order type when `trigger_buy_depth` is `0`. **Enum Value:**, `GMO` / `GLO` / `GTG` + x-description-zh: '`trigger_buy_depth` 为 `0` 时的买盘订单类型。**可选值:**, `GMO` / `GLO` / `GTG`' + x-description-zh-hk: '`trigger_buy_depth` 為 `0` 時的買盤訂單類型。**可選值:**, `GMO` / `GLO` / `GTG`' + - name: grid_trading_rule.multiple_trigger + in: body + type: boolean + required: false + description: Whether a single grid level may trigger multiple times + x-description-zh: 单个网格档位是否允许多次触发 + x-description-zh-hk: 單個網格檔位是否允許多次觸發 + - name: grid_trading_rule.support_shortsell + in: body + type: boolean + required: false + description: Whether short selling is allowed + x-description-zh: 是否允许卖空 + x-description-zh-hk: 是否允許賣空 + - name: grid_trading_rule.rth + in: body + type: integer + required: false + description: Regular-trading-hours flag (`0` / `1` / `2`) + x-description-zh: 常规交易时段标志(`0` / `1` / `2`) + x-description-zh-hk: 常規交易時段標誌(`0` / `1` / `2`) + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Submit a percent-triggered grid on 700.HK + longbridge grid submit 700.HK --currency HKD --base-price 300 --upper-price 360 --lower-price 240 --trigger-type percent --trigger-up 2 --trigger-down 2 --quantity 100 --upper-quantity 200 --lower-quantity 100 --order-type GMO --tif gtc + # Validate the rule without submitting + longbridge grid submit 700.HK --currency HKD --base-price 300 --upper-price 360 --lower-price 240 --trigger-type percent --trigger-up 2 --trigger-down 2 --quantity 100 --upper-quantity 200 --lower-quantity 100 --order-type GMO --tif gtc --dry-run + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/gridtrading/submit' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbol":"<symbol>","settlement_currency":"<settlement_currency>","grid_trading_rule":{},"submitted_base_price":"<submitted_base_price>","upper_limit_price":"<upper_limit_price>","lower_limit_price":"<lower_limit_price>","trigger_price_type":0,"trigger_spread_up":"<trigger_spread_up>","trigger_spread_down":"<trigger_spread_down>","trigger_percent_up":"<trigger_percent_up>","trigger_percent_down":"<trigger_percent_down>","trigger_quantity":"<trigger_quantity>","upper_limit_quantity":"<upper_limit_quantity>","lower_limit_quantity":"<lower_limit_quantity>","time_in_force":0,"expire_time":0,"upper_limit_event":0,"lower_limit_event":0,"trigger_sell_depth":0,"trigger_buy_depth":0,"grid_order_type_up":"<grid_order_type_up>","grid_order_type_down":"<grid_order_type_down>","multiple_trigger":false,"support_shortsell":false,"rth":0}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/gridtrading/submit", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "settlement_currency": "<settlement_currency>", "grid_trading_rule": {}, "submitted_base_price": "<submitted_base_price>", "upper_limit_price": "<upper_limit_price>", "lower_limit_price": "<lower_limit_price>", "trigger_price_type": 0, "trigger_spread_up": "<trigger_spread_up>", "trigger_spread_down": "<trigger_spread_down>", "trigger_percent_up": "<trigger_percent_up>", "trigger_percent_down": "<trigger_percent_down>", "trigger_quantity": "<trigger_quantity>", "upper_limit_quantity": "<upper_limit_quantity>", "lower_limit_quantity": "<lower_limit_quantity>", "time_in_force": 0, "expire_time": 0, "upper_limit_event": 0, "lower_limit_event": 0, "trigger_sell_depth": 0, "trigger_buy_depth": 0, "grid_order_type_up": "<grid_order_type_up>", "grid_order_type_down": "<grid_order_type_down>", "multiple_trigger": False, "support_shortsell": False, "rth": 0}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/gridtrading/submit", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "settlement_currency": "<settlement_currency>", "grid_trading_rule": {}, "submitted_base_price": "<submitted_base_price>", "upper_limit_price": "<upper_limit_price>", "lower_limit_price": "<lower_limit_price>", "trigger_price_type": 0, "trigger_spread_up": "<trigger_spread_up>", "trigger_spread_down": "<trigger_spread_down>", "trigger_percent_up": "<trigger_percent_up>", "trigger_percent_down": "<trigger_percent_down>", "trigger_quantity": "<trigger_quantity>", "upper_limit_quantity": "<upper_limit_quantity>", "lower_limit_quantity": "<lower_limit_quantity>", "time_in_force": 0, "expire_time": 0, "upper_limit_event": 0, "lower_limit_event": 0, "trigger_sell_depth": 0, "trigger_buy_depth": 0, "grid_order_type_up": "<grid_order_type_up>", "grid_order_type_down": "<grid_order_type_down>", "multiple_trigger": False, "support_shortsell": False, "rth": 0}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/gridtrading/submit", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbol: "<symbol>", settlement_currency: "<settlement_currency>", grid_trading_rule: {}, submitted_base_price: "<submitted_base_price>", upper_limit_price: "<upper_limit_price>", lower_limit_price: "<lower_limit_price>", trigger_price_type: 0, trigger_spread_up: "<trigger_spread_up>", trigger_spread_down: "<trigger_spread_down>", trigger_percent_up: "<trigger_percent_up>", trigger_percent_down: "<trigger_percent_down>", trigger_quantity: "<trigger_quantity>", upper_limit_quantity: "<upper_limit_quantity>", lower_limit_quantity: "<lower_limit_quantity>", time_in_force: 0, expire_time: 0, upper_limit_event: 0, lower_limit_event: 0, trigger_sell_depth: 0, trigger_buy_depth: 0, grid_order_type_up: "<grid_order_type_up>", grid_order_type_down: "<grid_order_type_down>", multiple_trigger: false, support_shortsell: false, rth: 0 }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/submit")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbol\":\"<symbol>\",\"settlement_currency\":\"<settlement_currency>\",\"grid_trading_rule\":{},\"submitted_base_price\":\"<submitted_base_price>\",\"upper_limit_price\":\"<upper_limit_price>\",\"lower_limit_price\":\"<lower_limit_price>\",\"trigger_price_type\":0,\"trigger_spread_up\":\"<trigger_spread_up>\",\"trigger_spread_down\":\"<trigger_spread_down>\",\"trigger_percent_up\":\"<trigger_percent_up>\",\"trigger_percent_down\":\"<trigger_percent_down>\",\"trigger_quantity\":\"<trigger_quantity>\",\"upper_limit_quantity\":\"<upper_limit_quantity>\",\"lower_limit_quantity\":\"<lower_limit_quantity>\",\"time_in_force\":0,\"expire_time\":0,\"upper_limit_event\":0,\"lower_limit_event\":0,\"trigger_sell_depth\":0,\"trigger_buy_depth\":0,\"grid_order_type_up\":\"<grid_order_type_up>\",\"grid_order_type_down\":\"<grid_order_type_down>\",\"multiple_trigger\":false,\"support_shortsell\":false,\"rth\":0}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/gridtrading/submit") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbol": "<symbol>" , "settlement_currency": "<settlement_currency>" , "grid_trading_rule": {} , "submitted_base_price": "<submitted_base_price>" , "upper_limit_price": "<upper_limit_price>" , "lower_limit_price": "<lower_limit_price>" , "trigger_price_type": 0 , "trigger_spread_up": "<trigger_spread_up>" , "trigger_spread_down": "<trigger_spread_down>" , "trigger_percent_up": "<trigger_percent_up>" , "trigger_percent_down": "<trigger_percent_down>" , "trigger_quantity": "<trigger_quantity>" , "upper_limit_quantity": "<upper_limit_quantity>" , "lower_limit_quantity": "<lower_limit_quantity>" , "time_in_force": 0 , "expire_time": 0 , "upper_limit_event": 0 , "lower_limit_event": 0 , "trigger_sell_depth": 0 , "trigger_buy_depth": 0 , "grid_order_type_up": "<grid_order_type_up>" , "grid_order_type_down": "<grid_order_type_down>" , "multiple_trigger": false , "support_shortsell": false , "rth": 0 })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/submit"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbol\":\"<symbol>\",\"settlement_currency\":\"<settlement_currency>\",\"grid_trading_rule\":{},\"submitted_base_price\":\"<submitted_base_price>\",\"upper_limit_price\":\"<upper_limit_price>\",\"lower_limit_price\":\"<lower_limit_price>\",\"trigger_price_type\":0,\"trigger_spread_up\":\"<trigger_spread_up>\",\"trigger_spread_down\":\"<trigger_spread_down>\",\"trigger_percent_up\":\"<trigger_percent_up>\",\"trigger_percent_down\":\"<trigger_percent_down>\",\"trigger_quantity\":\"<trigger_quantity>\",\"upper_limit_quantity\":\"<upper_limit_quantity>\",\"lower_limit_quantity\":\"<lower_limit_quantity>\",\"time_in_force\":0,\"expire_time\":0,\"upper_limit_event\":0,\"lower_limit_event\":0,\"trigger_sell_depth\":0,\"trigger_buy_depth\":0,\"grid_order_type_up\":\"<grid_order_type_up>\",\"grid_order_type_down\":\"<grid_order_type_down>\",\"multiple_trigger\":false,\"support_shortsell\":false,\"rth\":0}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/gridtrading/submit\", strings.NewReader(`{\"symbol\":\"<symbol>\",\"settlement_currency\":\"<settlement_currency>\",\"grid_trading_rule\":{},\"submitted_base_price\":\"<submitted_base_price>\",\"upper_limit_price\":\"<upper_limit_price>\",\"lower_limit_price\":\"<lower_limit_price>\",\"trigger_price_type\":0,\"trigger_spread_up\":\"<trigger_spread_up>\",\"trigger_spread_down\":\"<trigger_spread_down>\",\"trigger_percent_up\":\"<trigger_percent_up>\",\"trigger_percent_down\":\"<trigger_percent_down>\",\"trigger_quantity\":\"<trigger_quantity>\",\"upper_limit_quantity\":\"<upper_limit_quantity>\",\"lower_limit_quantity\":\"<lower_limit_quantity>\",\"time_in_force\":0,\"expire_time\":0,\"upper_limit_event\":0,\"lower_limit_event\":0,\"trigger_sell_depth\":0,\"trigger_buy_depth\":0,\"grid_order_type_up\":\"<grid_order_type_up>\",\"grid_order_type_down\":\"<grid_order_type_down>\",\"multiple_trigger\":false,\"support_shortsell\":false,\"rth\":0}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + order_id: '764609681686573056' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/gridtrading/replace: + post: + operationId: grid_replace + summary: Modify Grid Order + x-summary-zh: 修改网格订单 + x-summary-zh-hk: 修改網格訂單 + description: | + Modify an existing grid order's rule. The whole `grid_trading_rule` is replaced with the one you submit, so pass the complete rule — not just the fields you changed. + x-description-zh: | + 修改已存在的网格订单规则。提交的 `grid_trading_rule` 会整体替换原有规则,因此需要传入完整的规则,而不是仅传改动的字段。 + x-description-zh-hk: | + 修改已存在的網格訂單規則。提交的 `grid_trading_rule` 會整體替換原有規則,因此需要傳入完整的規則,而不是僅傳改動的欄位。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 + tags: + - Trade + x-parameters: + - name: order_id + in: body + type: string + required: true + description: 'Grid order ID, example: `764609681686573056`' + x-description-zh: 网格订单 ID,例如:`764609681686573056` + x-description-zh-hk: 網格訂單 ID,例如:`764609681686573056` + - name: grid_trading_rule + in: body + type: object + required: true + description: The full grid rule to apply. Fields below. + x-description-zh: 要应用的完整网格规则,字段见下。 + x-description-zh-hk: 要應用的完整網格規則,欄位見下。 + - name: grid_trading_rule.submitted_base_price + in: body + type: string + required: true + description: Base price the grid is anchored to + x-description-zh: 网格锚定的基准价 + x-description-zh-hk: 網格錨定的基準價 + - name: grid_trading_rule.upper_limit_price + in: body + type: string + required: true + description: Upper price bound + x-description-zh: 价格上边界 + x-description-zh-hk: 價格上邊界 + - name: grid_trading_rule.lower_limit_price + in: body + type: string + required: true + description: Lower price bound + x-description-zh: 价格下边界 + x-description-zh-hk: 價格下邊界 + - name: grid_trading_rule.trigger_price_type + in: body + type: integer + required: true + description: How trigger thresholds are interpreted. **Enum Value:**, `1` - spread (absolute), `2` - percent + x-description-zh: 触发阈值的计算方式。**可选值:**, `1` - 价差(绝对值), `2` - 百分比 + x-description-zh-hk: 觸發閾值的計算方式。**可選值:**, `1` - 價差(絕對值), `2` - 百分比 + - name: grid_trading_rule.trigger_spread_up + in: body + type: string + required: false + description: Upward trigger spread, required when `trigger_price_type` is `1` + x-description-zh: 向上触发价差,`trigger_price_type` 为 `1` 时必填 + x-description-zh-hk: 向上觸發價差,`trigger_price_type` 為 `1` 時必填 + - name: grid_trading_rule.trigger_spread_down + in: body + type: string + required: false + description: Downward trigger spread, required when `trigger_price_type` is `1` + x-description-zh: 向下触发价差,`trigger_price_type` 为 `1` 时必填 + x-description-zh-hk: 向下觸發價差,`trigger_price_type` 為 `1` 時必填 + - name: grid_trading_rule.trigger_percent_up + in: body + type: string + required: false + description: Upward trigger percent, required when `trigger_price_type` is `2` + x-description-zh: 向上触发百分比,`trigger_price_type` 为 `2` 时必填 + x-description-zh-hk: 向上觸發百分比,`trigger_price_type` 為 `2` 時必填 + - name: grid_trading_rule.trigger_percent_down + in: body + type: string + required: false + description: Downward trigger percent, required when `trigger_price_type` is `2` + x-description-zh: 向下触发百分比,`trigger_price_type` 为 `2` 时必填 + x-description-zh-hk: 向下觸發百分比,`trigger_price_type` 為 `2` 時必填 + - name: grid_trading_rule.trigger_buy_quantity + in: body + type: string + required: true + description: 'Buy quantity per trigger' + x-description-zh: '每次触发买入数量' + x-description-zh-hk: '每次觸發買入數量' + - name: grid_trading_rule.trigger_sell_quantity + in: body + type: string + required: true + description: 'Sell quantity per trigger' + x-description-zh: '每次触发卖出数量' + x-description-zh-hk: '每次觸發賣出數量' + - name: grid_trading_rule.time_in_force + in: body + type: integer + required: true + description: Time in force. **Enum Value:**, `0` - Day, `1` - GTC (Good-Til-Canceled), `6` - GTD (Good-Til-Date) + x-description-zh: 订单有效期类型。**可选值:**, `0` - 当日有效,`1` - GTC(撤单前有效), `6` - GTD(到期前有效) + x-description-zh-hk: 訂單有效期類型。**可選值:**, `0` - 當日有效,`1` - GTC(撤單前有效), `6` - GTD(到期前有效) + - name: grid_trading_rule.expire_time + in: body + type: integer + required: false + description: Expiry time (Unix timestamp, in seconds), required when `time_in_force` is `6` (GTD) + x-description-zh: 到期时间(Unix 时间戳,单位秒),`time_in_force` 为 `6`(GTD)时必填 + x-description-zh-hk: 到期時間(Unix 時間戳,單位秒),`time_in_force` 為 `6`(GTD)時必填 + - name: grid_trading_rule.upper_limit_event + in: body + type: integer + required: false + description: Action when the upper bound is reached. **Enum Value:**, `1` - ignore (keep running), `2` - close position at last price + x-description-zh: 到达上边界时的动作。**可选值:**, `1` - 忽略(保持运行), `2` - 以最新价平仓 + x-description-zh-hk: 到達上邊界時的動作。**可選值:**, `1` - 忽略(保持運行), `2` - 以最新價平倉 + - name: grid_trading_rule.lower_limit_event + in: body + type: integer + required: false + description: Action when the lower bound is reached. **Enum Value:**, `1` - ignore (keep running), `2` - close position at last price + x-description-zh: 到达下边界时的动作。**可选值:**, `1` - 忽略(保持运行), `2` - 以最新价平仓 + x-description-zh-hk: 到達下邊界時的動作。**可選值:**, `1` - 忽略(保持運行), `2` - 以最新價平倉 + - name: grid_trading_rule.trigger_sell_depth + in: body + type: integer + required: false + description: Sell-side order-book depth (-5 ~ 5). `0` = use `grid_order_type_up` instead of a depth level + x-description-zh: 卖方向盘口档位(-5 ~ 5)。为 `0` 时使用 `grid_order_type_up` 而非档位 + x-description-zh-hk: 賣方向盤口檔位(-5 ~ 5)。為 `0` 時使用 `grid_order_type_up` 而非檔位 + - name: grid_trading_rule.trigger_buy_depth + in: body + type: integer + required: false + description: Buy-side order-book depth (-5 ~ 5). `0` = use `grid_order_type_down` instead of a depth level + x-description-zh: 买方向盘口档位(-5 ~ 5)。为 `0` 时使用 `grid_order_type_down` 而非档位 + x-description-zh-hk: 買方向盤口檔位(-5 ~ 5)。為 `0` 時使用 `grid_order_type_down` 而非檔位 + - name: grid_trading_rule.grid_order_type_up + in: body + type: string + required: false + description: Sell-side order type when `trigger_sell_depth` is `0`. **Enum Value:**, `GMO` / `GLO` / `GTG` + x-description-zh: '`trigger_sell_depth` 为 `0` 时的卖方向订单类型。**可选值:**, `GMO` / `GLO` / `GTG`' + x-description-zh-hk: '`trigger_sell_depth` 為 `0` 時的賣方向訂單類型。**可選值:**, `GMO` / `GLO` / `GTG`' + - name: grid_trading_rule.grid_order_type_down + in: body + type: string + required: false + description: Buy-side order type when `trigger_buy_depth` is `0`. **Enum Value:**, `GMO` / `GLO` / `GTG` + x-description-zh: '`trigger_buy_depth` 为 `0` 时的买方向订单类型。**可选值:**, `GMO` / `GLO` / `GTG`' + x-description-zh-hk: '`trigger_buy_depth` 為 `0` 時的買方向訂單類型。**可選值:**, `GMO` / `GLO` / `GTG`' + - name: grid_trading_rule.multiple_trigger + in: body + type: boolean + required: false + description: Whether a single grid level may trigger multiple times + x-description-zh: 单个网格档位是否可多次触发 + x-description-zh-hk: 單個網格檔位是否可多次觸發 + - name: grid_trading_rule.support_shortsell + in: body + type: boolean + required: false + description: Whether short selling is allowed + x-description-zh: 是否允许卖空 + x-description-zh-hk: 是否允許賣空 + - name: grid_trading_rule.rth + in: body + type: integer + required: false + description: Regular-trading-hours flag (`0` / `1` / `2`) + x-description-zh: 常规交易时段标志(`0` / `1` / `2`) + x-description-zh-hk: 常規交易時段標誌(`0` / `1` / `2`) + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Modify a grid order's base/bound prices and quantities + longbridge grid replace 764609681686573056 --base-price 305 --upper-price 360 --lower-price 240 --trigger-type percent --trigger-up 2 --trigger-down 2 --quantity 100 --upper-quantity 200 --lower-quantity 100 --order-type GMO --tif gtc + # Validate the new rule without applying it + longbridge grid replace 764609681686573056 --base-price 305 --upper-price 360 --lower-price 240 --trigger-type percent --trigger-up 2 --trigger-down 2 --quantity 100 --upper-quantity 200 --lower-quantity 100 --order-type GMO --tif gtc --dry-run + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/gridtrading/replace' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"order_id":"<order_id>","grid_trading_rule":{},"submitted_base_price":"<submitted_base_price>","upper_limit_price":"<upper_limit_price>","lower_limit_price":"<lower_limit_price>","trigger_price_type":0,"trigger_spread_up":"<trigger_spread_up>","trigger_spread_down":"<trigger_spread_down>","trigger_percent_up":"<trigger_percent_up>","trigger_percent_down":"<trigger_percent_down>","trigger_quantity":"<trigger_quantity>","upper_limit_quantity":"<upper_limit_quantity>","lower_limit_quantity":"<lower_limit_quantity>","time_in_force":0,"expire_time":0,"upper_limit_event":0,"lower_limit_event":0,"trigger_sell_depth":0,"trigger_buy_depth":0,"grid_order_type_up":"<grid_order_type_up>","grid_order_type_down":"<grid_order_type_down>","multiple_trigger":false,"support_shortsell":false,"rth":0}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/gridtrading/replace", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>", "grid_trading_rule": {}, "submitted_base_price": "<submitted_base_price>", "upper_limit_price": "<upper_limit_price>", "lower_limit_price": "<lower_limit_price>", "trigger_price_type": 0, "trigger_spread_up": "<trigger_spread_up>", "trigger_spread_down": "<trigger_spread_down>", "trigger_percent_up": "<trigger_percent_up>", "trigger_percent_down": "<trigger_percent_down>", "trigger_quantity": "<trigger_quantity>", "upper_limit_quantity": "<upper_limit_quantity>", "lower_limit_quantity": "<lower_limit_quantity>", "time_in_force": 0, "expire_time": 0, "upper_limit_event": 0, "lower_limit_event": 0, "trigger_sell_depth": 0, "trigger_buy_depth": 0, "grid_order_type_up": "<grid_order_type_up>", "grid_order_type_down": "<grid_order_type_down>", "multiple_trigger": False, "support_shortsell": False, "rth": 0}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/gridtrading/replace", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>", "grid_trading_rule": {}, "submitted_base_price": "<submitted_base_price>", "upper_limit_price": "<upper_limit_price>", "lower_limit_price": "<lower_limit_price>", "trigger_price_type": 0, "trigger_spread_up": "<trigger_spread_up>", "trigger_spread_down": "<trigger_spread_down>", "trigger_percent_up": "<trigger_percent_up>", "trigger_percent_down": "<trigger_percent_down>", "trigger_quantity": "<trigger_quantity>", "upper_limit_quantity": "<upper_limit_quantity>", "lower_limit_quantity": "<lower_limit_quantity>", "time_in_force": 0, "expire_time": 0, "upper_limit_event": 0, "lower_limit_event": 0, "trigger_sell_depth": 0, "trigger_buy_depth": 0, "grid_order_type_up": "<grid_order_type_up>", "grid_order_type_down": "<grid_order_type_down>", "multiple_trigger": False, "support_shortsell": False, "rth": 0}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/gridtrading/replace", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ order_id: "<order_id>", grid_trading_rule: {}, submitted_base_price: "<submitted_base_price>", upper_limit_price: "<upper_limit_price>", lower_limit_price: "<lower_limit_price>", trigger_price_type: 0, trigger_spread_up: "<trigger_spread_up>", trigger_spread_down: "<trigger_spread_down>", trigger_percent_up: "<trigger_percent_up>", trigger_percent_down: "<trigger_percent_down>", trigger_quantity: "<trigger_quantity>", upper_limit_quantity: "<upper_limit_quantity>", lower_limit_quantity: "<lower_limit_quantity>", time_in_force: 0, expire_time: 0, upper_limit_event: 0, lower_limit_event: 0, trigger_sell_depth: 0, trigger_buy_depth: 0, grid_order_type_up: "<grid_order_type_up>", grid_order_type_down: "<grid_order_type_down>", multiple_trigger: false, support_shortsell: false, rth: 0 }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/replace")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"order_id\":\"<order_id>\",\"grid_trading_rule\":{},\"submitted_base_price\":\"<submitted_base_price>\",\"upper_limit_price\":\"<upper_limit_price>\",\"lower_limit_price\":\"<lower_limit_price>\",\"trigger_price_type\":0,\"trigger_spread_up\":\"<trigger_spread_up>\",\"trigger_spread_down\":\"<trigger_spread_down>\",\"trigger_percent_up\":\"<trigger_percent_up>\",\"trigger_percent_down\":\"<trigger_percent_down>\",\"trigger_quantity\":\"<trigger_quantity>\",\"upper_limit_quantity\":\"<upper_limit_quantity>\",\"lower_limit_quantity\":\"<lower_limit_quantity>\",\"time_in_force\":0,\"expire_time\":0,\"upper_limit_event\":0,\"lower_limit_event\":0,\"trigger_sell_depth\":0,\"trigger_buy_depth\":0,\"grid_order_type_up\":\"<grid_order_type_up>\",\"grid_order_type_down\":\"<grid_order_type_down>\",\"multiple_trigger\":false,\"support_shortsell\":false,\"rth\":0}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/gridtrading/replace") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "order_id": "<order_id>" , "grid_trading_rule": {} , "submitted_base_price": "<submitted_base_price>" , "upper_limit_price": "<upper_limit_price>" , "lower_limit_price": "<lower_limit_price>" , "trigger_price_type": 0 , "trigger_spread_up": "<trigger_spread_up>" , "trigger_spread_down": "<trigger_spread_down>" , "trigger_percent_up": "<trigger_percent_up>" , "trigger_percent_down": "<trigger_percent_down>" , "trigger_quantity": "<trigger_quantity>" , "upper_limit_quantity": "<upper_limit_quantity>" , "lower_limit_quantity": "<lower_limit_quantity>" , "time_in_force": 0 , "expire_time": 0 , "upper_limit_event": 0 , "lower_limit_event": 0 , "trigger_sell_depth": 0 , "trigger_buy_depth": 0 , "grid_order_type_up": "<grid_order_type_up>" , "grid_order_type_down": "<grid_order_type_down>" , "multiple_trigger": false , "support_shortsell": false , "rth": 0 })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/replace"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"order_id\":\"<order_id>\",\"grid_trading_rule\":{},\"submitted_base_price\":\"<submitted_base_price>\",\"upper_limit_price\":\"<upper_limit_price>\",\"lower_limit_price\":\"<lower_limit_price>\",\"trigger_price_type\":0,\"trigger_spread_up\":\"<trigger_spread_up>\",\"trigger_spread_down\":\"<trigger_spread_down>\",\"trigger_percent_up\":\"<trigger_percent_up>\",\"trigger_percent_down\":\"<trigger_percent_down>\",\"trigger_quantity\":\"<trigger_quantity>\",\"upper_limit_quantity\":\"<upper_limit_quantity>\",\"lower_limit_quantity\":\"<lower_limit_quantity>\",\"time_in_force\":0,\"expire_time\":0,\"upper_limit_event\":0,\"lower_limit_event\":0,\"trigger_sell_depth\":0,\"trigger_buy_depth\":0,\"grid_order_type_up\":\"<grid_order_type_up>\",\"grid_order_type_down\":\"<grid_order_type_down>\",\"multiple_trigger\":false,\"support_shortsell\":false,\"rth\":0}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/gridtrading/replace\", strings.NewReader(`{\"order_id\":\"<order_id>\",\"grid_trading_rule\":{},\"submitted_base_price\":\"<submitted_base_price>\",\"upper_limit_price\":\"<upper_limit_price>\",\"lower_limit_price\":\"<lower_limit_price>\",\"trigger_price_type\":0,\"trigger_spread_up\":\"<trigger_spread_up>\",\"trigger_spread_down\":\"<trigger_spread_down>\",\"trigger_percent_up\":\"<trigger_percent_up>\",\"trigger_percent_down\":\"<trigger_percent_down>\",\"trigger_quantity\":\"<trigger_quantity>\",\"upper_limit_quantity\":\"<upper_limit_quantity>\",\"lower_limit_quantity\":\"<lower_limit_quantity>\",\"time_in_force\":0,\"expire_time\":0,\"upper_limit_event\":0,\"lower_limit_event\":0,\"trigger_sell_depth\":0,\"trigger_buy_depth\":0,\"grid_order_type_up\":\"<grid_order_type_up>\",\"grid_order_type_down\":\"<grid_order_type_down>\",\"multiple_trigger\":false,\"support_shortsell\":false,\"rth\":0}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/gridtrading/list: + post: + operationId: grid_list_by_ids + summary: Query Grid Orders by IDs + x-summary-zh: 按 ID 查询网格订单 + x-summary-zh-hk: 按 ID 查詢網格訂單 + description: | + Query specific grid orders by their IDs. + x-description-zh: | + 按 ID 查询指定的网格订单。 + x-description-zh-hk: | + 按 ID 查詢指定的網格訂單。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 + tags: + - Trade + x-parameters: + - name: order_ids + in: body + type: array + required: true + description: Grid order IDs to query + x-description-zh: 待查询的网格订单 ID + x-description-zh-hk: 待查詢的網格訂單 ID + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Query specific grid orders by ID + longbridge grid --ids 764609681686573056 764609681686573057 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/gridtrading/list' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"order_ids":[]}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/gridtrading/list", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_ids": []}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/gridtrading/list", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_ids": []}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/gridtrading/list", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ order_ids: [] }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/list")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"order_ids\":[]}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/gridtrading/list") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "order_ids": [] })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/list"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"order_ids\":[]}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/gridtrading/list\", strings.NewReader(`{\"order_ids\":[]}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: grid_order + type: object[] + required: false + description: Grid orders + x-description-zh: 网格订单 + x-description-zh-hk: 網格訂單 + - name: └ order_id + type: string + required: true + description: Grid order ID + x-description-zh: 网格订单 ID + x-description-zh-hk: 網格訂單 ID + - name: └ symbol + type: string + required: true + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 标的代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 標的代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: └ stock_name + type: string + required: true + description: Stock name + x-description-zh: 标的名称 + x-description-zh-hk: 標的名稱 + - name: └ market + type: string + required: true + description: Market + x-description-zh: 市场 + x-description-zh-hk: 市場 + - name: └ status + type: string + required: true + description: Grid order status + x-description-zh: 网格订单状态 + x-description-zh-hk: 網格訂單狀態 + - name: └ grid_status + type: string + required: true + description: Grid running status + x-description-zh: 网格运行状态 + x-description-zh-hk: 網格運行狀態 + - name: └ submitted_base_price + type: string + required: true + description: Submitted base price the grid is anchored to + x-description-zh: 网格锚定的提交基准价格 + x-description-zh-hk: 網格錨定的提交基準價格 + - name: └ current_base_price + type: string + required: true + description: Current base price + x-description-zh: 当前基准价格 + x-description-zh-hk: 當前基準價格 + - name: └ pre_trigger_base_price + type: string + required: true + description: Base price before the last trigger + x-description-zh: 上次触发前的基准价格 + x-description-zh-hk: 上次觸發前的基準價格 + - name: └ post_trigger_base_price + type: string + required: true + description: Base price after the last trigger + x-description-zh: 上次触发后的基准价格 + x-description-zh-hk: 上次觸發後的基準價格 + - name: └ upper_limit_price + type: string + required: true + description: Upper price bound + x-description-zh: 价格上限 + x-description-zh-hk: 價格上限 + - name: └ lower_limit_price + type: string + required: true + description: Lower price bound + x-description-zh: 价格下限 + x-description-zh-hk: 價格下限 + - name: └ trigger_price_type + type: int32 + required: true + description: Trigger price type. **Enum Value:**, `1` - Spread, `2` - Percent + x-description-zh: 触发价格类型。**Enum Value:**, `1` - 价差,`2` - 百分比 + x-description-zh-hk: 觸發價格類型。**Enum Value:**, `1` - 價差,`2` - 百分比 + - name: └ trigger_spread_up + type: string + required: true + description: Upward trigger spread, used when `trigger_price_type` is `1` + x-description-zh: 向上触发价差,`trigger_price_type` 为 `1` 时使用 + x-description-zh-hk: 向上觸發價差,`trigger_price_type` 為 `1` 時使用 + - name: └ trigger_spread_down + type: string + required: true + description: Downward trigger spread, used when `trigger_price_type` is `1` + x-description-zh: 向下触发价差,`trigger_price_type` 为 `1` 时使用 + x-description-zh-hk: 向下觸發價差,`trigger_price_type` 為 `1` 時使用 + - name: └ trigger_percent_up + type: string + required: true + description: Upward trigger percent, used when `trigger_price_type` is `2` + x-description-zh: 向上触发百分比,`trigger_price_type` 为 `2` 时使用 + x-description-zh-hk: 向上觸發百分比,`trigger_price_type` 為 `2` 時使用 + - name: └ trigger_percent_down + type: string + required: true + description: Downward trigger percent, used when `trigger_price_type` is `2` + x-description-zh: 向下触发百分比,`trigger_price_type` 为 `2` 时使用 + x-description-zh-hk: 向下觸發百分比,`trigger_price_type` 為 `2` 時使用 + - name: └ pullback_percent + type: string + required: true + description: Pullback percent + x-description-zh: 回调百分比 + x-description-zh-hk: 回調百分比 + - name: └ pullback_spread + type: string + required: true + description: Pullback spread + x-description-zh: 回调价差 + x-description-zh-hk: 回調價差 + - name: └ rebound_percent + type: string + required: true + description: Rebound percent + x-description-zh: 反弹百分比 + x-description-zh-hk: 反彈百分比 + - name: └ rebound_spread + type: string + required: true + description: Rebound spread + x-description-zh: 反弹价差 + x-description-zh-hk: 反彈價差 + - name: └ trigger_sell_order_type + type: string + required: true + description: Sell-side grid order type. **Enum Value:**, `GMO` - Grid market order, `GLO` - Grid limit order, `GTG` - Grid touch-to-go + x-description-zh: 卖出方网格订单类型。**Enum Value:**, `GMO` - 网格市价单,`GLO` - 网格限价单,`GTG` - 网格触价单 + x-description-zh-hk: 賣出方網格訂單類型。**Enum Value:**, `GMO` - 網格市價單,`GLO` - 網格限價單,`GTG` - 網格觸價單 + - name: └ trigger_buy_order_type + type: string + required: true + description: Buy-side grid order type. **Enum Value:**, `GMO` - Grid market order, `GLO` - Grid limit order, `GTG` - Grid touch-to-go + x-description-zh: 买入方网格订单类型。**Enum Value:**, `GMO` - 网格市价单,`GLO` - 网格限价单,`GTG` - 网格触价单 + x-description-zh-hk: 買入方網格訂單類型。**Enum Value:**, `GMO` - 網格市價單,`GLO` - 網格限價單,`GTG` - 網格觸價單 + - name: └ trigger_sell_depth + type: int32 + required: true + description: Sell-side order-book depth, range `-5..5`; `0` means use `trigger_sell_order_type` + x-description-zh: 卖出方盘口深度,范围 `-5..5`;`0` 表示使用 `trigger_sell_order_type` + x-description-zh-hk: 賣出方盤口深度,範圍 `-5..5`;`0` 表示使用 `trigger_sell_order_type` + - name: └ trigger_buy_depth + type: int32 + required: true + description: Buy-side order-book depth, range `-5..5`; `0` means use `trigger_buy_order_type` + x-description-zh: 买入方盘口深度,范围 `-5..5`;`0` 表示使用 `trigger_buy_order_type` + x-description-zh-hk: 買入方盤口深度,範圍 `-5..5`;`0` 表示使用 `trigger_buy_order_type` + - name: └ trigger_quantity + type: string + required: true + description: Quantity per trigger + x-description-zh: 每次触发数量 + x-description-zh-hk: 每次觸發數量 + - name: └ trigger_sell_quantity + type: string + required: true + description: Sell-side trigger quantity + x-description-zh: 卖出方触发数量 + x-description-zh-hk: 賣出方觸發數量 + - name: └ trigger_buy_quantity + type: string + required: true + description: Buy-side trigger quantity + x-description-zh: 买入方触发数量 + x-description-zh-hk: 買入方觸發數量 + - name: └ upper_limit_quantity + type: string + required: true + description: Quantity handled at the upper bound + x-description-zh: 触及上限时处理的数量 + x-description-zh-hk: 觸及上限時處理的數量 + - name: └ lower_limit_quantity + type: string + required: true + description: Quantity handled at the lower bound + x-description-zh: 触及下限时处理的数量 + x-description-zh-hk: 觸及下限時處理的數量 + - name: └ upper_limit_event + type: int32 + required: true + description: Event when the upper bound is reached. **Enum Value:**, `1` - Ignore (keep grid running), `2` - Close position at last price + x-description-zh: 触及上限时的处理事件。**Enum Value:**, `1` - 忽略(保持网格运行), `2` - 以最新价平仓 + x-description-zh-hk: 觸及上限時的處理事件。**Enum Value:**, `1` - 忽略(保持網格運行), `2` - 以最新價平倉 + - name: └ lower_limit_event + type: int32 + required: true + description: Event when the lower bound is reached. **Enum Value:**, `1` - Ignore (keep grid running), `2` - Close position at last price + x-description-zh: 触及下限时的处理事件。**Enum Value:**, `1` - 忽略(保持网格运行), `2` - 以最新价平仓 + x-description-zh-hk: 觸及下限時的處理事件。**Enum Value:**, `1` - 忽略(保持網格運行), `2` - 以最新價平倉 + - name: └ multiple_trigger + type: boolean + required: true + description: Whether a single grid level can trigger multiple times + x-description-zh: 单个网格档位是否可多次触发 + x-description-zh-hk: 單個網格檔位是否可多次觸發 + - name: └ trigger_times + type: int32 + required: true + description: Number of times the grid has triggered + x-description-zh: 网格已触发次数 + x-description-zh-hk: 網格已觸發次數 + - name: └ total_buy_quantity + type: string + required: true + description: Total bought quantity + x-description-zh: 累计买入数量 + x-description-zh-hk: 累計買入數量 + - name: └ total_sell_quantity + type: string + required: true + description: Total sold quantity + x-description-zh: 累计卖出数量 + x-description-zh-hk: 累計賣出數量 + - name: └ total_profit_balance + type: string + required: true + description: Total profit balance + x-description-zh: 累计收益余额 + x-description-zh-hk: 累計收益餘額 + - name: └ settlement_currency + type: string + required: true + description: Settlement currency + x-description-zh: 结算货币 + x-description-zh-hk: 結算貨幣 + - name: └ time_in_force + type: int32 + required: true + description: Time in force type. **Enum Value:**, `0` - Day, `1` - Good-Til-Canceled, `6` - Good-Til-Date + x-description-zh: 订单有效期类型。**Enum Value:**, `0` - 当日有效,`1` - 撤单前有效,`6` - 指定日期前有效 + x-description-zh-hk: 訂單有效期類型。**Enum Value:**, `0` - 當日有效,`1` - 撤單前有效,`6` - 指定日期前有效 + - name: └ gtd + type: string + required: true + description: 'Good-Til-Date expiry date, format: `YYYY-MM-DD`' + x-description-zh: 指定日期前有效的到期日期,格式:`YYYY-MM-DD` + x-description-zh-hk: 指定日期前有效的到期日期,格式:`YYYY-MM-DD` + - name: └ created_at + type: string + required: true + description: Creation time, formatted as RFC3339 + x-description-zh: 创建时间,格式为 RFC3339 + x-description-zh-hk: 創建時間,格式為 RFC3339 + - name: └ rth + type: int32 + required: true + description: Regular trading hours flag. **Enum Value:**, `0`, `1`, `2` + x-description-zh: 盘中交易时段标识。**Enum Value:**, `0`, `1`, `2` + x-description-zh-hk: 盤中交易時段標識。**Enum Value:**, `0`, `1`, `2` + - name: └ support_shortsell + type: boolean + required: true + description: Whether short selling is allowed + x-description-zh: 是否允许卖空 + x-description-zh-hk: 是否允許賣空 + - name: └ grid_order_type_up + type: string + required: true + description: Sell-side order type when depth is `0`. **Enum Value:**, `GMO` - Grid market order, `GLO` - Grid limit order, `GTG` - Grid touch-to-go + x-description-zh: 深度为 `0` 时卖出方的订单类型。**Enum Value:**, `GMO` - 网格市价单,`GLO` - 网格限价单,`GTG` - 网格触价单 + x-description-zh-hk: 深度為 `0` 時賣出方的訂單類型。**Enum Value:**, `GMO` - 網格市價單,`GLO` - 網格限價單,`GTG` - 網格觸價單 + - name: └ grid_order_type_down + type: string + required: true + description: Buy-side order type when depth is `0`. **Enum Value:**, `GMO` - Grid market order, `GLO` - Grid limit order, `GTG` - Grid touch-to-go + x-description-zh: 深度为 `0` 时买入方的订单类型。**Enum Value:**, `GMO` - 网格市价单,`GLO` - 网格限价单,`GTG` - 网格触价单 + x-description-zh-hk: 深度為 `0` 時買入方的訂單類型。**Enum Value:**, `GMO` - 網格市價單,`GLO` - 網格限價單,`GTG` - 網格觸價單 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + grid_order: + - order_id: '764609681686573056' + symbol: 700.HK + stock_name: TENCENT + market: HK + status: Performing + grid_status: Performing + submitted_base_price: '300.000' + current_base_price: '300.000' + pre_trigger_base_price: '300.000' + post_trigger_base_price: '306.000' + upper_limit_price: '360.000' + lower_limit_price: '240.000' + trigger_price_type: 2 + trigger_spread_up: '' + trigger_spread_down: '' + trigger_percent_up: '2' + trigger_percent_down: '2' + pullback_percent: '' + pullback_spread: '' + rebound_percent: '' + rebound_spread: '' + trigger_sell_order_type: GMO + trigger_buy_order_type: GMO + trigger_sell_depth: 0 + trigger_buy_depth: 0 + trigger_quantity: '100' + trigger_sell_quantity: '100' + trigger_buy_quantity: '100' + upper_limit_quantity: '200' + lower_limit_quantity: '100' + upper_limit_event: 1 + lower_limit_event: 1 + multiple_trigger: false + trigger_times: 3 + total_buy_quantity: '300' + total_sell_quantity: '200' + total_profit_balance: '1250.00' + settlement_currency: HKD + time_in_force: 1 + gtd: '' + created_at: '2025-01-15T09:30:00+08:00' + rth: 0 + support_shortsell: false + grid_order_type_up: GMO + grid_order_type_down: GMO + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/gridtrading/cancel: + post: + operationId: grid_cancel + summary: Cancel Grid Order + x-summary-zh: 取消网格订单 + x-summary-zh-hk: 取消網格訂單 + description: | + Cancel a grid order. The grid stops immediately and no further trigger orders are placed. Cancellation is permanent — a canceled grid cannot be restarted; submit a new grid order instead. + x-description-zh: | + 取消网格订单。网格会立即停止,不再挂出新的触发订单。取消是不可逆的——已取消的网格无法重启,如需继续请重新提交新的网格订单。 + x-description-zh-hk: | + 取消網格訂單。網格會立即停止,不再掛出新的觸發訂單。取消是不可逆的——已取消的網格無法重啟,如需繼續請重新提交新的網格訂單。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 + tags: + - Trade + x-parameters: + - name: order_id + in: body + type: string + required: true + description: 'Grid order ID, example: `764609681686573056`' + x-description-zh: 网格订单 ID,例如:`764609681686573056` + x-description-zh-hk: 網格訂單 ID,例如:`764609681686573056` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Cancel a grid order by ID + longbridge grid cancel 764609681686573056 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/gridtrading/cancel' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"order_id":"<order_id>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/gridtrading/cancel", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/gridtrading/cancel", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/gridtrading/cancel", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ order_id: "<order_id>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/cancel")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"order_id\":\"<order_id>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/gridtrading/cancel") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "order_id": "<order_id>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/cancel"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"order_id\":\"<order_id>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/gridtrading/cancel\", strings.NewReader(`{\"order_id\":\"<order_id>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/gridtrading/suspend: + post: + operationId: grid_suspend + summary: Suspend Grid Order + x-summary-zh: 暂停网格订单 + x-summary-zh-hk: 暫停網格訂單 + description: | + Suspend a running grid order. The grid stops triggering new orders but is kept intact, so it can be resumed later with Restart Grid Order. Use suspend when you want to pause the strategy without losing its configuration. + x-description-zh: | + 暂停运行中的网格订单。网格停止触发新订单,但会保留原有配置,之后可通过重启网格订单恢复运行。当你希望暂停策略但不丢失其配置时,使用暂停操作。 + x-description-zh-hk: | + 暫停運行中的網格訂單。網格停止觸發新訂單,但會保留原有配置,之後可透過重啟網格訂單恢復運行。當你希望暫停策略但不丟失其配置時,使用暫停操作。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 + tags: + - Trade + x-parameters: + - name: order_id + in: body + type: string + required: true + description: 'Grid order ID, example: `764609681686573056`' + x-description-zh: 网格订单 ID,例如:`764609681686573056` + x-description-zh-hk: 網格訂單 ID,例如:`764609681686573056` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Suspend a running grid order by ID + longbridge grid suspend 764609681686573056 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/gridtrading/suspend' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"order_id":"<order_id>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/gridtrading/suspend", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/gridtrading/suspend", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/gridtrading/suspend", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ order_id: "<order_id>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/suspend")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"order_id\":\"<order_id>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/gridtrading/suspend") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "order_id": "<order_id>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/suspend"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"order_id\":\"<order_id>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/gridtrading/suspend\", strings.NewReader(`{\"order_id\":\"<order_id>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/gridtrading/restart: + post: + operationId: grid_restart + summary: Restart Grid Order + x-summary-zh: 重启网格订单 + x-summary-zh-hk: 重啟網格訂單 + description: | + Restart a suspended grid order. The grid resumes from its saved configuration and begins triggering orders again. Only a grid previously paused with Suspend Grid Order can be restarted. + x-description-zh: | + 重启已暂停的网格订单。网格会从保存的配置恢复,并重新开始触发订单。只有此前通过暂停网格订单暂停的网格才能被重启。 + x-description-zh-hk: | + 重啟已暫停的網格訂單。網格會從保存的配置恢復,並重新開始觸發訂單。只有此前透過暫停網格訂單暫停的網格才能被重啟。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 + tags: + - Trade + x-parameters: + - name: order_id + in: body + type: string + required: true + description: 'Grid order ID, example: `764609681686573056`' + x-description-zh: 网格订单 ID,例如:`764609681686573056` + x-description-zh-hk: 網格訂單 ID,例如:`764609681686573056` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Restart a suspended grid order by ID + longbridge grid restart 764609681686573056 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/gridtrading/restart' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"order_id":"<order_id>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/gridtrading/restart", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/gridtrading/restart", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"order_id": "<order_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/gridtrading/restart", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ order_id: "<order_id>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/restart")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"order_id\":\"<order_id>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/gridtrading/restart") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "order_id": "<order_id>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/restart"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"order_id\":\"<order_id>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/gridtrading/restart\", strings.NewReader(`{\"order_id\":\"<order_id>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /v1/watchlist/groups: + post: + operationId: create_watchlist_group + summary: Create Group + x-summary-zh: 创建分组 + x-summary-zh-hk: 創建分組 + description: | + Create watched group + x-description-zh: | + 创建自选股分组 + x-description-zh-hk: | + 創建自選股分組 + x-subgroup: Watchlist + x-subgroup-zh: 自选股 + x-subgroup-zh-hk: 自選股 + x-quote-level: basic + tags: + - Quote + x-parameters: + - name: name + in: body + type: string + required: true + description: Watchlist group name. + x-description-zh: 自选股分组名称。 + x-description-zh-hk: 自選股分組名稱。 + - name: securities + in: body + type: array + required: false + description: Optional list of security symbols to pre-populate the group, e.g. `["AAPL.US","700.HK"]`. + x-description-zh: 可选:创建时预填充的证券代码列表,如 `["AAPL.US","700.HK"]`。 + x-description-zh-hk: 可選:創建時預填充的證券代碼列表,如 `["AAPL.US","700.HK"]`。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + # create a new watchlist group + longbridge watchlist create "My Portfolio" + # create another watchlist group + longbridge watchlist create "Tech Stocks" + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/watchlist/groups' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"name":"<name>","securities":[]}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/watchlist/groups", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"name": "<name>", "securities": []}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/watchlist/groups", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"name": "<name>", "securities": []}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/watchlist/groups", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ name: "<name>", securities: [] }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/watchlist/groups")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"name\":\"<name>\",\"securities\":[]}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/watchlist/groups") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "name": "<name>" , "securities": [] })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/watchlist/groups"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"name\":\"<name>\",\"securities\":[]}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/watchlist/groups\", strings.NewReader(`{\"name\":\"<name>\",\"securities\":[]}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: id + type: integer + required: false + description: Group ID + x-description-zh: 分组 ID + x-description-zh-hk: 分組 ID + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + data: + id: 10086 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + operationId: update_watchlist_group + summary: Manage Group Securities + x-summary-zh: 管理分组标的 + x-summary-zh-hk: 管理分組標的 + description: | + Update watched group + x-description-zh: | + 更新自选股分组 + x-description-zh-hk: | + 更新自選股分組 + x-subgroup: Watchlist + x-subgroup-zh: 自选股 + x-subgroup-zh-hk: 自選股 + x-quote-level: basic + tags: + - Quote + x-parameters: + - name: id + in: body + type: integer + required: true + description: Watchlist group ID. + x-description-zh: 自选股分组 ID。 + x-description-zh-hk: 自選股分組 ID。 + - name: name + in: body + type: string + required: false + description: New group name. + x-description-zh: 新的分组名称。 + x-description-zh-hk: 新的分組名稱。 + - name: securities + in: body + type: array + required: false + description: Security symbols to add/remove/replace, per `mode`. + x-description-zh: 按 `mode` 增/删/替换的证券代码列表。 + x-description-zh-hk: 按 `mode` 增/刪/替換的證券代碼列表。 + - name: mode + in: body + type: string + required: true + description: 'Securities update mode: `add`, `remove`, or `replace`.' + x-description-zh: 证券更新模式:`add`、`remove`、`replace`。 + x-description-zh-hk: 證券更新模式:`add`、`remove`、`replace`。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + # add symbols to a group + longbridge watchlist update <id> --add TSLA.US --add AAPL.US + # remove a symbol from a group + longbridge watchlist update <id> --remove NVDA.US + # add and remove at the same time + longbridge watchlist update <id> --add TSLA.US --remove AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request PUT \ + --url 'https://openapi.longbridge.com/v1/watchlist/groups' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"id":0,"name":"<name>","securities":[],"mode":"<mode>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.put( + "https://openapi.longbridge.com/v1/watchlist/groups", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"id": 0, "name": "<name>", "securities": [], "mode": "<mode>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.put( + "https://openapi.longbridge.com/v1/watchlist/groups", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"id": 0, "name": "<name>", "securities": [], "mode": "<mode>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/watchlist/groups", { + method: "PUT", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ id: 0, name: "<name>", securities: [], mode: "<mode>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/watchlist/groups")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("PUT", HttpRequest.BodyPublishers.ofString("{\"id\":0,\"name\":\"<name>\",\"securities\":[],\"mode\":\"<mode>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::PUT, "https://openapi.longbridge.com/v1/watchlist/groups") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "id": 0 , "name": "<name>" , "securities": [] , "mode": "<mode>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/watchlist/groups"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"id\":0,\"name\":\"<name>\",\"securities\":[],\"mode\":\"<mode>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"PUT\", \"https://openapi.longbridge.com/v1/watchlist/groups\", strings.NewReader(`{\"id\":0,\"name\":\"<name>\",\"securities\":[],\"mode\":\"<mode>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/dailycoins/create: + post: + operationId: dca_create + summary: Create DCA Plan + x-summary-zh: 创建定投 + x-summary-zh-hk: 創建定投 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Create a new recurring investment plan for a security. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 为指定证券创建新的定投。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 為指定證券創建新的定投。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 + tags: + - Account + x-parameters: + - name: symbol + in: body + type: string + required: true + description: Security symbol, e.g. `AAPL.US`. + x-description-zh: 标的代码,如 `AAPL.US`。 + x-description-zh-hk: 標的代碼,如 `AAPL.US`。 + - name: per_invest_amount + in: body + type: string + required: true + description: Recurring investment amount per execution. + x-description-zh: 每次定投金额。 + x-description-zh-hk: 每次定投金額。 + - name: invest_frequency + in: body + type: string + required: true + description: 'Frequency: `Daily`, `Weekly`, `Fortnightly`, `Monthly`.' + x-description-zh: 频率:`Daily`、`Weekly`、`Fortnightly`、`Monthly`。 + x-description-zh-hk: 頻率:`Daily`、`Weekly`、`Fortnightly`、`Monthly`。 + - name: allow_margin_finance + in: body + type: integer + required: true + description: 'Whether to allow margin financing: `1` yes, `0` no.' + x-description-zh: 是否允许融资:`1` 是,`0` 否。 + x-description-zh-hk: 是否允許融資:`1` 是,`0` 否。 + - name: invest_day_of_week + in: body + type: string + required: false + description: 'Day of week for weekly/fortnightly plans: `mon`–`fri`.' + x-description-zh: 每周/每两周计划的执行星期:`mon`–`fri`。 + x-description-zh-hk: 每週/每兩週計劃的執行星期:`mon`–`fri`。 + - name: invest_day_of_month + in: body + type: string + required: false + description: 'Day of month for monthly plans: `1`–`28`.' + x-description-zh: 每月计划的执行日:`1`–`28`。 + x-description-zh-hk: 每月計劃的執行日:`1`–`28`。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge dca create AAPL.US --amount 500 --frequency monthly --day-of-month 15 + longbridge dca create TSLA.US --amount 200 --frequency weekly --day-of-week mon + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/dailycoins/create' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbol":"<symbol>","per_invest_amount":"<per_invest_amount>","invest_frequency":"<invest_frequency>","allow_margin_finance":0,"invest_day_of_week":"<invest_day_of_week>","invest_day_of_month":"<invest_day_of_month>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/dailycoins/create", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "per_invest_amount": "<per_invest_amount>", "invest_frequency": "<invest_frequency>", "allow_margin_finance": 0, "invest_day_of_week": "<invest_day_of_week>", "invest_day_of_month": "<invest_day_of_month>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/dailycoins/create", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "per_invest_amount": "<per_invest_amount>", "invest_frequency": "<invest_frequency>", "allow_margin_finance": 0, "invest_day_of_week": "<invest_day_of_week>", "invest_day_of_month": "<invest_day_of_month>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/create", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbol: "<symbol>", per_invest_amount: "<per_invest_amount>", invest_frequency: "<invest_frequency>", allow_margin_finance: 0, invest_day_of_week: "<invest_day_of_week>", invest_day_of_month: "<invest_day_of_month>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/create")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbol\":\"<symbol>\",\"per_invest_amount\":\"<per_invest_amount>\",\"invest_frequency\":\"<invest_frequency>\",\"allow_margin_finance\":0,\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/dailycoins/create") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbol": "<symbol>" , "per_invest_amount": "<per_invest_amount>" , "invest_frequency": "<invest_frequency>" , "allow_margin_finance": 0 , "invest_day_of_week": "<invest_day_of_week>" , "invest_day_of_month": "<invest_day_of_month>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/create"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbol\":\"<symbol>\",\"per_invest_amount\":\"<per_invest_amount>\",\"invest_frequency\":\"<invest_frequency>\",\"allow_margin_finance\":0,\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/dailycoins/create\", strings.NewReader(`{\"symbol\":\"<symbol>\",\"per_invest_amount\":\"<per_invest_amount>\",\"invest_frequency\":\"<invest_frequency>\",\"allow_margin_finance\":0,\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: id + type: string + required: true + description: ID of the newly created plan + x-description-zh: 新创建计划的 ID + x-description-zh-hk: 新創建計劃的 ID + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + id: '1225781523156889601' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/dailycoins/update: + post: + operationId: dca_update + summary: Update DCA Plan + x-summary-zh: 更新定投 + x-summary-zh-hk: 更新定投 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Pause or resume an existing recurring investment plan. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 暂停或恢复已有的定投。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 暫停或恢復已有的定投。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 + tags: + - Account + x-parameters: + - name: plan_id + in: body + type: string + required: true + description: DCA plan ID to update. + x-description-zh: 要更新的定投计划 ID。 + x-description-zh-hk: 要更新的定投計劃 ID。 + - name: per_invest_amount + in: body + type: string + required: false + description: New recurring investment amount. + x-description-zh: 新的每次定投金额。 + x-description-zh-hk: 新的每次定投金額。 + - name: invest_frequency + in: body + type: string + required: false + description: 'Frequency: `Daily`, `Weekly`, `Fortnightly`, `Monthly`.' + x-description-zh: 频率:`Daily`、`Weekly`、`Fortnightly`、`Monthly`。 + x-description-zh-hk: 頻率:`Daily`、`Weekly`、`Fortnightly`、`Monthly`。 + - name: invest_day_of_week + in: body + type: string + required: false + description: 'Day of week for weekly/fortnightly plans: `mon`–`fri`.' + x-description-zh: 每周/每两周计划的执行星期:`mon`–`fri`。 + x-description-zh-hk: 每週/每兩週計劃的執行星期:`mon`–`fri`。 + - name: invest_day_of_month + in: body + type: string + required: false + description: 'Day of month for monthly plans: `1`–`28`.' + x-description-zh: 每月计划的执行日:`1`–`28`。 + x-description-zh-hk: 每月計劃的執行日:`1`–`28`。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge dca pause 1225781523156889600 + longbridge dca resume 1225781523156889600 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/dailycoins/update' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"plan_id":"<plan_id>","per_invest_amount":"<per_invest_amount>","invest_frequency":"<invest_frequency>","invest_day_of_week":"<invest_day_of_week>","invest_day_of_month":"<invest_day_of_month>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/dailycoins/update", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"plan_id": "<plan_id>", "per_invest_amount": "<per_invest_amount>", "invest_frequency": "<invest_frequency>", "invest_day_of_week": "<invest_day_of_week>", "invest_day_of_month": "<invest_day_of_month>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/dailycoins/update", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"plan_id": "<plan_id>", "per_invest_amount": "<per_invest_amount>", "invest_frequency": "<invest_frequency>", "invest_day_of_week": "<invest_day_of_week>", "invest_day_of_month": "<invest_day_of_month>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/update", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ plan_id: "<plan_id>", per_invest_amount: "<per_invest_amount>", invest_frequency: "<invest_frequency>", invest_day_of_week: "<invest_day_of_week>", invest_day_of_month: "<invest_day_of_month>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/update")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"plan_id\":\"<plan_id>\",\"per_invest_amount\":\"<per_invest_amount>\",\"invest_frequency\":\"<invest_frequency>\",\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/dailycoins/update") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "plan_id": "<plan_id>" , "per_invest_amount": "<per_invest_amount>" , "invest_frequency": "<invest_frequency>" , "invest_day_of_week": "<invest_day_of_week>" , "invest_day_of_month": "<invest_day_of_month>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/update"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"plan_id\":\"<plan_id>\",\"per_invest_amount\":\"<per_invest_amount>\",\"invest_frequency\":\"<invest_frequency>\",\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/dailycoins/update\", strings.NewReader(`{\"plan_id\":\"<plan_id>\",\"per_invest_amount\":\"<per_invest_amount>\",\"invest_frequency\":\"<invest_frequency>\",\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: plan_id + type: string + required: true + description: ID of the updated plan + x-description-zh: 被更新计划的 ID + x-description-zh-hk: 被更新計劃的 ID + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + plan_id: '1286530167409217536' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/dailycoins/toggle: + post: + operationId: dca_toggle + summary: Pause / Resume / Stop DCA Plan + x-summary-zh: 暂停 / 恢复 / 终止定投计划 + x-summary-zh-hk: 暫停 / 恢復 / 終止定投計劃 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Change the status of an existing DCA plan: pause, resume, or permanently stop it. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 更改现有定投计划的状态:暂停、恢复或永久终止。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 更改現有定投計劃的狀態:暫停、恢復或永久終止。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 + tags: + - Account + x-parameters: + - name: plan_id + in: body + type: string + required: true + description: DCA plan ID. + x-description-zh: 定投计划 ID。 + x-description-zh-hk: 定投計劃 ID。 + - name: status + in: body + type: string + required: true + description: 'Target status: `Suspended` (pause), `Active` (resume), `Finished` (stop).' + x-description-zh: 目标状态:`Suspended`(暂停)、`Active`(恢复)、`Finished`(终止)。 + x-description-zh-hk: 目標狀態:`Suspended`(暫停)、`Active`(恢復)、`Finished`(終止)。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge dca pause 12345 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/dailycoins/toggle' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"plan_id":"<plan_id>","status":"<status>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/dailycoins/toggle", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"plan_id": "<plan_id>", "status": "<status>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/dailycoins/toggle", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"plan_id": "<plan_id>", "status": "<status>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/toggle", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ plan_id: "<plan_id>", status: "<status>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/toggle")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"plan_id\":\"<plan_id>\",\"status\":\"<status>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/dailycoins/toggle") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "plan_id": "<plan_id>" , "status": "<status>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/toggle"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"plan_id\":\"<plan_id>\",\"status\":\"<status>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/dailycoins/toggle\", strings.NewReader(`{\"plan_id\":\"<plan_id>\",\"status\":\"<status>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/dailycoins/batch-check-support: + post: + operationId: dca_check_support + summary: Check DCA Support + x-summary-zh: 检查定投支持 + x-summary-zh-hk: 檢查定投支持 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Check whether the given securities support DCA recurring investment. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 检查指定标的是否支持定投。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 檢查指定標的是否支持定投。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 + tags: + - Account + x-parameters: + - name: symbols + in: body + type: array + required: true + description: List of security symbols to check for DCA support. + x-description-zh: 要检查是否支持定投的标的代码列表。 + x-description-zh-hk: 要檢查是否支持定投的標的代碼列表。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge dca check AAPL.US 700.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/dailycoins/batch-check-support' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbols":[]}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/dailycoins/batch-check-support", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbols": []}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/dailycoins/batch-check-support", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbols": []}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/batch-check-support", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbols: [] }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/batch-check-support")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbols\":[]}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/dailycoins/batch-check-support") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbols": [] })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/batch-check-support"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbols\":[]}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/dailycoins/batch-check-support\", strings.NewReader(`{\"symbols\":[]}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: infos + type: object[] + required: true + description: List of DCA support results + x-description-zh: 定投支持情况列表, + x-description-zh-hk: 定投支持情況列表, + - name: └ symbol + type: string + required: true + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ support_regular_saving + type: boolean + required: true + description: Whether DCA is supported + x-description-zh: 是否支持定投 + x-description-zh-hk: 是否支持定投 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + infos: + - symbol: AAPL.US + support_regular_saving: true + - symbol: 700.HK + support_regular_saving: false + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/dailycoins/calc-trd-date: + post: + operationId: dca_calc_date + summary: Calculate DCA Date + x-summary-zh: 计算定投日期 + x-summary-zh-hk: 計算定投日期 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Calculate the next projected trade date for given DCA plan parameters. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 根据给定的定投计划参数,计算下一次预计交易日期。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 根據給定的定投計劃參數,計算下一次預計交易日期。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 + tags: + - Account + x-parameters: + - name: symbol + in: body + type: string + required: true + description: Security symbol. + x-description-zh: 标的代码。 + x-description-zh-hk: 標的代碼。 + - name: invest_frequency + in: body + type: string + required: true + description: 'Frequency: `Daily`, `Weekly`, `Fortnightly`, `Monthly`.' + x-description-zh: 频率:`Daily`、`Weekly`、`Fortnightly`、`Monthly`。 + x-description-zh-hk: 頻率:`Daily`、`Weekly`、`Fortnightly`、`Monthly`。 + - name: invest_day_of_week + in: body + type: string + required: false + description: 'Day of week for weekly/fortnightly plans: `mon`–`fri`.' + x-description-zh: 每周/每两周计划的执行星期:`mon`–`fri`。 + x-description-zh-hk: 每週/每兩週計劃的執行星期:`mon`–`fri`。 + - name: invest_day_of_month + in: body + type: string + required: false + description: 'Day of month for monthly plans: `1`–`28`.' + x-description-zh: 每月计划的执行日:`1`–`28`。 + x-description-zh-hk: 每月計劃的執行日:`1`–`28`。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge dca calc-date AAPL.US --frequency monthly --day-of-month 15 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/dailycoins/calc-trd-date' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"symbol":"<symbol>","invest_frequency":"<invest_frequency>","invest_day_of_week":"<invest_day_of_week>","invest_day_of_month":"<invest_day_of_month>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/dailycoins/calc-trd-date", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "invest_frequency": "<invest_frequency>", "invest_day_of_week": "<invest_day_of_week>", "invest_day_of_month": "<invest_day_of_month>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/dailycoins/calc-trd-date", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"symbol": "<symbol>", "invest_frequency": "<invest_frequency>", "invest_day_of_week": "<invest_day_of_week>", "invest_day_of_month": "<invest_day_of_month>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/calc-trd-date", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ symbol: "<symbol>", invest_frequency: "<invest_frequency>", invest_day_of_week: "<invest_day_of_week>", invest_day_of_month: "<invest_day_of_month>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/calc-trd-date")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"symbol\":\"<symbol>\",\"invest_frequency\":\"<invest_frequency>\",\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/dailycoins/calc-trd-date") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "symbol": "<symbol>" , "invest_frequency": "<invest_frequency>" , "invest_day_of_week": "<invest_day_of_week>" , "invest_day_of_month": "<invest_day_of_month>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/calc-trd-date"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"symbol\":\"<symbol>\",\"invest_frequency\":\"<invest_frequency>\",\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/dailycoins/calc-trd-date\", strings.NewReader(`{\"symbol\":\"<symbol>\",\"invest_frequency\":\"<invest_frequency>\",\"invest_day_of_week\":\"<invest_day_of_week>\",\"invest_day_of_month\":\"<invest_day_of_month>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: trade_date + type: string + required: true + description: Next projected trade date (YYYY-MM-DD) + x-description-zh: 下一次预计交易日期(YYYY-MM-DD) + x-description-zh-hk: 下一次預計交易日期(YYYY-MM-DD) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + trade_date: '2024-02-15' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/dailycoins/update-alter-hours: + post: + operationId: dca_set_reminder + summary: Set DCA Reminder + x-summary-zh: 设置定投提醒 + x-summary-zh-hk: 設置定投提醒 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Set the advance reminder time for DCA plans. Supported values: `1`, `6`, or `12` hours. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 设置定投计划的提前提醒时间。支持的值:`1`、`6` 或 `12` 小时。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 設置定投計劃的提前提醒時間。支持的值:`1`、`6` 或 `12` 小時。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 + tags: + - Account + x-parameters: + - name: alter_hours + in: body + type: string + required: true + description: 'Advance reminder hours before DCA execution: `1`, `6`, or `12`.' + x-description-zh: 定投执行前的提前提醒小时数:`1`、`6` 或 `12`。 + x-description-zh-hk: 定投執行前的提前提醒小時數:`1`、`6` 或 `12`。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge dca set-reminder 12 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request POST \ + --url 'https://openapi.longbridge.com/v1/dailycoins/update-alter-hours' \ + --header 'Authorization: Bearer <access_token>' \ + --header 'Content-Type: application/json' \ + --data '{"alter_hours":"<alter_hours>"}' + - lang: Python + label: Python + source: | + import requests + + resp = requests.post( + "https://openapi.longbridge.com/v1/dailycoins/update-alter-hours", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"alter_hours": "<alter_hours>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.post( + "https://openapi.longbridge.com/v1/dailycoins/update-alter-hours", + headers={"Authorization": "Bearer <access_token>", "Content-Type": "application/json"}, + json={"alter_hours": "<alter_hours>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/update-alter-hours", { + method: "POST", + headers: { + "Authorization": "Bearer <access_token>", + "Content-Type": "application/json", + }, + body: JSON.stringify({ alter_hours: "<alter_hours>" }), + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/update-alter-hours")) + .header("Authorization", "Bearer <access_token>") + .header("Content-Type", "application/json") + .method("POST", HttpRequest.BodyPublishers.ofString("{\"alter_hours\":\"<alter_hours>\"}")) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::POST, "https://openapi.longbridge.com/v1/dailycoins/update-alter-hours") + .header("Authorization", "Bearer <access_token>") + .json(&serde_json::json!({ "alter_hours": "<alter_hours>" })) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/update-alter-hours"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"alter_hours\":\"<alter_hours>\"}"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"POST\", \"https://openapi.longbridge.com/v1/dailycoins/update-alter-hours\", strings.NewReader(`{\"alter_hours\":\"<alter_hours>\"}`))\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/signals: + get: + operationId: signals + summary: Query Signals + x-summary-zh: 查询策略信号 + x-summary-zh-hk: 查詢策略信號 + description: | + Query strategy signals, filtered by symbol, strategy, catalyst and time range. Each signal is a strategy’s take on a security, triggered by a catalyst fact. + x-description-zh: | + 查询策略信号,可按标的、策略、催化因子和时间范围过滤。每条信号代表某个策略对一只标的的判断,由一个催化事实(fact)触发。 + x-description-zh-hk: | + 查詢策略信號,可按標的、策略、催化因子和時間範圍過濾。每條信號代表某個策略對一隻標的的判斷,由一個催化事實(fact)觸發。 + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 + tags: + - Quote + x-parameters: + - name: symbol_name + in: query + type: string + required: false + description: Filter by security symbol, e.g. `AAPL.US` or `700.HK`. If omitted, returns signals for all symbols. + x-description-zh: 按标的代码过滤,如 `AAPL.US` 或 `700.HK`。省略则返回全部标的的信号。 + x-description-zh-hk: 按標的代碼過濾,如 `AAPL.US` 或 `700.HK`。省略則返回全部標的的信號。 + - name: strategy_id + in: query + type: string + required: false + description: Filter by strategy id, e.g. `buffett-value`. Preferred over the deprecated `strategy_name`; takes precedence when both are provided. + x-description-zh: 按策略 id 过滤,如 `buffett-value`。优先于已废弃的 `strategy_name`,两者同时提供时以此为准。 + x-description-zh-hk: 按策略 id 過濾,如 `buffett-value`。優先於已廢棄的 `strategy_name`,兩者同時提供時以此為準。 + - name: strategy_name + in: query + type: string + required: false + description: Filter by strategy name. Deprecated in favour of `strategy_id`. If omitted, returns signals from all strategies. + x-description-zh: 按策略名称过滤。已废弃,请改用 `strategy_id`。省略则返回全部策略的信号。 + x-description-zh-hk: 按策略名稱過濾。已廢棄,請改用 `strategy_id`。省略則返回全部策略的信號。 + - name: catalyst_name + in: query + type: string + required: false + description: Filter by the name of the factor that triggered the signal, e.g. `EARNINGS_RELEASED` or `macd_12_26_9` — the triggering fact’s `factors[].name`, not the display label. If omitted, any catalyst name is returned. + x-description-zh: 按触发信号的因子名称过滤,如 `EARNINGS_RELEASED` 或 `macd_12_26_9`——即触发事实的 `factors[].name`,而非展示用标签。省略则不限催化因子名称。 + x-description-zh-hk: 按觸發信號的因子名稱過濾,如 `EARNINGS_RELEASED` 或 `macd_12_26_9`——即觸發事實的 `factors[].name`,而非展示用標籤。省略則不限催化因子名稱。 + - name: catalyst_type + in: query + type: string + required: false + description: Filter by the catalyst type that triggered the signal, e.g. `News`, `Fundamental`, `Technical`. If omitted, any catalyst type is returned. + x-description-zh: 按触发信号的催化类型过滤,如 `News`、`Fundamental`、`Technical`。省略则不限催化类型。 + x-description-zh-hk: 按觸發信號的催化類型過濾,如 `News`、`Fundamental`、`Technical`。省略則不限催化類型。 + - name: start_time + in: query + type: string + required: false + description: Only return signals created at or after this time. RFC3339 (a Unix timestamp is also accepted). If omitted, no lower bound. + x-description-zh: 仅返回创建时间不早于该时刻的信号。RFC3339(也接受 Unix 时间戳)。省略则无下界。 + x-description-zh-hk: 僅返回建立時間不早於該時刻的信號。RFC3339(也接受 Unix 時間戳)。省略則無下界。 + - name: end_time + in: query + type: string + required: false + description: Only return signals created at or before this time. RFC3339 (a Unix timestamp is also accepted). If omitted, no upper bound. + x-description-zh: 仅返回创建时间不晚于该时刻的信号。RFC3339(也接受 Unix 时间戳)。省略则无上界。 + x-description-zh-hk: 僅返回建立時間不晚於該時刻的信號。RFC3339(也接受 Unix 時間戳)。省略則無上界。 + - name: limit + in: query + type: integer + required: false + description: Maximum number of results to return. Defaults to 20. + x-description-zh: 返回结果的最大数量。默认 20。 + x-description-zh-hk: 返回結果的最大數量。預設 20。 + - name: offset + in: query + type: integer + required: false + description: Number of results to skip for pagination. Defaults to 0. + x-description-zh: 分页跳过的结果数量。默认 0。 + x-description-zh-hk: 分頁跳過的結果數量。預設 0。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/signals' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/signals", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/signals", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/signals", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/signals")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/signals") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/signals"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/signals\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: signals + type: object[] + required: false + description: Signals on this page + x-description-zh: 本页的信号列表 + x-description-zh-hk: 本頁的信號列表 + - name: └ id + type: string + required: false + description: Signal ID, e.g. `sign_992_1a00c9425c3_48ab` + x-description-zh: 信号 ID,如 `sign_992_1a00c9425c3_48ab` + x-description-zh-hk: 信號 ID,如 `sign_992_1a00c9425c3_48ab` + - name: └ stock_symbol + type: string + required: false + description: Security symbol, e.g. `700.HK`. + x-description-zh: 标的代码,如 `700.HK`。 + x-description-zh-hk: 標的代碼,如 `700.HK`。 + - name: └ symbol + type: string + required: false + description: Security symbol, e.g. `992.HK` + x-description-zh: 标的代码,如 `992.HK` + x-description-zh-hk: 標的代碼,如 `992.HK` + - name: └ company_name + type: string + required: false + description: Company name + x-description-zh: 公司名称 + x-description-zh-hk: 公司名稱 + - name: └ title + type: string + required: false + description: Signal headline + x-description-zh: 信号标题 + x-description-zh-hk: 信號標題 + - name: └ strategy_name + type: string + required: false + description: Strategy name that produced the signal + x-description-zh: 产生信号的策略名称 + x-description-zh-hk: 產生信號的策略名稱 + - name: └ strategy_id + type: string + required: false + description: Strategy ID that produced the signal + x-description-zh: 产生信号的策略 ID + x-description-zh-hk: 產生信號的策略 ID + - name: └ expression + type: string + required: false + description: Strategy expression, e.g. `992.HK:GROWTH:long` + x-description-zh: 策略表达式,如 `992.HK:GROWTH:long` + x-description-zh-hk: 策略表達式,如 `992.HK:GROWTH:long` + - name: └ created_at + type: string + required: false + description: Creation time (RFC3339) + x-description-zh: 创建时间(RFC3339) + x-description-zh-hk: 建立時間(RFC3339) + - name: └ updated_at + type: string + required: false + description: Last update time (RFC3339) + x-description-zh: 最后更新时间(RFC3339) + x-description-zh-hk: 最後更新時間(RFC3339) + - name: └ status + type: integer + required: false + description: 'Lifecycle status: `0` Pending, `1` Active, `2` Deleted, `3` AiFailed, `4` FilteredByManual, `5` AiSubmitFailed' + x-description-zh: 生命周期状态:`0` 待发布、`1` 生效中、`2` 已删除、`3` AI 生成失败、`4` 人工过滤、`5` AI 提交失败 + x-description-zh-hk: 生命週期狀態:`0` 待發布、`1` 生效中、`2` 已刪除、`3` AI 生成失敗、`4` 人工過濾、`5` AI 提交失敗 + - name: └ json_data + type: string + required: false + description: Full analysis behind the signal as a JSON document (strategy fit scores, valuation scenarios, evidence sources, related fact IDs). Carried verbatim because its shape is strategy-specific. + x-description-zh: 信号背后的完整分析(JSON 文档:策略契合评分、估值情景、证据来源、相关事实 ID)。因结构随策略而异,原样透传。 + x-description-zh-hk: 信號背後的完整分析(JSON 文檔:策略契合評分、估值情景、證據來源、相關事實 ID)。因結構隨策略而異,原樣透傳。 + - name: └ key_fact_id + type: string + required: false + description: ID of the fact that triggered the signal + x-description-zh: 触发信号的事实 ID + x-description-zh-hk: 觸發信號的事實 ID + - name: └ key_catalyst + type: string + required: false + description: Display label of the catalyst that triggered the signal, e.g. `Q1 Revenue Surge` + x-description-zh: 触发信号的催化剂展示标签,如 `Q1 Revenue Surge` + x-description-zh-hk: 觸發信號的催化劑展示標籤,如 `Q1 Revenue Surge` + - name: └ analysis_price + type: integer + required: false + description: Price the analysis was based on + x-description-zh: 分析所依据的价格 + x-description-zh-hk: 分析所依據的價格 + - name: └ conservative_price + type: integer + required: false + description: Conservative-scenario target price + x-description-zh: 保守情景目标价 + x-description-zh-hk: 保守情景目標價 + - name: └ benchmark_price + type: integer + required: false + description: Benchmark-scenario target price + x-description-zh: 基准情景目标价 + x-description-zh-hk: 基準情景目標價 + - name: └ optimistic_price + type: integer + required: false + description: Optimistic-scenario target price + x-description-zh: 乐观情景目标价 + x-description-zh-hk: 樂觀情景目標價 + - name: └ outlook + type: string + required: false + description: 'Outlook the strategy takes on the security: `Strong bullish`, `Bullish`, `Neutral`, `Bearish`, `Strong bearish`' + x-description-zh: 策略对标的的观点:`Strong bullish`、`Bullish`、`Neutral`、`Bearish`、`Strong bearish` + x-description-zh-hk: 策略對標的的觀點:`Strong bullish`、`Bullish`、`Neutral`、`Bearish`、`Strong bearish` + - name: └ summary + type: string + required: false + description: Natural-language summary of the signal, in Markdown + x-description-zh: 信号的自然语言摘要(Markdown) + x-description-zh-hk: 信號的自然語言摘要(Markdown) + - name: └ market + type: string + required: false + description: Market the security trades in, e.g. `HK` + x-description-zh: 标的所属市场,如 `HK` + x-description-zh-hk: 標的所屬市場,如 `HK` + - name: └ outlook_desc + type: string + required: false + description: Outlook label localized in the caller’s language + x-description-zh: 观点在调用方语言下的本地化标签 + x-description-zh-hk: 觀點在調用方語言下的本地化標籤 + - name: └ display_control + type: integer + required: false + description: Display control flag. + x-description-zh: 展示控制标志。 + x-description-zh-hk: 展示控制標誌。 + - name: └ recommend_by + type: string + required: false + description: Who recommended the signal; empty for strategy-generated signals + x-description-zh: 信号推荐人;策略自动生成时为空 + x-description-zh-hk: 信號推薦人;策略自動生成時為空 + - name: └ risk_level + type: string + required: false + description: Risk control level. <b>Option:</b>, `0` - safe, `1` - medium risk, `2` - early warning, `3` - danger + x-description-zh: 风控等级。<b>可选值:</b>, `0` - 安全,`1` - 中风险,`2` - 预警,`3` - 危险 + x-description-zh-hk: 風控等級。<b>可選值:</b>, `0` - 安全,`1` - 中風險,`2` - 預警,`3` - 危險 + - name: total + type: integer + required: false + description: Total number of signals matching the filters, for paging with `offset` + x-description-zh: 符合过滤条件的信号总数,配合 `offset` 分页 + x-description-zh-hk: 符合過濾條件的信號總數,配合 `offset` 分頁 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + signals: + - id: sign_992_1a00c9425c3_48ab + symbol: 992.HK + company_name: Lenovo Group + market: HK + title: Momentum breakout + summary: RSI crossed above 70 ... + strategy_id: growth-momentum + strategy_name: Growth Momentum + expression: 992.HK:GROWTH:long + key_fact_id: technical_rsi_14_short_1783674041337603409 + key_catalyst: RSI Breakout + analysis_price: 10.5 + conservative_price: 11 + benchmark_price: 12.5 + optimistic_price: 14 + outlook: Bullish + outlook_desc: 看涨 + status: 1 + json_data: '{}' + created_at: '2026-06-01T09:30:00Z' + updated_at: '2026-06-01T09:30:00Z' + total: 1 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/signals/{signal_id}: + get: + operationId: signal + summary: Get Signal Detail + x-summary-zh: 获取信号详情 + x-summary-zh-hk: 獲取信號詳情 + description: | + Get one signal by ID, including the full analysis in `json_data`. + x-description-zh: | + 按 ID 获取单条信号,包含 `json_data` 中的完整分析。 + x-description-zh-hk: | + 按 ID 獲取單條信號,包含 `json_data` 中的完整分析。 + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 + tags: + - Quote + x-parameters: + - name: signal_id + in: path + type: string + required: true + description: Signal ID, e.g. `sign_992_1a00c9425c3_48ab`. + x-description-zh: 信号 ID,如 `sign_992_1a00c9425c3_48ab`。 + x-description-zh-hk: 信號 ID,如 `sign_992_1a00c9425c3_48ab`。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/signals/<signal_id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/signals/<signal_id>", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/signals/<signal_id>", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/signals/<signal_id>", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/signals/<signal_id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/signals/<signal_id>") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/signals/<signal_id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/signals/<signal_id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: signal + type: object + required: true + description: The signal record + x-description-zh: 信号记录 + x-description-zh-hk: 信號記錄 + - name: └ id + type: string + required: true + description: Signal ID + x-description-zh: 信号 ID + x-description-zh-hk: 信號 ID + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 标的代码 + x-description-zh-hk: 標的代碼 + - name: └ title + type: string + required: false + description: Signal headline + x-description-zh: 信号标题 + x-description-zh-hk: 信號標題 + - name: └ summary + type: string + required: false + description: Natural-language summary (Markdown) + x-description-zh: 自然语言摘要(Markdown) + x-description-zh-hk: 自然語言摘要(Markdown) + - name: └ outlook + type: string + required: false + description: 'Outlook: `Strong bullish`, `Bullish`, `Neutral`, `Bearish`, `Strong bearish`' + x-description-zh: 观点:`Strong bullish`、`Bullish`、`Neutral`、`Bearish`、`Strong bearish` + x-description-zh-hk: 觀點:`Strong bullish`、`Bullish`、`Neutral`、`Bearish`、`Strong bearish` + - name: └ json_data + type: string + required: false + description: Full strategy analysis as a JSON document, carried verbatim + x-description-zh: 完整策略分析(JSON 文档),原样透传 + x-description-zh-hk: 完整策略分析(JSON 文檔),原樣透傳 + - name: └ created_at + type: string + required: true + description: Creation time (RFC3339) + x-description-zh: 创建时间(RFC3339) + x-description-zh-hk: 建立時間(RFC3339) + - name: └ updated_at + type: string + required: true + description: Last update time (RFC3339) + x-description-zh: 最后更新时间(RFC3339) + x-description-zh-hk: 最後更新時間(RFC3339) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + signal: + id: sign_992_1a00c9425c3_48ab + symbol: 992.HK + title: Momentum breakout + summary: RSI crossed above 70 ... + outlook: Bullish + json_data: '{"core_conclusion":{"outlook_enum":2}}' + created_at: '2026-06-01T09:30:00Z' + updated_at: '2026-06-01T09:30:00Z' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/facts/security_facts: + get: + operationId: security_facts + summary: List Security Facts + x-summary-zh: 查询标的催化事实 + x-summary-zh-hk: 查詢標的催化事實 + description: | + List the fact (catalyst) events for one security — anomaly detections, factor readings, data sources and natural-language summaries. Facts are what strategies react to: a signal names the fact that triggered it in `key_fact_id`. + x-description-zh: | + 查询某只标的的催化事实(fact)事件——异常检测、因子读数、数据来源与自然语言摘要。事实是策略所响应的对象:信号会在 `key_fact_id` 中标明触发它的事实。 + x-description-zh-hk: | + 查詢某隻標的的催化事實(fact)事件——異常檢測、因子讀數、數據來源與自然語言摘要。事實是策略所響應的對象:信號會在 `key_fact_id` 中標明觸發它的事實。 + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: The security to query, e.g. `AAPL.US` or `700.HK`. Required. + x-description-zh: 要查询的标的,如 `AAPL.US` 或 `700.HK`。必填。 + x-description-zh-hk: 要查詢的標的,如 `AAPL.US` 或 `700.HK`。必填。 + - name: begin_time + in: query + type: string + required: false + description: Start of the query window (RFC3339). If omitted, includes the earliest available data. + x-description-zh: 查询窗口起点(RFC3339)。省略则包含最早可用数据。 + x-description-zh-hk: 查詢窗口起點(RFC3339)。省略則包含最早可用數據。 + - name: end_time + in: query + type: string + required: false + description: End of the query window (RFC3339). If omitted, returns the latest data. + x-description-zh: 查询窗口终点(RFC3339)。省略则返回最新数据。 + x-description-zh-hk: 查詢窗口終點(RFC3339)。省略則返回最新數據。 + - name: limit + in: query + type: integer + required: false + description: Maximum number of facts to return. When more facts fall inside the range, only the latest `limit` are returned. Defaults to 100. + x-description-zh: 返回事实的最大数量。区间内事实更多时,仅返回最新的 `limit` 条。默认 100。 + x-description-zh-hk: 返回事實的最大數量。區間內事實更多時,僅返回最新的 `limit` 條。預設 100。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/facts/security_facts?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/facts/security_facts", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/facts/security_facts", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/facts/security_facts") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/facts/security_facts?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/facts/security_facts") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/facts/security_facts?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/facts/security_facts?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: facts + type: object[] + required: false + description: Fact (catalyst) events for the security + x-description-zh: 标的的催化事实事件 + x-description-zh-hk: 標的的催化事實事件 + - name: └ fact_id + type: string + required: false + description: Fact ID, e.g. `technical_rsi_14_short_1783674041337603409` + x-description-zh: 事实 ID,如 `technical_rsi_14_short_1783674041337603409` + x-description-zh-hk: 事實 ID,如 `technical_rsi_14_short_1783674041337603409` + - name: └ fact_type + type: string + required: false + description: 'Kind of fact: `News`, `Fundamental`, `Technical`' + x-description-zh: 事实类型:`News`、`Fundamental`、`Technical` + x-description-zh-hk: 事實類型:`News`、`Fundamental`、`Technical` + - name: └ occur_time + type: string + required: false + description: When the fact occurred (RFC3339) + x-description-zh: 事实发生时间(RFC3339) + x-description-zh-hk: 事實發生時間(RFC3339) + - name: └ direction + type: string + required: false + description: 'Side the fact points to: `long`, `short`, `neutral`' + x-description-zh: 事实方向:`long`、`short`、`neutral` + x-description-zh-hk: 事實方向:`long`、`short`、`neutral` + - name: └ data_source + type: object[] + required: false + description: Where the fact came from (`source_name`, `type`, `url`, `icon`) + x-description-zh: 事实来源(`source_name`、`type`、`url`、`icon`) + x-description-zh-hk: 事實來源(`source_name`、`type`、`url`、`icon`) + - name: └ ∟ type + type: string + required: false + description: '`"platform"` for preset strategies' + x-description-zh: '`"platform"` 表示平台预设策略' + x-description-zh-hk: '`"platform"` 表示平台預設策略' + - name: └ ∟ source_name + type: string + required: false + description: Data source name. + x-description-zh: 数据来源名称。 + x-description-zh-hk: 數據來源名稱。 + - name: └ ∟ url + type: string + required: false + description: Original image URL + x-description-zh: 原始图片 URL + x-description-zh-hk: 原始圖片 URL + - name: └ ∟ icon + type: string + required: false + description: Icon URL + x-description-zh: 图标链接 + x-description-zh-hk: 圖標鏈接 + - name: └ nl_info + type: object + required: false + description: Natural-language rendering of the fact (`title`, `sub_title`, `summary`, `invest_anal`, `eli_explain`) + x-description-zh: 事实的自然语言呈现(`title`、`sub_title`、`summary`、`invest_anal`、`eli_explain`) + x-description-zh-hk: 事實的自然語言呈現(`title`、`sub_title`、`summary`、`invest_anal`、`eli_explain`) + - name: └ ∟ title + type: string + required: false + description: Topic title + x-description-zh: 标题 + x-description-zh-hk: 標題 + - name: └ ∟ sub_title + type: string + required: false + description: Sub-title. + x-description-zh: 副标题。 + x-description-zh-hk: 副標題。 + - name: └ ∟ summary + type: string + required: false + description: Natural-language summary of the signal, in Markdown + x-description-zh: 信号的自然语言摘要(Markdown) + x-description-zh-hk: 信號的自然語言摘要(Markdown) + - name: └ ∟ invest_anal + type: string + required: false + description: Investment analysis. + x-description-zh: 投资分析。 + x-description-zh-hk: 投資分析。 + - name: └ ∟ eli_explain + type: string + required: false + description: Plain-language explanation. + x-description-zh: 通俗解释。 + x-description-zh-hk: 通俗解釋。 + - name: └ symbols_info + type: object[] + required: false + description: Securities the fact is about (`symbol`, `security_name`) + x-description-zh: 事实涉及的标的(`symbol`、`security_name`) + x-description-zh-hk: 事實涉及的標的(`symbol`、`security_name`) + - name: └ ∟ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ ∟ security_name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ factors + type: object[] + required: false + description: Factors that contributed to the fact (`name`, `factor_groups`, `long_short_direction`, `trigger_condition`, `anomaly_detection`) + x-description-zh: 构成该事实的因子(`name`、`factor_groups`、`long_short_direction`、`trigger_condition`、`anomaly_detection`) + x-description-zh-hk: 構成該事實的因子(`name`、`factor_groups`、`long_short_direction`、`trigger_condition`、`anomaly_detection`) + - name: └ ∟ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ ∟ factor_groups + type: array + required: false + description: Factor groups. + x-description-zh: 因子分组。 + x-description-zh-hk: 因子分組。 + - name: └ ∟ long_short_direction + type: string + required: false + description: Long/short direction. + x-description-zh: 多空方向。 + x-description-zh-hk: 多空方向。 + - name: └ ∟ anomaly_detection + type: object + required: false + description: Anomaly detection info. + x-description-zh: 异动检测信息。 + x-description-zh-hk: 異動檢測信息。 + - name: └ ∟ ∟ test_method + type: string + required: false + description: Test method. + x-description-zh: 检验方法。 + x-description-zh-hk: 檢驗方法。 + - name: └ ∟ ∟ anomaly_result + type: string + required: false + description: Anomaly detection result. + x-description-zh: 异动检测结果。 + x-description-zh-hk: 異動檢測結果。 + - name: └ ∟ ∟ thresholds + type: object + required: false + description: Threshold values. + x-description-zh: 阈值。 + x-description-zh-hk: 閾值。 + - name: └ ∟ ∟ ∟ low + type: string + required: false + description: 5-year low + x-description-zh: 5 年最低值 + x-description-zh-hk: 5 年最低值 + - name: └ ∟ ∟ ∟ medium + type: string + required: false + description: Medium bound. + x-description-zh: 中位区间。 + x-description-zh-hk: 中位區間。 + - name: └ ∟ ∟ ∟ high + type: string + required: false + description: 5-year high + x-description-zh: 5 年最高值 + x-description-zh-hk: 5 年最高值 + - name: └ ∟ ∟ significance_level + type: string + required: false + description: Statistical significance level. + x-description-zh: 显著性水平。 + x-description-zh-hk: 顯著性水平。 + - name: └ ∟ trigger_condition + type: string + required: false + description: Trigger condition. + x-description-zh: 触发条件。 + x-description-zh-hk: 觸發條件。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + facts: + - fact_id: technical_rsi_14_short_1783674041337603409 + fact_type: Technical + direction: short + occur_time: '2026-06-01T00:00:00Z' + symbols_info: + - symbol: AAPL.US + security_name: Apple + factors: + - name: rsi_14 + factor_groups: + - MOMENTUM + long_short_direction: short + trigger_condition: RSI > 70 + anomaly_detection: + anomaly_result: '' + significance_level: '' + test_method: '' + thresholds: + low: '' + medium: '' + high: '' + data_source: + - source_name: Nasdaq + type: Technical + url: '' + icon: '' + nl_info: + title: RSI overbought + sub_title: '' + summary: '[]' + invest_anal: '[]' + eli_explain: '[]' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/etf-asset-allocation: + get: + operationId: etf_asset_allocation + summary: ETF Asset Allocation + x-summary-zh: ETF 资产配置 + x-summary-zh-hk: ETF 資產配置 + description: | + Get ETF asset allocation, grouped by element type: holdings, regional, asset class and industry. + x-description-zh: | + 获取 ETF 资产配置,按元素类型分组:持仓、地区、资产类别与行业。 + x-description-zh-hk: | + 獲取 ETF 資產配置,按元素類型分組:持倉、地區、資產類別與行業。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: ETF symbol, e.g. `QQQ.US`. + x-description-zh: ETF 代码,如 `QQQ.US`。 + x-description-zh-hk: ETF 代碼,如 `QQQ.US`。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/etf-asset-allocation?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/etf-asset-allocation", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/etf-asset-allocation", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/etf-asset-allocation") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/etf-asset-allocation?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/etf-asset-allocation") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/etf-asset-allocation?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/etf-asset-allocation?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: info + type: object[] + required: false + description: Asset allocation groups (one per element type) + x-description-zh: 资产配置分组(每种元素类型一组) + x-description-zh-hk: 資產配置分組(每種元素類型一組) + - name: └ report_date + type: string + required: false + description: Report date, e.g. `20260601` + x-description-zh: 报告日期,如 `20260601` + x-description-zh-hk: 報告日期,如 `20260601` + - name: └ asset_type + type: integer + required: false + description: Element type of this group (holdings / regional / asset class / industry) + x-description-zh: 本组的元素类型(持仓 / 地区 / 资产类别 / 行业) + x-description-zh-hk: 本組的元素類型(持倉 / 地區 / 資產類別 / 行業) + - name: └ lists + type: object[] + required: false + description: Elements in this group + x-description-zh: 本组的元素列表 + x-description-zh-hk: 本組的元素列表 + - name: └ ∟ name + type: string + required: false + description: Element name + x-description-zh: 元素名称 + x-description-zh-hk: 元素名稱 + - name: └ ∟ position_ratio + type: string + required: false + description: Position ratio, e.g. `0.0861114` + x-description-zh: 持仓占比,如 `0.0861114` + x-description-zh-hk: 持倉佔比,如 `0.0861114` + - name: └ ∟ name_locales + type: string + required: false + description: Localized name (raw string; see `name_locales_map` for the locale map). + x-description-zh: 本地化名称(原始字符串,语言映射见 `name_locales_map`)。 + x-description-zh-hk: 本地化名稱(原始字符串,語言映射見 `name_locales_map`)。 + - name: └ ∟ name_locales_map + type: object + required: false + description: Localized names (locale → name, e.g. `zh-CN` → `英伟达`) + x-description-zh: 本地化名称(locale → 名称,如 `zh-CN` → `英伟达`) + x-description-zh-hk: 本地化名稱(locale → 名稱,如 `zh-CN` → `英偉達`) + - name: └ ∟ ∟ en + type: string + required: false + description: English name. + x-description-zh: 英文名称。 + x-description-zh-hk: 英文名稱。 + - name: └ ∟ ∟ zh-CN + type: string + required: false + description: Simplified Chinese name. + x-description-zh: 简体中文名称。 + x-description-zh-hk: 簡體中文名稱。 + - name: └ ∟ ∟ zh-HK + type: string + required: false + description: Traditional Chinese name. + x-description-zh: 繁体中文名称。 + x-description-zh-hk: 繁體中文名稱。 + - name: └ ∟ code + type: string + required: false + description: Security code (holdings only), e.g. `NVDA` + x-description-zh: 证券代码(仅持仓),如 `NVDA` + x-description-zh-hk: 證券代碼(僅持倉),如 `NVDA` + - name: └ ∟ symbol + type: string + required: false + description: Security symbol (holdings only), e.g. `NVDA.US` + x-description-zh: 证券代码带市场后缀(仅持仓),如 `NVDA.US` + x-description-zh-hk: 證券代碼帶市場後綴(僅持倉),如 `NVDA.US` + - name: └ ∟ holding_detail + type: string + required: false + description: Holding detail (holdings only) + x-description-zh: 持仓明细(仅持仓) + x-description-zh-hk: 持倉明細(僅持倉) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + info: + - report_date: '20260601' + asset_type: holdings + lists: + - name: NVIDIA + code: NVDA + position_ratio: '0.0861114' + symbol: NVDA.US + name_locales_map: + zh-CN: 英伟达 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/ai/screener/indicators: + get: + operationId: screener_indicators + summary: Screener Indicators + x-summary-zh: 选股指标 + x-summary-zh-hk: 選股指標 + description: | + Get all indicator definitions supported by the [stock screener](https://longbridge.com/screener), including keys, names, units, and available ranges. Use these to build custom filter conditions. + x-description-zh: | + 获取[选股器](https://longbridge.com/screener) 支持的所有指标定义,包含键值、名称、单位和可用范围,可用于构建自定义筛选条件。 + x-description-zh-hk: | + 獲取[選股器](https://longbridge.com/screener) 支持的所有指標定義,包含鍵值、名稱、單位和可用範圍,可用於構建自定義篩選條件。 + tags: + - Screener + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge screener indicators + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/ai/screener/indicators' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/indicators", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/indicators", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/ai/screener/indicators", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ai/screener/indicators")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/ai/screener/indicators") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ai/screener/indicators"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/ai/screener/indicators\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: groups + type: object[] + required: false + description: Indicator groups + x-description-zh: 指标分组 + x-description-zh-hk: 指標分組 + - name: └ group_type + type: string + required: false + description: Group type. + x-description-zh: 分组类型。 + x-description-zh-hk: 分組類型。 + - name: └ group_name + type: string + required: false + description: Group name + x-description-zh: 分组名称 + x-description-zh-hk: 分組名稱 + - name: └ indicators + type: object[] + required: false + description: Indicators in this group + x-description-zh: 该分组下的指标 + x-description-zh-hk: 該分組下的指標 + - name: └ ∟ id + type: integer + required: false + description: Indicator ID (string) + x-description-zh: 指标 ID(字符串类型) + x-description-zh-hk: 指標 ID(字符串類型) + - name: └ ∟ key + type: string + required: false + description: Indicator key, e.g. `filter_pettm`. + x-description-zh: 指标键名,如 `filter_pettm`。 + x-description-zh-hk: 指標鍵名,如 `filter_pettm`。 + - name: └ ∟ name + type: string + required: false + description: Indicator display name + x-description-zh: 指标显示名称 + x-description-zh-hk: 指標顯示名稱 + - name: └ ∟ places + type: integer + required: false + description: Number of decimal places. + x-description-zh: 小数位数。 + x-description-zh-hk: 小數位數。 + - name: └ ∟ category + type: integer + required: false + description: Company category + x-description-zh: 公司类别 + x-description-zh-hk: 公司類別 + - name: └ ∟ tech_indicators + type: array + required: false + description: Technical indicator options. + x-description-zh: 技术指标选项。 + x-description-zh-hk: 技術指標選項。 + - name: └ ∟ default_range + type: object + required: false + description: Default value range. + x-description-zh: 默认取值区间。 + x-description-zh-hk: 默認取值區間。 + - name: └ ∟ unit + type: string + required: false + description: Unit (e.g. `%`, `亿`) + x-description-zh: 单位(如 `%`、` 亿`) + x-description-zh-hk: 單位(如 `%`、` 億`) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + groups: + - group_name: 公司规模与财务 + indicators: + - id: '1' + key: marketcap + name: 市值 + unit: 亿 + min: null + max: null + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/ai/screener/strategies/recommend: + get: + operationId: screener_recommend_strategies + summary: Preset Screener Strategies + x-summary-zh: 预设选股策略 + x-summary-zh-hk: 預設選股策略 + description: |+ + Get the list of platform-preset stock screener strategies, including recent average daily change and constituent stocks. + + x-description-zh: | + 获取平台预设的选股策略列表,含近期平均日涨跌幅和策略内股票。 + x-description-zh-hk: | + 獲取平台預設的選股策略列表,含近期平均日漲跌幅和策略內股票。 + tags: + - Screener + x-parameters: + - name: market + in: query + type: string + required: false + description: 'Market filter: `US`, `HK`, `CN`, `SG`. Default: `US`' + x-description-zh: 市场筛选:`US`、`HK`、`CN`、`SG`,默认 `US` + x-description-zh-hk: 市場篩選:`US`、`HK`、`CN`、`SG`,默認 `US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge screener strategies + longbridge screener strategies --market HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/ai/screener/strategies/recommend\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: strategys + type: object[] + required: false + description: Strategy list + x-description-zh: 策略列表 + x-description-zh-hk: 策略列表 + - name: └ id + type: integer + required: false + description: Strategy ID (pass to `screener_strategy` or `screener_search`) + x-description-zh: 策略 ID(传入 `screener_strategy` 或 `screener_search`) + x-description-zh-hk: 策略 ID(傳入 `screener_strategy` 或 `screener_search`) + - name: └ name + type: string + required: false + description: Strategy name + x-description-zh: 策略名称 + x-description-zh-hk: 策略名稱 + - name: └ type + type: string + required: false + description: '`"platform"` for preset strategies' + x-description-zh: '`"platform"` 表示平台预设策略' + x-description-zh-hk: '`"platform"` 表示平台預設策略' + - name: └ market + type: string + required: false + description: Target market (e.g. `"US"`, `"HK"`) + x-description-zh: 目标市场(如 `"US"`、`"HK"`) + x-description-zh-hk: 目標市場(如 `"US"`、`"HK"`) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + strategys: + - id: 19 + name: 今日大涨股票 + type: platform + market: US + - id: 20 + name: 今年增长冠军 + type: platform + market: US + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/ai/screener/strategies/mine: + get: + operationId: screener_user_strategies + summary: My Screener Strategies + x-summary-zh: 我的选股策略 + x-summary-zh-hk: 我的選股策略 + description: |+ + Get the list of custom stock screener strategies created by the currently logged-in user. + + x-description-zh: | + 获取当前登录用户创建的自定义选股策略列表。 + x-description-zh-hk: | + 獲取當前登錄用戶創建的自定義選股策略列表。 + tags: + - Screener + x-parameters: + - name: market + in: query + type: string + required: false + description: 'Market filter: `US`, `HK`, `CN`, `SG`. Default: `US`' + x-description-zh: 市场筛选:`US`、`HK`、`CN`、`SG`,默认 `US` + x-description-zh-hk: 市場篩選:`US`、`HK`、`CN`、`SG`,默認 `US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge screener strategies --mine + longbridge screener strategies --mine --market HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/ai/screener/strategies/mine\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + strategys: + - id: 42 + name: My Growth Strategy + type: user + market: US + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/ai/screener/strategy/{id}: + get: + operationId: screener_strategy + summary: Screener Strategy Detail + x-summary-zh: 选股策略详情 + x-summary-zh-hk: 選股策略詳情 + description: | + Get the full configuration of a single stock screener strategy by strategy ID, including all indicator groups and the filter range for each indicator. + (strategy ID as path parameter) + x-description-zh: | + 根据策略 ID 获取单个选股策略的完整配置,包含所有指标分组和各指标的筛选范围。 + x-description-zh-hk: | + 根據策略 ID 獲取單個選股策略的完整配置,包含所有指標分組和各指標的篩選範圍。 + tags: + - Screener + x-parameters: + - name: id + in: path + type: integer + required: true + description: Strategy ID from `screener_recommend_strategies` or `screener_user_strategies` + x-description-zh: 策略 ID,来自 `screener_recommend_strategies` 或 `screener_user_strategies` + x-description-zh-hk: 策略 ID,來自 `screener_recommend_strategies` 或 `screener_user_strategies` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge screener run 42 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/ai/screener/strategy/<id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: id + type: integer + required: false + description: Strategy ID + x-description-zh: 策略 ID + x-description-zh-hk: 策略 ID + - name: market + type: string + required: false + description: Target market + x-description-zh: 目标市场 + x-description-zh-hk: 目標市場 + - name: filter + type: object + required: false + description: Filter configuration + x-description-zh: 筛选配置 + x-description-zh-hk: 篩選配置 + - name: └ filters + type: object[] + required: false + description: Filter conditions + x-description-zh: 筛选条件列表 + x-description-zh-hk: 篩選條件列表 + - name: └ ∟ key + type: string + required: false + description: Indicator key, e.g. `filter_pettm`. + x-description-zh: 指标键名,如 `filter_pettm`。 + x-description-zh-hk: 指標鍵名,如 `filter_pettm`。 + - name: └ ∟ min + type: string + required: false + description: Lower bound + x-description-zh: 下限 + x-description-zh-hk: 下限 + - name: └ ∟ max + type: string + required: false + description: Upper bound + x-description-zh: 上限 + x-description-zh-hk: 上限 + - name: └ ∟ tech_values + type: object + required: false + description: Technical indicator params + x-description-zh: 技术指标参数 + x-description-zh-hk: 技術指標參數 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + id: 19 + name: 今日大涨股票 + market: US + type: platform + filter: + filters: + - key: prevchg + min: '2' + max: '' + tech_values: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/short-positions/hk: + get: + operationId: short_positions_hk + summary: Short Positions (HK) + x-summary-zh: 沽空数据(港股) + x-summary-zh-hk: 沽空數據(港股) + description: | + Get short interest data for US or HK securities. Market is auto-detected from the symbol suffix: `.HK` → HKEX short position data (daily); others → US FINRA short interest data (bi-monthly). + x-description-zh: | + 获取美股或港股沽空持仓数据。市场根据代码后缀自动识别:`.HK` → 港交所沽空数据(每日更新);其他 → 美股 FINRA 沽空数据(双月更新)。 + x-description-zh-hk: | + 獲取美股或港股沽空持倉數據。市場根據代碼後綴自動識別:`.HK` → 港交所沽空數據(每日更新);其他 → 美股 FINRA 沽空數據(雙月更新)。 + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `TSLA.US` or `700.HK` + x-description-zh: 证券代码,例如 `TSLA.US` 或 `700.HK` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` 或 `700.HK` + - name: count + in: query + type: integer + required: false + description: 'Number of records to return (1–100, default: 20)' + x-description-zh: 返回记录数(1–100,默认 20) + x-description-zh-hk: 返回記錄數(1–100,默認 20) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge short-positions TSLA.US + longbridge short-positions 700.HK + longbridge short-positions AAPL.US --count 50 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/short-positions/hk?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/short-positions/hk", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/short-positions/hk", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/short-positions/hk") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/short-positions/hk?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/short-positions/hk") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/short-positions/hk?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/short-positions/hk?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: symbol + type: string + required: false + description: Security symbol, e.g. `700.HK`. + x-description-zh: 标的代码,如 `700.HK`。 + x-description-zh-hk: 標的代碼,如 `700.HK`。 + - name: update_timestamp + type: string + required: false + description: Last update time (Unix seconds). + x-description-zh: 最后更新时间(Unix 秒)。 + x-description-zh-hk: 最後更新時間(Unix 秒)。 + - name: data + type: object[] + required: false + description: Daily HKEX short-position records. + x-description-zh: 港交所每日沽空持仓记录。 + x-description-zh-hk: 港交所每日沽空持倉記錄。 + - name: └ timestamp + type: string + required: false + description: Record date (Unix seconds). + x-description-zh: 记录日期(Unix 秒)。 + x-description-zh-hk: 記錄日期(Unix 秒)。 + - name: └ amount + type: string + required: false + description: Short-sold shares that day. + x-description-zh: 当日沽空股数。 + x-description-zh-hk: 當日沽空股數。 + - name: └ balance + type: string + required: false + description: Outstanding short-position value. + x-description-zh: 沽空持仓金额。 + x-description-zh-hk: 沽空持倉金額。 + - name: └ rate + type: string + required: false + description: Short-position ratio. + x-description-zh: 沽空比率。 + x-description-zh-hk: 沽空比率。 + - name: └ cost + type: string + required: false + description: Average short-position price. + x-description-zh: 沽空平均价。 + x-description-zh-hk: 沽空平均價。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + - timestamp: '2022-03-15T04:00:00Z' + current_shares_short: '111286790' + avg_daily_share_volume: '95077016' + days_to_cover: '1.17' + rate: '0.0068' + close: '' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/short-positions/us: + get: + operationId: short_positions_us + summary: Short Positions (US) + x-summary-zh: 沽空数据(美股) + x-summary-zh-hk: 沽空數據(美股) + description: | + Get short interest data for US or HK securities. Market is auto-detected from the symbol suffix: `.HK` → HKEX short position data (daily); others → US FINRA short interest data (bi-monthly). + x-description-zh: | + 获取美股或港股沽空持仓数据。市场根据代码后缀自动识别:`.HK` → 港交所沽空数据(每日更新);其他 → 美股 FINRA 沽空数据(双月更新)。 + x-description-zh-hk: | + 獲取美股或港股沽空持倉數據。市場根據代碼後綴自動識別:`.HK` → 港交所沽空數據(每日更新);其他 → 美股 FINRA 沽空數據(雙月更新)。 + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `TSLA.US` or `700.HK` + x-description-zh: 证券代码,例如 `TSLA.US` 或 `700.HK` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` 或 `700.HK` + - name: count + in: query + type: integer + required: false + description: 'Number of records to return (1–100, default: 20)' + x-description-zh: 返回记录数(1–100,默认 20) + x-description-zh-hk: 返回記錄數(1–100,默認 20) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge short-positions TSLA.US + longbridge short-positions 700.HK + longbridge short-positions AAPL.US --count 50 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/short-positions/us?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/short-positions/us", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/short-positions/us", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/short-positions/us") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/short-positions/us?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/short-positions/us") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/short-positions/us?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/short-positions/us?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: symbol + type: string + required: false + description: Security symbol, e.g. `AAPL.US`. + x-description-zh: 标的代码,如 `AAPL.US`。 + x-description-zh-hk: 標的代碼,如 `AAPL.US`。 + - name: data + type: object[] + required: false + description: Bi-monthly FINRA short-interest records. + x-description-zh: FINRA 双月沽空数据记录。 + x-description-zh-hk: FINRA 雙月沽空數據記錄。 + - name: └ timestamp + type: string + required: false + description: Settlement date (Unix seconds). + x-description-zh: 结算日期(Unix 秒)。 + x-description-zh-hk: 結算日期(Unix 秒)。 + - name: └ rate + type: string + required: false + description: Short interest as a fraction of float. + x-description-zh: 沽空占流通股比率。 + x-description-zh-hk: 沽空佔流通股比率。 + - name: └ avg_daily_share_volume + type: string + required: false + description: Average daily share volume. + x-description-zh: 日均成交股数。 + x-description-zh-hk: 日均成交股數。 + - name: └ current_shares_short + type: string + required: false + description: Current shares sold short. + x-description-zh: 当前沽空股数。 + x-description-zh-hk: 當前沽空股數。 + - name: └ days_to_cover + type: string + required: false + description: Days to cover (short interest / avg volume). + x-description-zh: 回补天数(沽空量 / 日均量)。 + x-description-zh-hk: 回補天數(沽空量 / 日均量)。 + - name: └ close + type: string + required: false + description: Closing price. + x-description-zh: 收盘价。 + x-description-zh-hk: 收盤價。 + - name: sources + type: integer + required: false + description: Number of contributing data sources. + x-description-zh: 数据来源数量。 + x-description-zh-hk: 數據來源數量。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + - timestamp: '2022-03-15T04:00:00Z' + current_shares_short: '111286790' + avg_daily_share_volume: '95077016' + days_to_cover: '1.17' + rate: '0.0068' + close: '' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/short-trades/hk: + get: + operationId: short_trades_hk + summary: Daily Short Sale Volume (HK) + x-summary-zh: 每日沽空成交量(港股) + x-summary-zh-hk: 每日沽空成交量(港股) + description: | + Get daily short sale volume data for a security. Supports US stocks (FINRA/NASDAQ) and HK stocks (HKEX). US data is updated bi-weekly; HK data is updated each trading day. + x-description-zh: | + 获取个股每日沽空成交量数据,支持美股(FINRA)和港股(HKEX)。美股数据每两周更新一次,港股数据每个交易日更新。 + x-description-zh-hk: | + 獲取個股每日沽空成交量數據,支持美股(FINRA)和港股(HKEX)。美股數據每兩週更新一次,港股數據每個交易日更新。 + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol; supports US (e.g. `TSLA.US`) and HK (e.g. `700.HK`) + x-description-zh: 证券代码,支持美股(如 `TSLA.US`)和港股(如 `700.HK`) + x-description-zh-hk: 證券代碼,支持美股(如 `TSLA.US`)和港股(如 `700.HK`) + - name: count + in: query + type: integer + required: false + description: Number of records to return (1–100, default 20) + x-description-zh: 返回记录数(1–100,默认 20) + x-description-zh-hk: 返回記錄數(1–100,默認 20) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge short-trades TSLA.US + longbridge short-trades 700.HK --count 30 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/short-trades/hk?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/short-trades/hk", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/short-trades/hk", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/short-trades/hk") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/short-trades/hk?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/short-trades/hk") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/short-trades/hk?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/short-trades/hk?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: symbol + type: string + required: false + description: Security symbol, e.g. `700.HK`. + x-description-zh: 标的代码,如 `700.HK`。 + x-description-zh-hk: 標的代碼,如 `700.HK`。 + - name: data + type: object[] + required: false + description: Daily HKEX short-sale records. + x-description-zh: 港交所每日沽空成交记录。 + x-description-zh-hk: 港交所每日沽空成交記錄。 + - name: └ timestamp + type: string + required: false + description: Trade date (Unix seconds). + x-description-zh: 成交日期(Unix 秒)。 + x-description-zh-hk: 成交日期(Unix 秒)。 + - name: └ amount + type: string + required: false + description: Short-sale turnover that day. + x-description-zh: 当日沽空成交额。 + x-description-zh-hk: 當日沽空成交額。 + - name: └ balance + type: string + required: false + description: Short-sale balance. + x-description-zh: 沽空余额。 + x-description-zh-hk: 沽空餘額。 + - name: └ close + type: string + required: false + description: Closing price. + x-description-zh: 收盘价。 + x-description-zh-hk: 收盤價。 + - name: └ rate + type: string + required: false + description: Short-sale ratio. + x-description-zh: 沽空比率。 + x-description-zh-hk: 沽空比率。 + - name: └ total_amount + type: string + required: false + description: Total market turnover. + x-description-zh: 总成交额。 + x-description-zh-hk: 總成交額。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + - timestamp: '2026-05-15T04:00:00Z' + nus_amount: '5748485' + ny_amount: '0' + total_amount: '15778974' + rate: '0.3643' + close: '300.230' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/short-trades/us: + get: + operationId: short_trades_us + summary: Daily Short Sale Volume (US) + x-summary-zh: 每日沽空成交量(美股) + x-summary-zh-hk: 每日沽空成交量(美股) + description: | + Get daily short sale volume data for a security. Supports US stocks (FINRA/NASDAQ) and HK stocks (HKEX). US data is updated bi-weekly; HK data is updated each trading day. + x-description-zh: | + 获取个股每日沽空成交量数据,支持美股(FINRA)和港股(HKEX)。美股数据每两周更新一次,港股数据每个交易日更新。 + x-description-zh-hk: | + 獲取個股每日沽空成交量數據,支持美股(FINRA)和港股(HKEX)。美股數據每兩週更新一次,港股數據每個交易日更新。 + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol; supports US (e.g. `TSLA.US`) and HK (e.g. `700.HK`) + x-description-zh: 证券代码,支持美股(如 `TSLA.US`)和港股(如 `700.HK`) + x-description-zh-hk: 證券代碼,支持美股(如 `TSLA.US`)和港股(如 `700.HK`) + - name: count + in: query + type: integer + required: false + description: Number of records to return (1–100, default 20) + x-description-zh: 返回记录数(1–100,默认 20) + x-description-zh-hk: 返回記錄數(1–100,默認 20) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge short-trades TSLA.US + longbridge short-trades 700.HK --count 30 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/short-trades/us?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/short-trades/us", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/short-trades/us", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/short-trades/us") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/short-trades/us?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/short-trades/us") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/short-trades/us?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/short-trades/us?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: symbol + type: string + required: false + description: Security symbol, e.g. `AAPL.US`. + x-description-zh: 标的代码,如 `AAPL.US`。 + x-description-zh-hk: 標的代碼,如 `AAPL.US`。 + - name: data + type: object[] + required: false + description: Daily US short-sale volume records. + x-description-zh: 美股每日沽空成交量记录。 + x-description-zh-hk: 美股每日沽空成交量記錄。 + - name: └ timestamp + type: string + required: false + description: Trade date (Unix seconds). + x-description-zh: 成交日期(Unix 秒)。 + x-description-zh-hk: 成交日期(Unix 秒)。 + - name: └ nus_amount + type: string + required: false + description: Non-exchange (off-exchange) short volume. + x-description-zh: 场外沽空量。 + x-description-zh-hk: 場外沽空量。 + - name: └ total_amount + type: string + required: false + description: Total short volume. + x-description-zh: 总沽空量。 + x-description-zh-hk: 總沽空量。 + - name: └ ny_amount + type: string + required: false + description: NYSE short volume. + x-description-zh: 纽交所沽空量。 + x-description-zh-hk: 紐交所沽空量。 + - name: └ close + type: string + required: false + description: Closing price. + x-description-zh: 收盘价。 + x-description-zh-hk: 收盤價。 + - name: └ rate + type: string + required: false + description: Short-volume ratio. + x-description-zh: 沽空量占比。 + x-description-zh-hk: 沽空量佔比。 + - name: sources + type: integer + required: false + description: Number of contributing data sources. + x-description-zh: 数据来源数量。 + x-description-zh-hk: 數據來源數量。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + - timestamp: '2026-05-15T04:00:00Z' + nus_amount: '5748485' + ny_amount: '0' + total_amount: '15778974' + rate: '0.3643' + close: '300.230' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/ahpremium/klines: + get: + operationId: ah_premium + summary: A/H Premium + x-summary-zh: A/H 溢价 + x-summary-zh-hk: A/H 溢價 + description: | + Get the A/H premium ratio for dual-listed stocks comparing A-share and H-share prices. + x-description-zh: | + 获取 A+H 两地上市股票的 A/H 溢价比率,对比 A 股和 H 股价格。 + x-description-zh-hk: | + 獲取 A+H 兩地上市股票的 A/H 溢價比率,對比 A 股和 H 股價格。 + x-subgroup: Market Data + x-subgroup-zh: 市场数据 + x-subgroup-zh-hk: 市場數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `600519.SH` or `000001.SZ`. + x-description-zh: 标的代码,如 `600519.SH` 或 `000001.SZ`。 + x-description-zh-hk: 標的代碼,如 `600519.SH` 或 `000001.SZ`。 + - name: line_type + in: query + type: string + required: true + description: Candlestick period, e.g. `day`, `week`, `month`. + x-description-zh: K 线周期,如 `day`、`week`、`month`。 + x-description-zh-hk: K 線週期,如 `day`、`week`、`month`。 + - name: line_num + in: query + type: integer + required: true + description: Number of candlesticks to return. + x-description-zh: 返回的 K 线数量。 + x-description-zh-hk: 返回的 K 線數量。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge ah-premium 939.HK + longbridge ah-premium 0939.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/ahpremium/klines?symbol=<symbol>&line_type=<line_type>&line_num=<line_num>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/ahpremium/klines", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "line_type": "<line_type>", "line_num": "<line_num>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/ahpremium/klines", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "line_type": "<line_type>", "line_num": "<line_num>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/ahpremium/klines") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("line_type", "<line_type>") + url.searchParams.set("line_num", "<line_num>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ahpremium/klines?symbol=<symbol>&line_type=<line_type>&line_num=<line_num>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/ahpremium/klines") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("line_type", "<line_type>"), ("line_num", "<line_num>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ahpremium/klines?symbol=<symbol>&line_type=<line_type>&line_num=<line_num>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/ahpremium/klines?symbol=<symbol>&line_type=<line_type>&line_num=<line_num>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: klines + type: object[] + required: true + description: A/H premium daily kline records + x-description-zh: A/H 溢价日 K 线记录 + x-description-zh-hk: A/H 溢價日 K 線記錄 + - name: └ timestamp + type: string + required: false + description: Unix timestamp + x-description-zh: Unix 时间戳 + x-description-zh-hk: Unix 時間戳 + - name: └ ahpremium_rate + type: string + required: false + description: A/H premium rate + x-description-zh: A/H 溢价率 + x-description-zh-hk: A/H 溢價率 + - name: └ aprice + type: string + required: false + description: A-share price (CNY) + x-description-zh: A 股价格(CNY) + x-description-zh-hk: A 股價格(CNY) + - name: └ apreclose + type: string + required: false + description: A-share previous close (CNY) + x-description-zh: A 股昨收价(CNY) + x-description-zh-hk: A 股昨收價(CNY) + - name: └ hprice + type: string + required: false + description: H-share price (HKD) + x-description-zh: H 股价格(HKD) + x-description-zh-hk: H 股價格(HKD) + - name: └ hpreclose + type: string + required: false + description: H-share previous close (HKD) + x-description-zh: H 股昨收价(HKD) + x-description-zh-hk: H 股昨收價(HKD) + - name: └ currency_rate + type: string + required: false + description: CNH/HKD exchange rate + x-description-zh: CNH/HKD 汇率 + x-description-zh-hk: CNH/HKD 匯率 + - name: └ price_spread + type: string + required: false + description: Price spread + x-description-zh: 价差 + x-description-zh-hk: 價差 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + klines: + - ahpremium_rate: '0.1523' + apreclose: '24.80' + aprice: '25.10' + currency_rate: '0.8920' + hpreclose: '19.20' + hprice: '19.50' + price_spread: '1.23' + timestamp: '1778198400' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/ahpremium/timeshares: + get: + operationId: ah_premium_intraday + summary: A/H Premium Intraday + x-summary-zh: A/H 溢价盘中数据 + x-summary-zh-hk: A/H 溢價盤中數據 + description: | + Get intraday A/H premium timeseries data for a dual-listed security. + x-description-zh: | + 获取两地上市证券的盘中 A/H 溢价时间序列数据。 + x-description-zh-hk: | + 獲取兩地上市證券的盤中 A/H 溢價時間序列數據。 + x-subgroup: Market Data + x-subgroup-zh: 市场数据 + x-subgroup-zh-hk: 市場數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: HK-side symbol of a dual-listed stock, e.g. `939.HK` + x-description-zh: 两地上市股票的港股代码,例如 `939.HK` + x-description-zh-hk: 兩地上市股票的港股代碼,例如 `939.HK` + - name: days + in: query + type: integer + required: true + description: 'Number of intraday trading days to return, e.g. `1`' + x-description-zh: '返回的日内交易日数量,例如 `1`' + x-description-zh-hk: '返回的日內交易日數量,例如 `1`' + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge ah-premium intraday 939.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/ahpremium/timeshares?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/ahpremium/timeshares", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/ahpremium/timeshares", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/ahpremium/timeshares") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ahpremium/timeshares?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/ahpremium/timeshares") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ahpremium/timeshares?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/ahpremium/timeshares?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: klines + type: object[] + required: true + description: Intraday A/H premium kline data + x-description-zh: 日内 A/H 溢价 K 线数据 + x-description-zh-hk: 日內 A/H 溢價 K 線數據 + - name: └ timestamp + type: string + required: false + description: Unix timestamp + x-description-zh: Unix 时间戳 + x-description-zh-hk: Unix 時間戳 + - name: └ ahpremium_rate + type: string + required: false + description: A/H premium rate + x-description-zh: A/H 溢价率 + x-description-zh-hk: A/H 溢價率 + - name: └ aprice + type: string + required: false + description: A-share price (CNY) + x-description-zh: A 股价格(CNY) + x-description-zh-hk: A 股價格(CNY) + - name: └ apreclose + type: string + required: false + description: A-share previous close (CNY) + x-description-zh: A 股昨收价(CNY) + x-description-zh-hk: A 股昨收價(CNY) + - name: └ hprice + type: string + required: false + description: H-share price (HKD) + x-description-zh: H 股价格(HKD) + x-description-zh-hk: H 股價格(HKD) + - name: └ hpreclose + type: string + required: false + description: H-share previous close (HKD) + x-description-zh: H 股昨收价(HKD) + x-description-zh-hk: H 股昨收價(HKD) + - name: └ currency_rate + type: string + required: false + description: CNH/HKD exchange rate + x-description-zh: CNH/HKD 汇率 + x-description-zh-hk: CNH/HKD 匯率 + - name: └ price_spread + type: string + required: false + description: Price spread + x-description-zh: 价差 + x-description-zh-hk: 價差 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + klines: + - ahpremium_rate: '0.1523' + apreclose: '24.80' + aprice: '25.10' + currency_rate: '0.8920' + hpreclose: '19.20' + hprice: '19.50' + price_spread: '1.23' + timestamp: '1778198400' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/broker-holding: + get: + operationId: broker_positions + summary: Broker Positions + x-summary-zh: 经纪商持仓 + x-summary-zh-hk: 經紀商持倉 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + View broker holding positions for HK-listed stocks, including top buyers/sellers and full detail. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可查询任意标的,包括美股。 + ::: + + 查看港股券商持仓情况,包含主要买卖方和详细持仓列表。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可查詢任意標的,包括美股。 + ::: + + 查看港股券商持倉情況,包含主要買賣方和詳細持倉列表。 + x-subgroup: Market Data + x-subgroup-zh: 市场数据 + x-subgroup-zh-hk: 市場數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `700.HK`. + x-description-zh: 标的代码,如 `700.HK`。 + x-description-zh-hk: 標的代碼,如 `700.HK`。 + - name: type + in: query + type: string + required: true + description: 'Statistics period: `rct_1` (1 day), `rct_5` (5 days), `rct_20` (20 days), `rct_60` (60 days).' + x-description-zh: 统计周期:`rct_1`(1 日)、`rct_5`(5 日)、`rct_20`(20 日)、`rct_60`(60 日)。 + x-description-zh-hk: 統計週期:`rct_1`(1 日)、`rct_5`(5 日)、`rct_20`(20 日)、`rct_60`(60 日)。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge broker-holding 700.HK + longbridge broker-holding 9988.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/broker-holding?symbol=<symbol>&type=<type>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/broker-holding", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "type": "<type>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/broker-holding", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "type": "<type>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/broker-holding") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("type", "<type>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/broker-holding?symbol=<symbol>&type=<type>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/broker-holding") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("type", "<type>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/broker-holding?symbol=<symbol>&type=<type>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/broker-holding?symbol=<symbol>&type=<type>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: buy + type: object[] + required: false + description: Top buying brokers + x-description-zh: 净买入经纪商列表, + x-description-zh-hk: 淨買入經紀商列表, + - name: └ name + type: string + required: false + description: Broker name + x-description-zh: 经纪商名称 + x-description-zh-hk: 經紀商名稱 + - name: └ parti_number + type: string + required: false + description: Broker participant number + x-description-zh: 经纪商参与者编号 + x-description-zh-hk: 經紀商參與者編號 + - name: └ chg + type: string + required: false + description: Position change + x-description-zh: 持仓变动 + x-description-zh-hk: 持倉變動 + - name: └ strong + type: boolean + required: false + description: Whether marked as strong holder + x-description-zh: 是否为主要持仓者 + x-description-zh-hk: 是否為主要持倉者 + - name: sell + type: object[] + required: false + description: Top selling brokers + x-description-zh: 净卖出经纪商列表, + x-description-zh-hk: 淨賣出經紀商列表, + - name: └ name + type: string + required: false + description: Broker name + x-description-zh: 经纪商名称 + x-description-zh-hk: 經紀商名稱 + - name: └ parti_number + type: string + required: false + description: Broker participant number + x-description-zh: 经纪商参与者编号 + x-description-zh-hk: 經紀商參與者編號 + - name: └ chg + type: string + required: false + description: Position change + x-description-zh: 持仓变动 + x-description-zh-hk: 持倉變動 + - name: └ strong + type: boolean + required: false + description: Whether marked as strong holder + x-description-zh: 是否为主要持仓者 + x-description-zh-hk: 是否為主要持倉者 + - name: updated_at + type: string + required: false + description: Last update timestamp + x-description-zh: 最后更新时间 + x-description-zh-hk: 最後更新時間 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + buy: + - parti_number: B01224 + name: HSBC + chg: '5000000' + strong: true + sell: + - parti_number: B01274 + name: Goldman Sachs HK + chg: '-3000000' + strong: false + updated_at: 2026.05.13 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/broker-holding/daily: + get: + operationId: broker_holding_daily + summary: Broker Holding Daily + x-summary-zh: 经纪商每日持仓历史 + x-summary-zh-hk: 經紀商每日持倉歷史 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Get daily holding history for a specific broker in an HK-listed security. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可查询任意标的,包括美股。 + ::: + + 获取某一经纪商在港股上市证券中的每日持仓历史记录。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可查詢任意標的,包括美股。 + ::: + + 獲取某一經紀商在港股上市證券中的每日持倉歷史記錄。 + x-subgroup: Market Data + x-subgroup-zh: 市场数据 + x-subgroup-zh-hk: 市場數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `700.HK`. + x-description-zh: 标的代码,如 `700.HK`。 + x-description-zh-hk: 標的代碼,如 `700.HK`。 + - name: parti_number + in: query + type: string + required: true + description: Broker (participant) number. + x-description-zh: 券商(参与者)编号。 + x-description-zh-hk: 券商(參與者)編號。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge broker-holding daily 700.HK --broker B01224 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/broker-holding/daily?symbol=<symbol>&parti_number=<parti_number>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/broker-holding/daily", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "parti_number": "<parti_number>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/broker-holding/daily", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "parti_number": "<parti_number>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/broker-holding/daily") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("parti_number", "<parti_number>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/broker-holding/daily?symbol=<symbol>&parti_number=<parti_number>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/broker-holding/daily") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("parti_number", "<parti_number>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/broker-holding/daily?symbol=<symbol>&parti_number=<parti_number>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/broker-holding/daily?symbol=<symbol>&parti_number=<parti_number>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: true + description: Daily holding history records + x-description-zh: 每日持仓历史记录, + x-description-zh-hk: 每日持倉歷史紀錄, + - name: └ date + type: string + required: true + description: Date (e.g. `2026.05.13`) + x-description-zh: 日期(如 `2026.05.13`) + x-description-zh-hk: 日期(如 `2026.05.13`) + - name: └ holding + type: string + required: false + description: Total shares held + x-description-zh: 总持股数 + x-description-zh-hk: 總持股數 + - name: └ chg + type: string + required: false + description: Daily change in shares + x-description-zh: 日变动量 + x-description-zh-hk: 日變動量 + - name: └ ratio + type: string + required: false + description: Holding ratio + x-description-zh: 持仓比率 + x-description-zh-hk: 持倉比率 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + - date: 2026.05.13 + holding: '22903430' + chg: '7029132.0000' + ratio: '0.0025' + - date: 2026.05.12 + holding: '15874298' + chg: '-2150000.0000' + ratio: '0.0017' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/broker-holding/detail: + get: + operationId: broker_holding_detail + summary: Broker Holding Detail + x-summary-zh: 经纪商持仓详情 + x-summary-zh-hk: 經紀商持倉詳情 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Get full broker holding detail list for an HK-listed security (all brokers and their positions). + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可查询任意标的,包括美股。 + ::: + + 获取港股上市证券的完整经纪商持仓详情列表(所有经纪商及其持仓数量)。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可查詢任意標的,包括美股。 + ::: + + 獲取港股上市證券的完整經紀商持倉詳情列表(所有經紀商及其持倉數量)。 + x-subgroup: Market Data + x-subgroup-zh: 市场数据 + x-subgroup-zh-hk: 市場數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: HK security symbol, e.g. `700.HK` + x-description-zh: 港股代码,例如 `700.HK` + x-description-zh-hk: 港股代碼,例如 `700.HK` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge broker-holding detail 700.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/broker-holding/detail?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/broker-holding/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/broker-holding/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/broker-holding/detail") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/broker-holding/detail?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/broker-holding/detail") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/broker-holding/detail?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/broker-holding/detail?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Broker holding detail records + x-description-zh: 经纪商持仓明细, + x-description-zh-hk: 經紀商持倉明細, + - name: └ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ parti_number + type: string + required: false + description: Broker participant number + x-description-zh: 经纪商参与者编号 + x-description-zh-hk: 經紀商參與者編號 + - name: └ ratio + type: object + required: false + description: Holding ratio + x-description-zh: 持仓比率 + x-description-zh-hk: 持倉比率 + - name: └ ∟ value + type: string + required: false + description: Indicator value as a string; empty when unavailable. + x-description-zh: 指标值(字符串),无数据时为空。 + x-description-zh-hk: 指標值(字符串),無數據時為空。 + - name: └ ∟ chg_1 + type: string + required: false + description: 1-day change ratio. + x-description-zh: 近 1 日涨跌幅。 + x-description-zh-hk: 近 1 日漲跌幅。 + - name: └ ∟ chg_5 + type: string + required: false + description: 5-day change ratio. + x-description-zh: 近 5 日涨跌幅。 + x-description-zh-hk: 近 5 日漲跌幅。 + - name: └ ∟ chg_20 + type: string + required: false + description: 20-day change ratio. + x-description-zh: 近 20 日涨跌幅。 + x-description-zh-hk: 近 20 日漲跌幅。 + - name: └ ∟ chg_60 + type: string + required: false + description: 60-day change ratio. + x-description-zh: 近 60 日涨跌幅。 + x-description-zh-hk: 近 60 日漲跌幅。 + - name: └ shares + type: object + required: false + description: Holding share counts + x-description-zh: 持股数量 + x-description-zh-hk: 持股數量 + - name: └ ∟ value + type: string + required: false + description: Indicator value as a string; empty when unavailable. + x-description-zh: 指标值(字符串),无数据时为空。 + x-description-zh-hk: 指標值(字符串),無數據時為空。 + - name: └ ∟ chg_1 + type: string + required: false + description: 1-day change ratio. + x-description-zh: 近 1 日涨跌幅。 + x-description-zh-hk: 近 1 日漲跌幅。 + - name: └ ∟ chg_5 + type: string + required: false + description: 5-day change ratio. + x-description-zh: 近 5 日涨跌幅。 + x-description-zh-hk: 近 5 日漲跌幅。 + - name: └ ∟ chg_20 + type: string + required: false + description: 20-day change ratio. + x-description-zh: 近 20 日涨跌幅。 + x-description-zh-hk: 近 20 日漲跌幅。 + - name: └ ∟ chg_60 + type: string + required: false + description: 60-day change ratio. + x-description-zh: 近 60 日涨跌幅。 + x-description-zh-hk: 近 60 日漲跌幅。 + - name: └ strong + type: boolean + required: false + description: Whether marked as strong holder + x-description-zh: 是否为主要持仓者 + x-description-zh-hk: 是否為主要持倉者 + - name: updated_at + type: string + required: false + description: Last update date + x-description-zh: 最后更新日期 + x-description-zh-hk: 最後更新日期 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + updated_at: 2026.05.13 + list: + - parti_number: B01224 + name: HSBC Securities + strong: false + shares: + value: '25100' + chg_1: '4000.0000' + chg_5: '6100.0000' + chg_20: '12600.0000' + chg_60: '8800.0000' + ratio: + value: '0.0025' + chg_1: '0.0004' + chg_5: '0.0006' + chg_20: '0.0012' + chg_60: '0.0009' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/index-constituents: + get: + operationId: index_components + summary: Index Components + x-summary-zh: 指数成分股 + x-summary-zh-hk: 指數成分股 + description: | + Get the constituent stocks of an index or ETF with sorting options and rise/fall statistics. + x-description-zh: | + 获取指数或 ETF 的成分股列表,支持排序并显示涨跌统计。 + x-description-zh-hk: | + 獲取指數或 ETF 的成分股列表,支持排序並顯示漲跌統計。 + x-subgroup: Market Data + x-subgroup-zh: 市场数据 + x-subgroup-zh-hk: 市場數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Index or ETF symbol, e.g. `HSI.HK`, `SPY.US` + x-description-zh: 指数或 ETF 代码,例如 `HSI.HK`、`SPY.US` + x-description-zh-hk: 指數或 ETF 代碼,例如 `HSI.HK`、`SPY.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge constituent HSI.HK + longbridge constituent SPY.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/index-constituents?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/index-constituents", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/index-constituents", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/index-constituents") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/index-constituents?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/index-constituents") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/index-constituents?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/index-constituents?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: total + type: integer + required: false + description: Total number of matching stocks. + x-description-zh: 满足条件的股票总数。 + x-description-zh-hk: 滿足條件的股票總數。 + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ trade_status + type: integer + required: false + description: Trading status code + x-description-zh: 交易状态码 + x-description-zh-hk: 交易狀態碼 + - name: └ last_done + type: string + required: false + description: Last trade price + x-description-zh: 最新价 + x-description-zh-hk: 最新價 + - name: └ prev_close + type: string + required: false + description: Previous close + x-description-zh: 前收盘价 + x-description-zh-hk: 前收盤價 + - name: └ inflow + type: string + required: false + description: Capital inflow + x-description-zh: 资金净流入 + x-description-zh-hk: 資金淨流入 + - name: └ balance + type: string + required: false + description: Market cap + x-description-zh: 市值 + x-description-zh-hk: 市值 + - name: └ amount + type: string + required: false + description: Trading volume amount + x-description-zh: 成交额 + x-description-zh-hk: 成交額 + - name: └ total_shares + type: string + required: false + description: Total shares + x-description-zh: 总股数 + x-description-zh-hk: 總股數 + - name: └ circulating_shares + type: string + required: false + description: Circulating shares + x-description-zh: 流通股数 + x-description-zh-hk: 流通股數 + - name: └ tags + type: array + required: false + description: Tags + x-description-zh: 标签 + x-description-zh-hk: 標籤 + - name: └ name + type: string + required: false + description: Security name + x-description-zh: 证券名称 + x-description-zh-hk: 證券名稱 + - name: └ market + type: string + required: false + description: Market + x-description-zh: 市场 + x-description-zh-hk: 市場 + - name: └ intro + type: string + required: false + description: Brief description + x-description-zh: 简介 + x-description-zh-hk: 簡介 + - name: └ delay + type: boolean + required: false + description: Whether data is delayed + x-description-zh: 是否为延迟数据 + x-description-zh-hk: 是否為延遲數據 + - name: └ chg + type: string + required: false + description: Price change percentage + x-description-zh: 涨跌幅 + x-description-zh-hk: 漲跌幅 + - name: rise_num + type: integer + required: false + description: Number of rising stocks + x-description-zh: 上涨数量 + x-description-zh-hk: 上漲數量 + - name: fall_num + type: integer + required: false + description: Number of falling stocks + x-description-zh: 下跌数量 + x-description-zh-hk: 下跌數量 + - name: flat_num + type: integer + required: false + description: Number of flat stocks + x-description-zh: 平盘数量 + x-description-zh-hk: 平盤數量 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + fall_num: 10 + flat_num: 3 + rise_num: 7 + stocks: + - symbol: 9988.HK + name: BABA-W + market: HK + last_done: '140.90' + prev_close: '132.80' + chg: '0.0610' + amount: '93828577' + inflow: '18483450' + balance: '13320299492' + circulating_shares: '19192403958' + total_shares: '19192403958' + trade_status: 105 + intro: China's largest e-commerce platform + delay: false + tags: + - Top gainers + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/trades-statistics: + get: + operationId: trading_stats + summary: Trading Stats + x-summary-zh: 成交统计 + x-summary-zh-hk: 成交統計 + description: | + Get trade statistics showing price distribution by volume for a security. + x-description-zh: | + 获取指定证券的成交统计数据,展示成交量的价格分布。 + x-description-zh-hk: | + 獲取指定證券的成交統計數據,展示成交量的價格分佈。 + x-subgroup: Market Data + x-subgroup-zh: 市场数据 + x-subgroup-zh-hk: 市場數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `700.HK` + x-description-zh: 证券代码,例如 `700.HK` + x-description-zh-hk: 證券代碼,例如 `700.HK` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge trade-stats 700.HK + longbridge trade-stats TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/trades-statistics?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/trades-statistics", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/trades-statistics", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/trades-statistics") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/trades-statistics?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/trades-statistics") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/trades-statistics?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/trades-statistics?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: trades + type: object[] + required: false + description: Price-level trade distribution + x-description-zh: 按价位的成交分布, + x-description-zh-hk: 按價位的成交分佈, + - name: └ price + type: string + required: false + description: Price level + x-description-zh: 价位 + x-description-zh-hk: 價位 + - name: └ buy_amount + type: string + required: false + description: Buy amount at this price + x-description-zh: 该价位买入成交额 + x-description-zh-hk: 該價位買入成交額 + - name: └ sell_amount + type: string + required: false + description: Sell amount at this price + x-description-zh: 该价位卖出成交额 + x-description-zh-hk: 該價位賣出成交額 + - name: └ neutral_amount + type: string + required: false + description: Neutral amount at this price + x-description-zh: 该价位中性成交额 + x-description-zh-hk: 該價位中性成交額 + - name: statistics + type: object + required: false + description: Aggregate trade statistics + x-description-zh: 成交统计汇总 + x-description-zh-hk: 成交統計匯總 + - name: └ avgprice + type: string + required: false + description: Average price. + x-description-zh: 平均价。 + x-description-zh-hk: 平均價。 + - name: └ trades_count + type: string + required: false + description: Number of trades. + x-description-zh: 成交笔数。 + x-description-zh-hk: 成交筆數。 + - name: └ total_amount + type: string + required: false + description: Total market turnover. + x-description-zh: 总成交额。 + x-description-zh-hk: 總成交額。 + - name: └ buy + type: string + required: false + description: Top buying brokers + x-description-zh: 净买入经纪商列表, + x-description-zh-hk: 淨買入經紀商列表, + - name: └ sell + type: string + required: false + description: Top selling brokers + x-description-zh: 净卖出经纪商列表, + x-description-zh-hk: 淨賣出經紀商列表, + - name: └ neutral + type: string + required: false + description: Neutral value/count. + x-description-zh: 中性值/数量。 + x-description-zh-hk: 中性值/數量。 + - name: └ timestamp + type: string + required: false + description: Event time (Unix seconds as string) + x-description-zh: 异动时间(Unix 秒,字符串格式) + x-description-zh-hk: 異動時間(Unix 秒,字符串格式) + - name: └ preclose + type: string + required: false + description: Previous close price. + x-description-zh: 前收盘价。 + x-description-zh-hk: 前收盤價。 + - name: └ trade_date + type: array + required: false + description: Next projected trade date (YYYY-MM-DD) + x-description-zh: 下一次预计交易日期(YYYY-MM-DD) + x-description-zh-hk: 下一次預計交易日期(YYYY-MM-DD) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + statistics: + avgprice: '210.50' + buy: '45000000' + sell: '38000000' + neutral: '12000000' + total_amount: '95000000' + trades_count: '125000' + preclose: '208.20' + timestamp: '1778198400' + trade_date: + - '2026-05-13' + trades: + - price: '210.00' + buy_amount: '5000000' + sell_amount: '4000000' + neutral_amount: '1000000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/ratings/institutional: + get: + operationId: institution_rating_views + summary: Institutional Rating Views Timeline + x-summary-zh: 机构评级分布时间线 + x-summary-zh-hk: 機構評級分佈時間線 + description: | + Get the monthly institutional rating (buy/hold/sell) distribution timeline, newest first. + x-description-zh: | + 获取按月统计的机构评级(买入/持有/卖出)分布时间线,最新月份在前。 + x-description-zh-hk: | + 獲取按月統計的機構評級(買入/持有/賣出)分佈時間線,最新月份在前。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge institution-rating AAPL.US --views + longbridge institution-rating TSLA.US --views + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/ratings/institutional?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/ratings/institutional", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/ratings/institutional", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/ratings/institutional") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/ratings/institutional?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/ratings/institutional") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/ratings/institutional?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/ratings/institutional?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: elist + type: object[] + required: false + description: Monthly rating distribution list, newest first + x-description-zh: 月度评级分布列表,最新月份在前 + x-description-zh-hk: 月度評級分佈列表,最新月份在前 + - name: └ buy + type: string + required: false + description: Number of Buy ratings + x-description-zh: 买入评级数量 + x-description-zh-hk: 買入評級數量 + - name: └ over + type: string + required: false + description: Number of Outperform ratings + x-description-zh: 跑赢市场评级数量 + x-description-zh-hk: 跑贏市場評級數量 + - name: └ hold + type: string + required: false + description: Number of Hold ratings + x-description-zh: 持有评级数量 + x-description-zh-hk: 持有評級數量 + - name: └ under + type: string + required: false + description: Number of Underperform ratings + x-description-zh: 跑输市场评级数量 + x-description-zh-hk: 跑輸市場評級數量 + - name: └ sell + type: string + required: false + description: Number of Sell ratings + x-description-zh: 卖出评级数量 + x-description-zh-hk: 賣出評級數量 + - name: └ total + type: string + required: false + description: Total analyst count + x-description-zh: 机构总数 + x-description-zh-hk: 機構總數 + - name: └ no_opinion + type: string + required: false + description: Number of analysts with no opinion. + x-description-zh: 无观点的分析师数。 + x-description-zh-hk: 無觀點的分析師數。 + - name: └ date + type: string + required: false + description: Unix timestamp (seconds) + x-description-zh: Unix 时间戳(秒) + x-description-zh-hk: Unix 時間戳(秒) + - name: tlist + type: object[] + required: false + description: Time-series list. + x-description-zh: 时间序列列表。 + x-description-zh-hk: 時間序列列表。 + - name: └ date + type: string + required: false + description: Unix timestamp (seconds) + x-description-zh: Unix 时间戳(秒) + x-description-zh-hk: Unix 時間戳(秒) + - name: └ close + type: string + required: false + description: Closing price. + x-description-zh: 收盘价。 + x-description-zh-hk: 收盤價。 + - name: └ high_price + type: string + required: false + description: High price. + x-description-zh: 最高价。 + x-description-zh-hk: 最高價。 + - name: └ low_price + type: string + required: false + description: Low price. + x-description-zh: 最低价。 + x-description-zh-hk: 最低價。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + elist: + - date: 1746057600 + buy: '18' + over: '5' + hold: '17' + under: '3' + sell: '4' + total: '51' + - date: 1743379200 + buy: '17' + over: '6' + hold: '18' + under: '3' + sell: '5' + total: '53' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/market-status: + get: + operationId: market_status + summary: Market Status + x-summary-zh: 市场状态 + x-summary-zh-hk: 市場狀態 + description: | + Get the current open/close status for each exchange. + x-description-zh: | + 获取各交易所当前的开市/休市状态。 + x-description-zh-hk: | + 獲取各交易所當前的開市/休市狀態。 + x-subgroup: Market Status + x-subgroup-zh: 市场状态 + x-subgroup-zh-hk: 市場狀態 + tags: + - Market + x-parameters: + - name: market + in: query + type: string + required: false + description: 'Market code: `US`, `HK`, `SH`, `SZ`, `SG`. Omit for all markets.' + x-description-zh: 市场代码:`US`、`HK`、`SH`、`SZ`、`SG`。不填则返回全部市场。 + x-description-zh-hk: 市場代碼:`US`、`HK`、`SH`、`SZ`、`SG`。不填則返回全部市場。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge market-status + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/market-status' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/market-status", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/market-status", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/market-status", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/market-status")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/market-status") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/market-status"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/market-status\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: market_time + type: object[] + required: false + description: List of market status items + x-description-zh: 市场状态列表, + x-description-zh-hk: 市場狀態列表, + - name: └ market + type: string + required: false + description: 'Market: `US`, `HK`, `CN`, `SG`, `Crypto`' + x-description-zh: 市场:`US`、`HK`、`CN`、`SG`、`Crypto` + x-description-zh-hk: 市場:`US`、`HK`、`CN`、`SG`、`Crypto` + - name: └ trade_status + type: integer + required: false + description: Trading status code + x-description-zh: 交易状态码 + x-description-zh-hk: 交易狀態碼 + - name: └ timestamp + type: string + required: false + description: Event time (Unix seconds as string) + x-description-zh: 异动时间(Unix 秒,字符串格式) + x-description-zh-hk: 異動時間(Unix 秒,字符串格式) + - name: └ delay_trade_status + type: integer + required: false + description: Delayed trading status + x-description-zh: 延迟交易状态 + x-description-zh-hk: 延遲交易狀態 + - name: └ delay_timestamp + type: string + required: false + description: Delay timestamp + x-description-zh: 延迟时间戳 + x-description-zh-hk: 延遲時間戳 + - name: └ sub_status + type: integer + required: false + description: Sub-status. + x-description-zh: 子状态。 + x-description-zh-hk: 子狀態。 + - name: └ delay_sub_status + type: integer + required: false + description: Delayed subscription status + x-description-zh: 延迟订阅状态 + x-description-zh-hk: 延遲訂閱狀態 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + market_time: + - market: US + delay_sub_status: 0 + delay_timestamp: '0' + delay_trade_status: 0 + - market: HK + delay_sub_status: 0 + delay_timestamp: '0' + delay_trade_status: 0 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/market/rank/categories: + get: + operationId: rank_categories + summary: Rank Categories + x-summary-zh: 人气排行分类 + x-summary-zh-hk: 人氣排行分類 + description: | + Get the tag category configuration for the popularity leaderboard. `second_tags[].key` can be passed to `rank_list`. + x-description-zh: | + 获取人气排行榜的标签分类配置,`second_tags[].key` 可传入 `rank_list`。 + x-description-zh-hk: | + 獲取人氣排行榜的標籤分類配置,`second_tags[].key` 可傳入 `rank_list`。 + x-subgroup: Market Status + x-subgroup-zh: 市场状态 + x-subgroup-zh-hk: 市場狀態 + tags: + - Market + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge rank + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/market/rank/categories' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/market/rank/categories", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/market/rank/categories", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/quote/market/rank/categories", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/market/rank/categories")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/market/rank/categories") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/market/rank/categories"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/market/rank/categories\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: first_tags + type: object[] + required: false + description: Top-level category list + x-description-zh: 一级分类列表 + x-description-zh-hk: 一級分類列表 + - name: └ key + type: string + required: false + description: Top-level category key + x-description-zh: 一级分类键值 + x-description-zh-hk: 一級分類鍵值 + - name: └ name + type: string + required: false + description: Top-level category name + x-description-zh: 一级分类名称 + x-description-zh-hk: 一級分類名稱 + - name: └ second_tags + type: object[] + required: false + description: Second-level category list + x-description-zh: 二级分类列表 + x-description-zh-hk: 二級分類列表 + - name: └ ∟ key + type: string + required: false + description: Top-level category key + x-description-zh: 一级分类键值 + x-description-zh-hk: 一級分類鍵值 + - name: └ ∟ name + type: string + required: false + description: Top-level category name + x-description-zh: 一级分类名称 + x-description-zh-hk: 一級分類名稱 + - name: └ ∟ market + type: string + required: false + description: 'Associated market: `US`, `HK`, `CN`, `SG`' + x-description-zh: 所属市场:`US`、`HK`、`CN`、`SG` + x-description-zh-hk: 所屬市場:`US`、`HK`、`CN`、`SG` + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + first_tags: + - key: ib_hot + name: Hotness Rank + second_tags: + - key: ib_hot_all-us + name: US Total Hotness + market: US + - key: ib_hot_all-hk + name: HK Total Hotness + market: HK + - key: ib_hot_all-cn + name: A-share Total Hotness + market: CN + - key: ib_change + name: Price Change Rank + second_tags: + - key: ib_change_top-us + name: US Top Gainers + market: US + - key: ib_change_top-hk + name: HK Top Gainers + market: HK + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/market/rank/list: + get: + operationId: rank_list + summary: Popularity Leaderboard + x-summary-zh: 人气排行榜 + x-summary-zh-hk: 人氣排行榜 + description: | + Get the stock ranking for a given leaderboard tag key. The key comes from `rank_categories` `second_tags[].key`, e.g. `hot_all-us` (US total hotness). + x-description-zh: | + 根据排行榜标签 key 获取股票排行。key 来自 `rank_categories` 的 `second_tags[].key`,例如 `hot_all-us`(美股总热度)。 + x-description-zh-hk: | + 根據排行榜標籤 key 獲取股票排行。key 來自 `rank_categories` 的 `second_tags[].key`,例如 `hot_all-us`(美股總熱度)。 + x-subgroup: Market Status + x-subgroup-zh: 市场状态 + x-subgroup-zh-hk: 市場狀態 + tags: + - Market + x-parameters: + - name: key + in: query + type: string + required: true + description: Leaderboard tag key from `rank_categories` `second_tags[].key` + x-description-zh: 排行榜标签键值,来自 `rank_categories` 的 `second_tags[].key` + x-description-zh-hk: 排行榜標籤鍵值,來自 `rank_categories` 的 `second_tags[].key` + - name: need_article + in: query + type: boolean + required: false + description: Whether to return associated articles, default `false` + x-description-zh: 是否返回关联文章,默认 `false` + x-description-zh-hk: 是否返回關聯文章,默認 `false` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge rank --key hot_all-us + longbridge rank --key hot_all-hk + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/market/rank/list?key=<key>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/market/rank/list", + headers={"Authorization": "Bearer <access_token>"}, + params={"key": "<key>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/market/rank/list", + headers={"Authorization": "Bearer <access_token>"}, + params={"key": "<key>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/market/rank/list") + url.searchParams.set("key", "<key>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/market/rank/list?key=<key>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/market/rank/list") + .header("Authorization", "Bearer <access_token>") + .query(&[("key", "<key>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/market/rank/list?key=<key>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/market/rank/list?key=<key>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: bmp + type: boolean + required: false + description: Whether the response is a market preview (before open) + x-description-zh: 是否为盘前预览数据 + x-description-zh-hk: 是否為盤前預覽數據 + - name: lists + type: object[] + required: false + description: Leaderboard stock list + x-description-zh: 排行榜股票列表 + x-description-zh-hk: 排行榜股票列表 + - name: └ code + type: string + required: false + description: Ticker code (e.g. `MU`) + x-description-zh: 股票代码(如 `MU`) + x-description-zh-hk: 股票代碼(如 `MU`) + - name: └ symbol + type: string + required: false + description: Symbol in `CODE.MARKET` format (e.g. `MU.US`) + x-description-zh: 标的代码,格式为 `代码。市场`(如 `MU.US`) + x-description-zh-hk: 標的代碼,格式為 `代碼。市場`(如 `MU.US`) + - name: └ name + type: string + required: false + description: Security name + x-description-zh: 证券名称 + x-description-zh-hk: 證券名稱 + - name: └ last_done + type: string + required: false + description: Latest trade price + x-description-zh: 最新成交价 + x-description-zh-hk: 最新成交價 + - name: └ chg + type: string + required: false + description: Price change ratio as decimal (e.g. `0.0252` = 2.52%) + x-description-zh: 涨跌幅(小数比率,如 `0.0252` 表示 2.52%) + x-description-zh-hk: 漲跌幅(小數比率,如 `0.0252` 表示 2.52%) + - name: └ change + type: string + required: false + description: Absolute price change (e.g. `17.200`) + x-description-zh: 价格涨跌额(如 `17.200`) + x-description-zh-hk: 價格漲跌額(如 `17.200`) + - name: └ inflow + type: string + required: false + description: Net capital inflow (in the market's currency) + x-description-zh: 净流入资金(单位:所属市场货币) + x-description-zh-hk: 淨流入資金(單位:所屬市場貨幣) + - name: └ market_cap + type: string + required: false + description: Market capitalisation + x-description-zh: 市值 + x-description-zh-hk: 市值 + - name: └ industry + type: string + required: false + description: Industry classification + x-description-zh: 行业分类 + x-description-zh-hk: 行業分類 + - name: └ pre_post_price + type: string + required: false + description: Pre/post-market price + x-description-zh: 盘前/盘后价格 + x-description-zh-hk: 盤前/盤後價格 + - name: └ pre_post_chg + type: string + required: false + description: Pre/post-market price change ratio (decimal) + x-description-zh: 盘前/盘后涨跌幅(小数比率) + x-description-zh-hk: 盤前/盤後漲跌幅(小數比率) + - name: └ amplitude + type: string + required: false + description: Amplitude / intraday range ratio (decimal) + x-description-zh: 振幅(小数比率) + x-description-zh-hk: 振幅(小數比率) + - name: └ five_day_chg + type: string + required: false + description: 5-day price change ratio (decimal) + x-description-zh: 5 日涨跌幅(小数比率) + x-description-zh-hk: 5 日漲跌幅(小數比率) + - name: └ turnover_rate + type: string + required: false + description: Turnover rate (decimal) + x-description-zh: 换手率(小数比率) + x-description-zh-hk: 換手率(小數比率) + - name: └ volume_rate + type: string + required: false + description: Volume ratio (vs average) + x-description-zh: 量比 + x-description-zh-hk: 量比 + - name: └ pb_ttm + type: string + required: false + description: Price-to-book ratio (TTM) + x-description-zh: 市净率(TTM) + x-description-zh-hk: 市淨率(TTM) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + bmp: false + lists: + - code: MU + symbol: MU.US + name: Micron Technology + last_done: '698.740' + chg: '0.0252' + change: '17.200' + inflow: '-347041642' + market_cap: '787992890796' + industry: Semiconductor Manufacturers + pre_post_price: '726.600' + pre_post_chg: '0.0399' + amplitude: '0.1082' + five_day_chg: '-0.0885' + turnover_rate: '0.0550' + volume_rate: '1.11' + pb_ttm: '32.68' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/changes: + get: + operationId: unusual_items + summary: Unusual Items + x-summary-zh: 异动行情 + x-summary-zh-hk: 異動行情 + description: | + Detect unusual market movements — price spikes, volume surges, and other abnormal trading activity. + x-description-zh: | + 识别市场异动,包括价格异常波动、成交量激增等非正常交易行为。 + x-description-zh-hk: | + 識別市場異動,包括價格異常波動、成交量激增等非正常交易行為。 + x-subgroup: Market Status + x-subgroup-zh: 市场状态 + x-subgroup-zh-hk: 市場狀態 + tags: + - Market + x-parameters: + - name: market + in: query + type: string + required: true + description: 'Market code: `US`, `HK`, `SH`, `SZ`, `SG`' + x-description-zh: 市场代码:`US`、`HK`、`SH`、`SZ`、`SG` + x-description-zh-hk: 市場代碼:`US`、`HK`、`SH`、`SZ`、`SG` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge anomaly --market US + longbridge anomaly --market HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/changes?market=<market>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/changes", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/changes", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/changes") + url.searchParams.set("market", "<market>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/changes?market=<market>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/changes") + .header("Authorization", "Bearer <access_token>") + .query(&[("market", "<market>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/changes?market=<market>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/changes?market=<market>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: all_off + type: boolean + required: false + description: Whether anomaly alerts are globally disabled + x-description-zh: 是否全局关闭异动提醒 + x-description-zh-hk: 是否全局關閉異動提醒 + - name: changes + type: object[] + required: false + description: List of market anomaly events + x-description-zh: 市场异动事件列表, + x-description-zh-hk: 市場異動事件列表, + - name: └ symbol + type: string + required: true + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ name + type: string + required: false + description: Security name + x-description-zh: 证券名称 + x-description-zh-hk: 證券名稱 + - name: └ alert_name + type: string + required: false + description: Anomaly type name + x-description-zh: 异动类型名称 + x-description-zh-hk: 異動類型名稱 + - name: └ alert_time + type: integer + required: false + description: Anomaly time (Unix timestamp, ms) + x-description-zh: 异动时间(Unix 时间戳,毫秒) + x-description-zh-hk: 異動時間(Unix 時間戳,毫秒) + - name: └ emotion + type: integer + required: false + description: 'Sentiment: `1`=positive/up, `2`=negative/down' + x-description-zh: 情绪方向:`1`=正面/上涨,`2`=负面/下跌 + x-description-zh-hk: 情緒方向:`1`=正面/上漲,`2`=負面/下跌 + - name: └ change_values + type: string[] + required: false + description: Change value strings + x-description-zh: 变化数值字符串列表 + x-description-zh-hk: 變化數值字符串列表 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + all_off: false + changes: + - symbol: TSLA.US + name: Tesla Inc. + alert_name: 大宗交易 + alert_time: 1778198400000 + emotion: 1 + change_values: + - +5.2% + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/finance_calendar: + get: + operationId: finance_calendar + summary: Earnings Calendar + x-summary-zh: 财报日历 + x-summary-zh-hk: 財報日曆 + description: | + Browse upcoming earnings reports and recent results, with EPS and revenue estimates. + x-description-zh: | + 浏览即将发布的财报及近期业绩,包含 EPS 和营收预期。 + x-description-zh-hk: | + 瀏覽即將發布的財報及近期業績,包含 EPS 和營收預期。 + x-subgroup: Financial Calendar + x-subgroup-zh: 财经日历 + x-subgroup-zh-hk: 財經日曆 + tags: + - Market + x-parameters: + - name: date + in: query + type: string + required: true + description: Start date, `YYYYMMDD`. Paginate by passing the returned `next_date`. + x-description-zh: 起始日期,`YYYYMMDD`。用返回的 `next_date` 翻页。 + x-description-zh-hk: 起始日期,`YYYYMMDD`。用返回的 `next_date` 翻頁。 + - name: date_end + in: query + type: string + required: true + description: End date, `YYYYMMDD`. + x-description-zh: 结束日期,`YYYYMMDD`。 + x-description-zh-hk: 結束日期,`YYYYMMDD`。 + - name: types[] + in: query + type: string + required: true + description: 'Event category: `report`, `dividend`, `split`, `ipo`, `macrodata`, `closed`, `meeting`, `merge`.' + x-description-zh: 事件类别:`report`、`dividend`、`split`、`ipo`、`macrodata`、`closed`、`meeting`、`merge`。 + x-description-zh-hk: 事件類別:`report`、`dividend`、`split`、`ipo`、`macrodata`、`closed`、`meeting`、`merge`。 + - name: markets[] + in: query + type: string + required: false + description: Market filter, e.g. `US`, `HK`, `CN`. If omitted, all markets. + x-description-zh: 市场过滤,如 `US`、`HK`、`CN`。省略则不限市场。 + x-description-zh-hk: 市場過濾,如 `US`、`HK`、`CN`。省略則不限市場。 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge finance-calendar report + longbridge finance-calendar report --market US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/finance_calendar?date=<date>&date_end=<date_end>&types[]=<types[]>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/finance_calendar", + headers={"Authorization": "Bearer <access_token>"}, + params={"date": "<date>", "date_end": "<date_end>", "types[]": "<types[]>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/finance_calendar", + headers={"Authorization": "Bearer <access_token>"}, + params={"date": "<date>", "date_end": "<date_end>", "types[]": "<types[]>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/finance_calendar") + url.searchParams.set("date", "<date>") + url.searchParams.set("date_end", "<date_end>") + url.searchParams.set("types[]", "<types[]>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/finance_calendar?date=<date>&date_end=<date_end>&types[]=<types[]>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/finance_calendar") + .header("Authorization", "Bearer <access_token>") + .query(&[("date", "<date>"), ("date_end", "<date_end>"), ("types[]", "<types[]>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/finance_calendar?date=<date>&date_end=<date_end>&types[]=<types[]>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/finance_calendar?date=<date>&date_end=<date_end>&types[]=<types[]>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: date + type: string + required: false + description: Response date + x-description-zh: 响应日期 + x-description-zh-hk: 響應日期 + - name: list + type: object[] + required: true + description: List of calendar date groups + x-description-zh: 日历日期分组列表, + x-description-zh-hk: 日曆日期分組列表, + - name: count + type: integer + required: false + description: Number of events on this date + x-description-zh: 该日期的事件数量 + x-description-zh-hk: 該日期的事件數量 + - name: infos + type: object[] + required: true + description: List of calendar events + x-description-zh: 日历事件列表, + x-description-zh-hk: 日曆事件列表, + - name: id + type: string + required: false + description: Event ID + x-description-zh: 事件 ID + x-description-zh-hk: 事件 ID + - name: symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: market + type: string + required: false + description: Market + x-description-zh: 市场 + x-description-zh-hk: 市場 + - name: counter_name + type: string + required: false + description: Security name + x-description-zh: 证券名称 + x-description-zh-hk: 證券名稱 + - name: type + type: string + required: false + description: Event type + x-description-zh: 事件类型 + x-description-zh-hk: 事件類型 + - name: activity_type + type: string + required: false + description: Activity type + x-description-zh: 活动类型 + x-description-zh-hk: 活動類型 + - name: datetime + type: string + required: false + description: Event datetime + x-description-zh: 事件时间 + x-description-zh-hk: 事件時間 + - name: date_type + type: string + required: false + description: Date type + x-description-zh: 日期类型 + x-description-zh-hk: 日期類型 + - name: content + type: string + required: false + description: Event content description + x-description-zh: 事件内容描述 + x-description-zh-hk: 事件內容描述 + - name: currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: star + type: integer + required: false + description: Importance rating (1-3) + x-description-zh: 重要性(1-3 星) + x-description-zh-hk: 重要性(1-3 星) + - name: icon + type: string + required: false + description: Icon URL + x-description-zh: 图标链接 + x-description-zh-hk: 圖標鏈接 + - name: chart_uid + type: string + required: false + description: Chart identifier + x-description-zh: 图表标识符 + x-description-zh-hk: 圖表標識符 + - name: financial_market_time + type: string + required: false + description: Financial market time + x-description-zh: 金融市场时间 + x-description-zh-hk: 金融市場時間 + - name: data_kv + type: object[] + required: false + description: Key-value data pairs + x-description-zh: 键值数据对 + x-description-zh-hk: 鍵值數據對 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + date: '2026-04-30' + list: + - date: '2026-04-30' + count: 2228 + infos: + - id: '12345' + symbol: AAPL.US + market: US + counter_name: Apple Inc. + event_type: '' + activity_type: '' + date: '2026-05-14' + datetime: '' + content: '' + star: 0 + currency: '' + icon: '' + chart_uid: '' + date_type: '' + financial_market_time: '' + data_kv: [] + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/option-volume-stats: + get: + operationId: option_volume + summary: Volume + x-summary-zh: 期权成交量 + x-summary-zh-hk: 期權成交量 + description: | + Get real-time call/put volume snapshot for today. + x-description-zh: | + 获取今日认购/认沽期权成交量快照。 + x-description-zh-hk: | + 獲取今日認購/認沽期權成交量快照。 + x-subgroup: Options + x-subgroup-zh: 期权 + x-subgroup-zh-hk: 期權 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: US stock symbol, e.g. `AAPL.US`, `TSLA.US` + x-description-zh: 美股代码,如 `AAPL.US`、`TSLA.US` + x-description-zh-hk: 美股代碼,如 `AAPL.US`、`TSLA.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge option volume AAPL.US + longbridge option volume TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/option-volume-stats?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/option-volume-stats", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/option-volume-stats", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/option-volume-stats") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/option-volume-stats?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/option-volume-stats") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/option-volume-stats?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/option-volume-stats?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: c + type: string + required: false + description: Total call option volume. + x-description-zh: 看涨期权总成交量。 + x-description-zh-hk: 看漲期權總成交量。 + - name: p + type: string + required: false + description: Total put option volume. + x-description-zh: 看跌期权总成交量。 + x-description-zh-hk: 看跌期權總成交量。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + symbol: AAPL.US + call_volume: 284512 + put_volume: 195830 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/option-volume-stats/daily: + get: + operationId: option_volume_daily + summary: Daily Volume + x-summary-zh: 日度成交量 + x-summary-zh-hk: 日度成交量 + description: | + Get historical daily call/put volume and open interest data for a US stock's options. + x-description-zh: | + 获取美股期权的历史每日认购/认沽成交量和未平仓量数据。 + x-description-zh-hk: | + 獲取美股期權的歷史每日認購/認沽成交量和未平倉量數據。 + x-subgroup: Options + x-subgroup-zh: 期权 + x-subgroup-zh-hk: 期權 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Underlying US stock symbol, e.g. `AAPL.US`, `TSLA.US` + x-description-zh: 标的美股代码,如 `AAPL.US`、`TSLA.US` + x-description-zh-hk: 標的美股代碼,如 `AAPL.US`、`TSLA.US` + - name: timestamp + in: query + type: integer + required: false + description: Start Unix timestamp (seconds); `0` returns the most recent (default `0`) + x-description-zh: 起始 Unix 时间戳(秒);`0` 返回最新数据(默认 `0`) + x-description-zh-hk: 起始 Unix 時間戳(秒);`0` 返回最新數據(默認 `0`) + - name: count + in: query + type: integer + required: false + description: Number of trading days to return (default `30`) + x-description-zh: 返回的交易日数量(默认 `30`) + x-description-zh-hk: 返回的交易日數量(默認 `30`) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge option volume daily AAPL.US + longbridge option volume daily TSLA.US --count 60 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/option-volume-stats/daily?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/option-volume-stats/daily", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/option-volume-stats/daily", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/option-volume-stats/daily") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/option-volume-stats/daily?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/option-volume-stats/daily") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/option-volume-stats/daily?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/option-volume-stats/daily?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: stats + type: object[] + required: true + description: Daily volume records. + x-description-zh: 每日成交量记录。 + x-description-zh-hk: 每日成交量記錄。 + - name: └ symbol + type: string + required: true + description: Security symbol, e.g. `AAPL.US`. + x-description-zh: 标的代码,如 `AAPL.US`。 + x-description-zh-hk: 標的代碼,如 `AAPL.US`。 + - name: └ timestamp + type: string + required: true + description: Record date (Unix seconds). + x-description-zh: 记录日期(Unix 秒)。 + x-description-zh-hk: 記錄日期(Unix 秒)。 + - name: └ total_call_volume + type: string + required: true + description: Call volume on that day. + x-description-zh: 当日认购成交量。 + x-description-zh-hk: 當日認購成交量。 + - name: └ total_put_volume + type: string + required: true + description: Put volume on that day. + x-description-zh: 当日认沽成交量。 + x-description-zh-hk: 當日認沽成交量。 + - name: └ total_call_open_interest + type: string + required: true + description: Call open interest. + x-description-zh: 认购未平仓量。 + x-description-zh-hk: 認購未平倉量。 + - name: └ total_put_open_interest + type: string + required: true + description: Put open interest. + x-description-zh: 认沽未平仓量。 + x-description-zh-hk: 認沽未平倉量。 + - name: └ total_volume + type: string + required: true + description: Total options volume. + x-description-zh: 期权总成交量。 + x-description-zh-hk: 期權總成交量。 + - name: └ total_open_interest + type: string + required: true + description: Total options open interest. + x-description-zh: 期权总未平仓量。 + x-description-zh-hk: 期權總未平倉量。 + - name: └ put_call_volume_ratio + type: string + required: true + description: Put/call volume ratio. + x-description-zh: 认沽/认购成交量比率。 + x-description-zh-hk: 認沽/認購成交量比率。 + - name: └ put_call_open_interest_ratio + type: string + required: true + description: Put/call open interest ratio. + x-description-zh: 认沽/认购未平仓量比率。 + x-description-zh-hk: 認沽/認購未平倉量比率。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + symbol: AAPL.US + stats: + - symbol: AAPL.US + date: '2026-05-07' + call_volume: 284512 + put_volume: 195830 + call_open_interest: 1824500 + put_open_interest: 1532100 + total_volume: 480342 + total_open_interest: 3356600 + pc_vol: 0.6886 + pc_oi: 0.8398 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/gemini/crypto-overview: + get: + operationId: us_crypto_overview + summary: US Crypto Overview + x-summary-zh: 美股加密货币概览 + x-summary-zh-hk: 美股加密貨幣概覽 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get overview data for a US crypto trading pair — all-time highs/lows, asset info, and currency details. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股加密货币交易对的概览信息——历史最高/最低价、资产详情和货币信息。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股加密貨幣交易對的概覽資訊——歷史最高/最低價、資產詳情和貨幣資訊。 + x-subgroup: Stocks + x-subgroup-zh: 个股行情 + x-subgroup-zh-hk: 個股 + tags: + - Quote + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Crypto symbol, e.g. `DOGEUSD.BKKT` + x-description-zh: 加密货币交易对,例如 `DOGEUSD.BKKT` + x-description-zh-hk: 加密貨幣交易對,例如 `DOGEUSD.BKKT` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # US crypto overview + longbridge static DOGEUSD.BKKT + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/gemini/crypto-overview?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/gemini/crypto-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/gemini/crypto-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/gemini/crypto-overview") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/gemini/crypto-overview?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/gemini/crypto-overview") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/gemini/crypto-overview?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/gemini/crypto-overview?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: symbol + type: string + required: true + description: Trading-pair symbol (e.g. `DOGEUSD.BKKT`) + x-description-zh: 交易对代码,如 `DOGEUSD.BKKT` + x-description-zh-hk: 交易對代碼,如 `DOGEUSD.BKKT` + - name: ticker + type: string + required: true + description: Short ticker + x-description-zh: 简短代码 + x-description-zh-hk: 簡短代碼 + - name: base_asset + type: string + required: true + description: Base asset code (e.g. `DOGE`) + x-description-zh: 基础资产代码,如 `DOGE` + x-description-zh-hk: 基礎資產代碼,如 `DOGE` + - name: currency + type: string + required: true + description: Quote currency (e.g. `USD`) + x-description-zh: 计价货币,如 `USD` + x-description-zh-hk: 計價貨幣,如 `USD` + - name: all_time_high + type: string + required: true + description: All-time high price + x-description-zh: 历史最高价 + x-description-zh-hk: 歷史最高價 + - name: all_time_high_date + type: string + required: true + description: Date of all-time high + x-description-zh: 历史最高价日期 + x-description-zh-hk: 歷史最高價日期 + - name: all_time_low + type: string + required: true + description: All-time low price + x-description-zh: 历史最低价 + x-description-zh-hk: 歷史最低價 + - name: all_time_low_date + type: string + required: true + description: Date of all-time low + x-description-zh: 历史最低价日期 + x-description-zh-hk: 歷史最低價日期 + - name: ipo_date + type: string + required: false + description: Initial listing date + x-description-zh: 初始上市日期 + x-description-zh-hk: 初始上市日期 + - name: issue_price + type: string + required: false + description: Initial issue price + x-description-zh: 初始发行价格 + x-description-zh-hk: 初始發行價格 + - name: shares + type: string + required: false + description: Total circulating supply + x-description-zh: 流通总量 + x-description-zh-hk: 流通總量 + - name: official_web_address + type: string + required: false + description: Official website URL + x-description-zh: 官方网站 URL + x-description-zh-hk: 官方網站 URL + - name: logo + type: string + required: false + description: Asset logo URL + x-description-zh: 资产 Logo URL + x-description-zh-hk: 資產 Logo URL + - name: wiki_url + type: string + required: false + description: Wikipedia URL + x-description-zh: 维基百科 URL + x-description-zh-hk: 維基百科 URL + - name: profile + type: string + required: false + description: Asset profile description (JSON string) + x-description-zh: 资产简介(JSON 字符串) + x-description-zh-hk: 資產簡介(JSON 字符串) + responses: + '200': + description: Successful response + content: + application/json: + example: + symbol: DOGEUSD.BKKT + name: Dogecoin + ticker: DOGE + base_asset: DOGE + currency: USD + all_time_high: '0.7376' + all_time_high_date: '2021-05-08' + all_time_low: '0.0000869' + all_time_low_date: '2015-05-06' + ipo_date: '2013-12-06' + issue_price: '0.00026' + shares: '147000000000' + official_web_address: https://dogecoin.com + wiki_url: https://en.wikipedia.org/wiki/Dogecoin + profile: '{...}' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v3/trade/execution/all: + get: + operationId: all_executions + summary: All Executions + x-summary-zh: 全部成交明细 + x-summary-zh-hk: 全部成交明細 + description: | + This API is used to query execution (fill) records, including both buy and sell records. It supports querying today's and historical executions at the same time. + x-description-zh: | + 该接口用于获取订单的成交明细,包括买入和卖出的成交记录,同时支持当日成交和历史成交查询。 + x-description-zh-hk: | + 該接口用於獲取訂單的成交明細,包括買入和賣出的成交記錄,同時支持當日成交和歷史成交查詢。 + x-subgroup: Execution + x-subgroup-zh: 成交 + x-subgroup-zh-hk: 成交 + tags: + - Trade + x-parameters: + - name: symbol + in: query + type: string + required: false + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: order_id + in: query + type: string + required: false + description: 'Order ID, example: `701276261045858304`' + x-description-zh: 订单 ID,例如:`701276261045858304` + x-description-zh-hk: 訂單 ID,例如:`701276261045858304` + - name: start_at + in: query + type: string + required: false + description: 'Start time, formatted as a timestamp (second), example: `1650410999`. If the start time is null, the default is the 90 days before of the end time or 90 days before of the current time' + x-description-zh: 开始时间,格式为时间戳 (秒),例如:`1650410999`。. 开始时间为空时,默认为结束时间或当前时间前九十天。 + x-description-zh-hk: 開始時間,格式為時間戳 (秒),例如:`1650410999`。. 開始時間為空時,默認為結束時間或當前時間前九十天。 + - name: end_at + in: query + type: string + required: false + description: 'End time, formatted as a timestamp (second), example: `1650410999`. If the end time is null, the default is the current time or 90 days after of the start time' + x-description-zh: 结束时间,格式为时间戳 (秒),例如:`1650410999`。. 结束时间为空时,默认为开始时间后九十天或当前时间。 + x-description-zh-hk: 結束時間,格式為時間戳 (秒),例如:`1650410999`。. 結束時間為空時,默認為開始時間後九十天或當前時間。 + - name: page + in: query + type: integer + required: false + description: Page number, starting from `1`. The maximum number of records per query is 1000. If the number of results exceeds 1000, `has_more` will be `true`, use `page` together with `has_more` to paginate. + x-description-zh: 页码,从 `1` 开始。单次查询最多返回 1000 条记录,若结果超过 1000 条,`has_more` 为 `true`,可结合 `has_more` 翻页。 + x-description-zh-hk: 頁碼,從 `1` 開始。單次查詢最多返回 1000 條記錄,若結果超過 1000 條,`has_more` 為 `true`,可結合 `has_more` 翻頁。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v3/trade/execution/all' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v3/trade/execution/all", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v3/trade/execution/all", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v3/trade/execution/all", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v3/trade/execution/all")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v3/trade/execution/all") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v3/trade/execution/all"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v3/trade/execution/all\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: trades + type: object[] + required: false + description: Execution Detail + x-description-zh: 成交明细 + x-description-zh-hk: 成交明細 + - name: └ trade_id + type: string + required: false + description: Execution ID + x-description-zh: 成交 ID + x-description-zh-hk: 成交 ID + - name: └ order_id + type: string + required: false + description: Order ID + x-description-zh: 订单 ID + x-description-zh-hk: 訂單 ID + - name: └ symbol + type: string + required: false + description: 'Stock symbol, use `ticker.region` format,example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: └ price + type: string + required: false + description: Executed price + x-description-zh: 成交价格 + x-description-zh-hk: 成交價格 + - name: └ quantity + type: string + required: false + description: Executed quantity + x-description-zh: 成交数量 + x-description-zh-hk: 成交數量 + - name: └ trade_done_at + type: string + required: false + description: Trade done time, formatted as a timestamp (second) + x-description-zh: 成交时间,格式为时间戳 (秒) + x-description-zh-hk: 成交時間,格式為時間戳 (秒) + - name: └ side + type: string + required: false + description: Trade side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: has_more + type: boolean + required: false + description: has more orders record. The maximum number of orders per query is 1000, if the number of results exceeds 1000, then has_more will be true + x-description-zh: 是否有更多记录。. 单次查询最多返回 1000 条记录,若结果超过 1000 条,has_more 为 true + x-description-zh-hk: 是否有更多記錄。. 單次查詢最多返回 1000 條記錄,若結果超過 1000 條,has_more 為 true + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + has_more: false + trades: + - order_id: '693664675163312128' + price: '388' + quantity: '100' + symbol: 700.HK + trade_done_at: '1648611351' + trade_id: 693664675163312128-1648611351433741210 + side: Buy + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/financial-reports: + get: + operationId: financial_report + summary: Financial Report + x-summary-zh: 财务报告 + x-summary-zh-hk: 財務報告 + description: | + Fetch income statement, balance sheet, and cash flow statement for any public company. + x-description-zh: | + 获取任意上市公司的利润表、资产负债表和现金流量表。 + x-description-zh-hk: | + 獲取任意上市公司的利潤表、資產負債表和現金流量表。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: kind + in: query + type: string + required: true + description: 'Report type: `IncomeStatement`, `BalanceSheet`, `CashFlow`, `All`' + x-description-zh: 报表类型:`IncomeStatement`(利润表)、`BalanceSheet`(资产负债表)、`CashFlow`(现金流量表)、`All`(全部) + x-description-zh-hk: 報表類型:`IncomeStatement`(利潤表)、`BalanceSheet`(資產負債表)、`CashFlow`(現金流量表)、`All`(全部) + - name: period + in: query + type: string + required: true + description: 'Report period: `Annual`, `SemiAnnual`, `Q1`, `Q2`, `Q3`, `ThreeQ`, `QuarterlyFull`' + x-description-zh: 报告期:`Annual`(年报)、`SemiAnnual`(中报)、`Q1`/`Q2`/`Q3`/`ThreeQ`(季报)、`QuarterlyFull`(累计季报) + x-description-zh-hk: 報告期:`Annual`(年報)、`SemiAnnual`(中報)、`Q1`/`Q2`/`Q3`/`ThreeQ`(季報)、`QuarterlyFull`(累計季報) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge financial-report TSLA.US --kind IS + longbridge financial-report AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/financial-reports?symbol=<symbol>&kind=<kind>&period=<period>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/financial-reports", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "kind": "<kind>", "period": "<period>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/financial-reports", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "kind": "<kind>", "period": "<period>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/financial-reports") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("kind", "<kind>") + url.searchParams.set("period", "<period>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/financial-reports?symbol=<symbol>&kind=<kind>&period=<period>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/financial-reports") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("kind", "<kind>"), ("period", "<period>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/financial-reports?symbol=<symbol>&kind=<kind>&period=<period>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/financial-reports?symbol=<symbol>&kind=<kind>&period=<period>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object + required: true + description: 'Report data grouped by kind (key: report type code, e.g. `IS`, `BS`, `CF`)' + x-description-zh: 按报表类型分组的数据(key 为报表类型代码,如 `IS`、`BS`、`CF`) + x-description-zh-hk: 按報表類型分組的數據(key 為報表類型代碼,如 `IS`、`BS`、`CF`) + - name: title + type: string + required: false + description: Indicator title + x-description-zh: 指标标题 + x-description-zh-hk: 指標標題 + - name: short_title + type: string + required: false + description: Short title + x-description-zh: 短标题 + x-description-zh-hk: 短標題 + - name: currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: has_yoy + type: boolean + required: false + description: Whether year-over-year data is available + x-description-zh: 是否有同比数据 + x-description-zh-hk: 是否有同比數據 + - name: entry + type: string + required: false + description: Entry identifier + x-description-zh: 条目标识符 + x-description-zh-hk: 條目標識符 + - name: periods + type: string[] + required: false + description: Available reporting periods + x-description-zh: 可用报告期列表 + x-description-zh-hk: 可用報告期列表 + - name: accounts + type: object[] + required: false + description: List of financial line items + x-description-zh: 财务科目列表, + x-description-zh-hk: 財務科目列表, + - name: field + type: string + required: true + description: Field identifier + x-description-zh: 字段标识符 + x-description-zh-hk: 字段標識符 + - name: percent + type: boolean + required: false + description: Whether the value is a percentage + x-description-zh: 是否为百分比值 + x-description-zh-hk: 是否為百分比值 + - name: tip + type: string + required: false + description: Tooltip description + x-description-zh: 提示说明 + x-description-zh-hk: 提示說明 + - name: values + type: object[] + required: false + description: Historical values by period + x-description-zh: 按报告期的历史数值, + x-description-zh-hk: 按報告期的歷史數值, + - name: period + type: string + required: true + description: Period label (e.g. `FY 2024`) + x-description-zh: 报告期标签(如 `FY 2024`) + x-description-zh-hk: 報告期標簽(如 `FY 2024`) + - name: year + type: integer + required: false + description: Fiscal year + x-description-zh: 财政年度 + x-description-zh-hk: 財政年度 + - name: fp_end + type: string + required: false + description: Period end timestamp + x-description-zh: 报告期结束时间戳 + x-description-zh-hk: 報告期結束時間戳 + - name: value + type: string + required: false + description: Reported value + x-description-zh: 报告值 + x-description-zh-hk: 報告值 + - name: ratio + type: string + required: false + description: Ratio value + x-description-zh: 比率值 + x-description-zh-hk: 比率值 + - name: yoy + type: string + required: false + description: Year-over-year growth rate + x-description-zh: 同比增长率 + x-description-zh-hk: 同比增長率 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + IS: + indicators: + - title: Income Statement + short_title: IS + currency: USD + has_yoy: true + entry: IS + periods: + - FY2025 + - FY2024 + accounts: + - field: EPS + name: Earnings Per Share(USD) + percent: false + tip: '' + values: + - period: FY 2025 + year: 2025 + fp_end: '1758945600' + value: '7.46' + ratio: '' + yoy: '0.227' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/financials/earnings-snapshot: + get: + operationId: financial_report_snapshot + summary: Financial Report Snapshot + x-summary-zh: 财报快照(AI 摘要 + 预测对比) + x-summary-zh-hk: 財報快照(AI 摘要 + 預測對比) + description: | + Get an AI-generated earnings summary, revenue/EBIT/EPS forecast vs actual (beat/miss analysis), and key financial ratios. + x-description-zh: | + 获取 AI 生成的财报摘要、营收/EBIT/EPS 预测对比(超预期/低于预期),以及关键财务指标。 + x-description-zh-hk: | + 獲取 AI 生成的財報摘要、營收/EBIT/EPS 預測對比(超預期/低於預期),以及關鍵財務指標。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: report + in: query + type: string + required: false + description: 'Report type: `qf` (quarterly) / `saf` (semi-annual) / `af` (annual)' + x-description-zh: 报告类型:`qf`(季报)/ `saf`(半年报)/ `af`(年报) + x-description-zh-hk: 報告類型:`qf`(季報)/ `saf`(半年報)/ `af`(年報) + - name: fiscal_year + in: query + type: integer + required: false + description: Fiscal year, e.g. `2024` + x-description-zh: 财政年度,例如 `2024` + x-description-zh-hk: 財政年度,例如 `2024` + - name: fiscal_period + in: query + type: string + required: false + description: Fiscal period, e.g. `1` / `2` / `3` / `4` + x-description-zh: 财政季度,例如 `1` / `2` / `3` / `4` + x-description-zh-hk: 財政季度,例如 `1` / `2` / `3` / `4` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge financial-report snapshot AAPL.US + longbridge financial-report snapshot AAPL.US --report qf --year 2024 --period 4 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/financials/earnings-snapshot?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: ticker + type: string + required: false + description: Ticker symbol without market suffix, e.g. `AAPL` + x-description-zh: 证券代码(不含市场后缀,例如 `AAPL`) + x-description-zh-hk: 證券代碼(不含市場後綴,例如 `AAPL`) + - name: currency + type: string + required: false + description: Currency code + x-description-zh: 货币代码 + x-description-zh-hk: 貨幣代碼 + - name: report + type: string + required: false + description: Report period code (e.g. `af` = annual) + x-description-zh: 报告期代码(如 `af` = 年报) + x-description-zh-hk: 報告期代碼(如 `af` = 年報) + - name: fiscal_year + type: integer + required: false + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: fiscal_period + type: string + required: false + description: Fiscal period number within the fiscal year + x-description-zh: 财年内的财季序号 + x-description-zh-hk: 財年內的財季序號 + - name: opt_reports + type: array + required: false + description: Optional available report periods + x-description-zh: 可选的报告期列表 + x-description-zh-hk: 可選的報告期列表 + - name: fp_start + type: string + required: false + description: Fiscal period start date in `YYYY.MM.DD` format + x-description-zh: 财政期开始日期,格式 `YYYY.MM.DD` + x-description-zh-hk: 財政期開始日期,格式 `YYYY.MM.DD` + - name: fp_end + type: string + required: false + description: Fiscal period end date in `YYYY.MM.DD` format + x-description-zh: 财政期结束日期,格式 `YYYY.MM.DD` + x-description-zh-hk: 財政期結束日期,格式 `YYYY.MM.DD` + - name: report_desc + type: string + required: false + description: AI-generated earnings summary + x-description-zh: AI 生成的财报摘要 + x-description-zh-hk: AI 生成的財報摘要 + - name: fo_revenue + type: object + required: false + description: Revenue forecast vs actual + x-description-zh: 营收预测对比, + x-description-zh-hk: 營收預測對比, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fo_ebit + type: object + required: false + description: EBIT forecast vs actual + x-description-zh: EBIT 预测对比, + x-description-zh-hk: EBIT 預測對比, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fo_eps + type: object + required: false + description: EPS forecast vs actual + x-description-zh: EPS 预测对比, + x-description-zh-hk: EPS 預測對比, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fr_roe_ttm + type: string + required: false + description: ROE TTM as percentage, e.g. `141.47` + x-description-zh: 净资产收益率 TTM(百分比,例如 `141.47`) + x-description-zh-hk: 淨資產收益率 TTM(百分比,例如 `141.47`) + - name: fr_asset_turn_ttm + type: string + required: false + description: Asset turnover TTM as percentage + x-description-zh: 资产周转率 TTM(百分比) + x-description-zh-hk: 資產周轉率 TTM(百分比) + - name: fr_profit_margin_ttm + type: string + required: false + description: Net profit margin TTM as percentage + x-description-zh: 净利率 TTM(百分比) + x-description-zh-hk: 淨利率 TTM(百分比) + - name: fr_leverage_ttm + type: string + required: false + description: Leverage TTM as percentage + x-description-zh: 杠杆率 TTM(百分比) + x-description-zh-hk: 槓桿率 TTM(百分比) + - name: fr_total_assets + type: object + required: false + description: Total assets + x-description-zh: 总资产, + x-description-zh-hk: 總資產, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fr_total_liability + type: object + required: false + description: Total liabilities + x-description-zh: 总负债, + x-description-zh-hk: 總負債, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fr_debt_assets_ratio + type: string + required: false + description: Debt-to-assets ratio as percentage + x-description-zh: 资产负债率(百分比) + x-description-zh-hk: 資產負債率(百分比) + - name: fr_revenue + type: object + required: false + description: Reported revenue + x-description-zh: 营收财务数据, + x-description-zh-hk: 營收財務數據, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fr_profit + type: object + required: false + description: Reported net profit + x-description-zh: 净利润财务数据, + x-description-zh-hk: 淨利潤財務數據, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fr_profit_margin + type: string + required: false + description: Net profit margin as percentage + x-description-zh: 净利率(百分比) + x-description-zh-hk: 淨利率(百分比) + - name: fr_operate_cash + type: object + required: false + description: Operating cash flow + x-description-zh: 经营现金流, + x-description-zh-hk: 經營現金流, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fr_finance_cash + type: object + required: false + description: Financing cash flow + x-description-zh: 融资现金流, + x-description-zh-hk: 融資現金流, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: fr_invest_cash + type: object + required: false + description: Investing cash flow + x-description-zh: 投资现金流, + x-description-zh-hk: 投資現金流, + - name: └ value + type: string + required: false + description: Actual value + x-description-zh: 实际值 + x-description-zh-hk: 實際值 + - name: └ yoy + type: string + required: false + description: Year-over-year growth as percentage, e.g. `16.6` + x-description-zh: 同比增速(百分比,例如 `16.6`) + x-description-zh-hk: 同比增速(百分比,例如 `16.6`) + - name: └ est_value + type: string + required: false + description: Consensus estimate (may be empty) + x-description-zh: 一致预期值(可能为空) + x-description-zh-hk: 一致預期值(可能為空) + - name: └ est_yoy + type: string + required: false + description: Estimated year-over-year change. + x-description-zh: 预估同比变化。 + x-description-zh-hk: 預估同比變化。 + - name: └ cmp + type: string + required: false + description: Comparison value. + x-description-zh: 对比值。 + x-description-zh-hk: 對比值。 + - name: └ cmp_desc + type: string + required: false + description: Beat/miss description (may be empty) + x-description-zh: 超预期/低于预期描述(可能为空) + x-description-zh-hk: 超預期/低於預期描述(可能為空) + - name: market + type: string + required: false + description: 'Market: `US`, `HK`, `CN`, `SG`' + x-description-zh: 市场:`US`、`HK`、`CN`、`SG` + x-description-zh-hk: 市場:`US`、`HK`、`CN`、`SG` + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + name: 苹果 + ticker: AAPL + fp_start: 2025.12.28 + fp_end: 2026.03.28 + currency: USD + report_desc: 概要:苹果(AAPL)的营业收入是 1112 亿(+16.6%);每股收益是 2.01(+21.82%)。 + fo_revenue: + value: '111184000000.0000' + yoy: '16.6' + cmp_desc: '' + est_value: '' + fo_ebit: + value: '35885000000.0000' + yoy: '21.28' + cmp_desc: '' + est_value: '' + fo_eps: + value: '2.0100' + yoy: '21.82' + cmp_desc: '' + est_value: '' + fr_revenue: + value: '111184000000.0000' + yoy: '16.6' + fr_profit: + value: '29578000000.0000' + yoy: '19.36' + fr_roe_ttm: '141.4705' + fr_profit_margin: '26.6027' + fr_debt_assets_ratio: '71.3025' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/financial-consensus-detail: + get: + operationId: consensus + summary: Financial Consensus + x-summary-zh: 机构共识 + x-summary-zh-hk: 機構共識 + description: | + Get financial consensus estimates including revenue, EPS, and net income forecasts. + x-description-zh: | + 获取机构共识预测,包括营收、EPS 和净利润预测。 + x-description-zh-hk: | + 獲取機構共識預測,包括營收、EPS 和淨利潤預測。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `TSLA.US` + x-description-zh: 证券代码,例如 `TSLA.US` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge consensus TSLA.US + longbridge consensus AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/financial-consensus-detail?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/financial-consensus-detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/financial-consensus-detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/financial-consensus-detail") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/financial-consensus-detail?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/financial-consensus-detail") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/financial-consensus-detail?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/financial-consensus-detail?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: List of consensus forecast periods + x-description-zh: 共识预测期列表, + x-description-zh-hk: 共識預測期列表, + - name: └ fiscal_year + type: integer + required: false + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: └ fiscal_period + type: string + required: false + description: Fiscal period number within the fiscal year + x-description-zh: 财年内的财季序号 + x-description-zh-hk: 財年內的財季序號 + - name: └ period_text + type: string + required: false + description: Display period label (e.g. Q1 2027) + x-description-zh: 展示用周期标签(如 Q1 2027) + x-description-zh-hk: 展示用週期標籤(如 Q1 2027) + - name: └ details + type: object[] + required: false + description: List of financial indicator details + x-description-zh: 财务指标详情列表 + x-description-zh-hk: 財務指標詳情列表 + - name: └ ∟ key + type: string + required: false + description: Indicator key (e.g. `eps`, `revenue`) + x-description-zh: 指标键(如 `eps`、`revenue`) + x-description-zh-hk: 指標鍵(如 `eps`、`revenue`) + - name: └ ∟ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ ∟ description + type: string + required: false + description: Full description + x-description-zh: 完整描述 + x-description-zh-hk: 完整描述 + - name: └ ∟ estimate + type: string + required: false + description: Consensus estimate + x-description-zh: 一致预期值 + x-description-zh-hk: 一致預期值 + - name: └ ∟ is_released + type: boolean + required: false + description: Whether actual has been released + x-description-zh: 实际值是否已公布 + x-description-zh-hk: 實際值是否已公佈 + - name: └ ∟ actual + type: string + required: false + description: Actual reported value + x-description-zh: 实际披露值 + x-description-zh-hk: 實際披露值 + - name: └ ∟ comp_value + type: string + required: false + description: Comparison percentage value + x-description-zh: 对比百分比值 + x-description-zh-hk: 對比百分比值 + - name: └ ∟ comp_desc + type: string + required: false + description: Comparison description + x-description-zh: 对比描述 + x-description-zh-hk: 對比描述 + - name: └ ∟ comp + type: string + required: false + description: Comparison result (e.g. `beat`, `miss`) + x-description-zh: 对比结果(如 `beat`、`miss`) + x-description-zh-hk: 對比結果(如 `beat`、`miss`) + - name: current_index + type: integer + required: false + description: Index of the current period in opt_periods + x-description-zh: 当前周期在 opt_periods 中的索引 + x-description-zh-hk: 當前週期在 opt_periods 中的索引 + - name: currency + type: string + required: false + description: Reporting currency + x-description-zh: 报告货币 + x-description-zh-hk: 報告貨幣 + - name: opt_periods + type: array + required: false + description: Available period options + x-description-zh: 可选周期选项 + x-description-zh-hk: 可選週期選項 + - name: current_period + type: string + required: false + description: Current period code (e.g. `qf`) + x-description-zh: 当前周期代码(如 `qf`) + x-description-zh-hk: 當前週期代碼(如 `qf`) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + currency: USD + current_index: 3 + current_period: qf + opt_periods: + - qf + - af + - saf + list: + - fiscal_year: 2026 + fiscal_period: Q2 FY2026 + period_text: Q2 FY2026 + details: + - key: revenue + name: Revenue + estimate: '95000000000' + actual: '' + comp: '' + comp_value: null + comp_desc: '' + description: '' + is_released: false + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/forecast-eps: + get: + operationId: forecast_eps + summary: Forecast EPS + x-summary-zh: EPS 预测 + x-summary-zh-hk: EPS 預測 + description: | + Get EPS forecasts and analyst consensus estimates. + x-description-zh: | + 获取 EPS 预测及分析师共识估值。 + x-description-zh-hk: | + 獲取 EPS 預測及分析師共識估值。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `TSLA.US` + x-description-zh: 证券代码,例如 `TSLA.US` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge forecast-eps TSLA.US + longbridge forecast-eps AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/forecast-eps?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/forecast-eps", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/forecast-eps", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/forecast-eps") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/forecast-eps?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/forecast-eps") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/forecast-eps?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/forecast-eps?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: items + type: object[] + required: false + description: List of EPS forecast periods + x-description-zh: EPS 预测周期列表 + x-description-zh-hk: EPS 預測週期列表 + - name: └ forecast_eps_median + type: string + required: false + description: Median EPS estimate + x-description-zh: EPS 预估中位数 + x-description-zh-hk: EPS 預估中位數 + - name: └ forecast_eps_mean + type: string + required: false + description: Mean EPS estimate + x-description-zh: EPS 预估平均值 + x-description-zh-hk: EPS 預估平均值 + - name: └ forecast_eps_lowest + type: string + required: false + description: Lowest EPS estimate + x-description-zh: 最低 EPS 预估值 + x-description-zh-hk: 最低 EPS 預估值 + - name: └ forecast_eps_highest + type: string + required: false + description: Highest EPS estimate + x-description-zh: 最高 EPS 预估值 + x-description-zh-hk: 最高 EPS 預估值 + - name: └ institution_total + type: integer + required: false + description: Total contributing institutions + x-description-zh: 参与机构总数 + x-description-zh-hk: 參與機構總數 + - name: └ institution_up + type: integer + required: false + description: Institutions revising up + x-description-zh: 上调预期的机构数 + x-description-zh-hk: 上調預期的機構數 + - name: └ institution_down + type: integer + required: false + description: Institutions revising down + x-description-zh: 下调预期的机构数 + x-description-zh-hk: 下調預期的機構數 + - name: └ forecast_start_date + type: string + required: false + description: Forecast period start date + x-description-zh: 预测周期开始日期 + x-description-zh-hk: 預測週期開始日期 + - name: └ forecast_end_date + type: string + required: false + description: Forecast period end date + x-description-zh: 预测周期结束日期 + x-description-zh-hk: 預測週期結束日期 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + items: + - forecast_end_date: '1727827200' + forecast_eps_highest: '3.71' + forecast_eps_lowest: '2.37' + forecast_eps_mean: '2.998' + forecast_eps_median: '3.02' + forecast_start_date: '1727827200' + institution_down: 0 + institution_total: 0 + institution_up: 0 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/valuation: + get: + operationId: valuation + summary: Valuations + x-summary-zh: 估值指标 + x-summary-zh-hk: 估值指標 + description: | + Get current valuation metrics (P/E, P/B, P/S, dividend yield) with 5-year historical context. + x-description-zh: | + 获取当前估值指标(市盈率、市净率、市销率、股息率)及 5 年历史区间数据。 + x-description-zh-hk: | + 獲取當前估值指標(市盈率、市凈率、市銷率、股息率)及 5 年歷史區間數據。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: indicator + in: query + type: string + required: false + description: 'Indicator filter: `pe`, `pb`, `ps`, `dvd_yld`' + x-description-zh: 指标筛选:`pe`、`pb`、`ps`、`dvd_yld` + x-description-zh-hk: 指標篩選:`pe`、`pb`、`ps`、`dvd_yld` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge valuation TSLA.US --indicator pe + longbridge valuation AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/valuation?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/valuation", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/valuation", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/valuation") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/valuation?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/valuation") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/valuation?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/valuation?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: metrics + type: object + required: true + description: Valuation metrics map + x-description-zh: 估值指标映射 + x-description-zh-hk: 估值指標映射 + - name: └ pe + type: object + required: false + description: P/E ratio data + x-description-zh: 市盈率数据 + x-description-zh-hk: 市盈率數據 + - name: └ ∟ current + type: string + required: true + description: Current value + x-description-zh: 当前值 + x-description-zh-hk: 當前值 + - name: └ ∟ high + type: string + required: true + description: 5-year high + x-description-zh: 5 年最高值 + x-description-zh-hk: 5 年最高值 + - name: └ ∟ low + type: string + required: true + description: 5-year low + x-description-zh: 5 年最低值 + x-description-zh-hk: 5 年最低值 + - name: └ ∟ median + type: string + required: true + description: 5-year median + x-description-zh: 5 年中位值 + x-description-zh-hk: 5 年中位值 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + metrics: + pe: + current: '29.5' + high: '35.2' + low: '18.0' + median: '26.0' + pb: + current: '45.1' + high: '50.0' + low: '30.0' + median: '42.0' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/valuation/detail: + get: + operationId: valuation_history + summary: Valuation History + x-summary-zh: 估值历史 + x-summary-zh-hk: 估值歷史 + description: | + Get historical valuation metric time series (PE, PB, PS, dividend yield). + x-description-zh: | + 获取历史估值指标时间序列(市盈率、市净率、市销率、股息率)。 + x-description-zh-hk: | + 獲取歷史估值指標時間序列(市盈率、市淨率、市銷率、股息率)。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/valuation/detail?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/valuation/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/valuation/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/valuation/detail") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/valuation/detail?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/valuation/detail") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/valuation/detail?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/valuation/detail?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: overview + type: object + required: true + description: 'Current-valuation summary' + x-description-zh: '当前估值概览' + x-description-zh-hk: '當前估值概覽' + - name: overview.metrics + type: object + required: false + description: 'Metrics map keyed by indicator (pe/pb/ps)' + x-description-zh: '按指标(pe/pb/ps)分组的指标映射' + x-description-zh-hk: '按指標(pe/pb/ps)分組的指標映射' + - name: overview.metrics.pe + type: object + required: false + description: 'Metric block for the requested indicator' + x-description-zh: '所请求指标的数据块' + x-description-zh-hk: '所請求指標的數據塊' + - name: overview.metrics.pe.metric + type: string + required: false + description: 'Current value, e.g. 14.85x' + x-description-zh: '当前值,如 14.85x' + x-description-zh-hk: '當前值,如 14.85x' + - name: overview.metrics.pe.industry_median + type: string + required: false + description: 'Industry median' + x-description-zh: '行业中位数' + x-description-zh-hk: '行業中位數' + - name: overview.metrics.pe.desc + type: string + required: false + description: 'Human-readable summary' + x-description-zh: '可读描述' + x-description-zh-hk: '可讀描述' + - name: overview.indicator + type: string + required: false + description: 'Requested indicator key' + x-description-zh: '请求的指标键' + x-description-zh-hk: '請求的指標鍵' + - name: overview.range + type: integer + required: false + description: 'Lookback range in years' + x-description-zh: '回溯年数' + x-description-zh-hk: '回溯年數' + - name: overview.date + type: string + required: false + description: 'As-of date' + x-description-zh: '数据日期' + x-description-zh-hk: '數據日期' + - name: overview.ccy_symbol + type: string + required: false + description: 'Currency symbol' + x-description-zh: '货币符号' + x-description-zh-hk: '貨幣符號' + - name: history + type: object + required: true + description: 'Historical time series' + x-description-zh: '历史时间序列' + x-description-zh-hk: '歷史時間序列' + - name: history.metrics.pe + type: object + required: false + description: 'History block for the indicator' + x-description-zh: '该指标的历史数据块' + x-description-zh-hk: '該指標的歷史數據塊' + - name: history.metrics.pe.low + type: string + required: false + description: 'Historical low' + x-description-zh: '历史低值' + x-description-zh-hk: '歷史低值' + - name: history.metrics.pe.median + type: string + required: false + description: 'Historical median' + x-description-zh: '历史中位数' + x-description-zh-hk: '歷史中位數' + - name: history.metrics.pe.high + type: string + required: false + description: 'Historical high' + x-description-zh: '历史高值' + x-description-zh-hk: '歷史高值' + - name: history.metrics.pe.list + type: object[] + required: false + description: 'Time-series points' + x-description-zh: '历史数据点列表' + x-description-zh-hk: '歷史數據點列表' + - name: history.metrics.pe.list[].timestamp + type: string + required: false + description: 'Unix seconds (string)' + x-description-zh: 'Unix 秒(字符串)' + x-description-zh-hk: 'Unix 秒(字串)' + - name: history.metrics.pe.list[].value + type: string + required: false + description: 'Value at this point' + x-description-zh: '该点数值' + x-description-zh-hk: '該點數值' + - name: peers + type: object + required: false + description: 'Industry peers comparison' + x-description-zh: '行业可比公司' + x-description-zh-hk: '行業可比公司' + - name: peers.pe.industry_median + type: string + required: false + description: 'Peer-group median' + x-description-zh: '同业中位数' + x-description-zh-hk: '同業中位數' + - name: peers.pe.list + type: object[] + required: false + description: 'Peer companies' + x-description-zh: '同业公司列表' + x-description-zh-hk: '同業公司列表' + - name: peers.pe.list[].symbol + type: string + required: false + description: 'Security symbol, e.g. 9626.HK' + x-description-zh: '标的代码,如 9626.HK' + x-description-zh-hk: '標的代碼,如 9626.HK' + - name: peers.pe.list[].name + type: string + required: false + description: 'Security name' + x-description-zh: '标的名称' + x-description-zh-hk: '標的名稱' + - name: peers.pe.list[].value + type: string + required: false + description: 'Metric value' + x-description-zh: '指标值' + x-description-zh-hk: '指標值' + - name: layouts + type: object + required: false + description: 'Distribution buckets for charting' + x-description-zh: '用于绘图的分布分组' + x-description-zh-hk: '用於繪圖的分佈分組' + - name: symbols + type: object + required: false + description: 'Map keyed by symbol -> { name, market_cap }' + x-description-zh: '以 symbol 为键的映射 -> { name, market_cap }' + x-description-zh-hk: '以 symbol 為鍵的映射 -> { name, market_cap }' + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + history: + metrics: + pe: + desc: P/E Ratio + high: '35.2' + low: '18.1' + median: '26.5' + list: + - timestamp: '1622520000' + value: '28.5' + pb: null + ps: null + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/compare/valuation: + get: + operationId: valuation_comparison + summary: Valuation Comparison + x-summary-zh: 多股估值对比 + x-summary-zh-hk: 多股估值對比 + description: | + Compare valuation metrics (PE/PB/PS/market cap/close price) across multiple stocks. When no comparison symbols are provided, the server automatically selects peers from the same industry. + x-description-zh: | + 对比多只股票的估值指标(PE/PB/PS/市值/收盘价)。不传对比股票时,服务端自动选取同行业标的。 + x-description-zh-hk: | + 對比多隻股票的估值指標(PE/PB/PS/市值/收盤價)。不傳對比股票時,服務端自動選取同行業標的。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Primary security symbol, e.g. `AAPL.US` + x-description-zh: 主标的证券代码,例如 `AAPL.US` + x-description-zh-hk: 主標的證券代碼,例如 `AAPL.US` + - name: currency + in: query + type: string + required: true + description: 'Result currency: `USD`, `HKD`, or `CNY`' + x-description-zh: 结果货币:`USD`、`HKD`、`CNY` + x-description-zh-hk: 結果貨幣:`USD`、`HKD`、`CNY` + - name: comparison_symbols + in: query + type: array + required: false + description: Symbols to compare against; if omitted, the server auto-selects industry peers + x-description-zh: 对比股票代码列表;不传时服务端自动选取同行业标的 + x-description-zh-hk: 對比股票代碼列表;不傳時服務端自動選取同行業標的 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge compare AAPL.US + longbridge compare AAPL.US MSFT.US GOOGL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/compare/valuation?symbol=<symbol>¤cy=<currency>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/compare/valuation", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "currency": "<currency>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/compare/valuation", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "currency": "<currency>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/compare/valuation") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("currency", "<currency>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/compare/valuation?symbol=<symbol>¤cy=<currency>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/compare/valuation") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("currency", "<currency>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/compare/valuation?symbol=<symbol>¤cy=<currency>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/compare/valuation?symbol=<symbol>¤cy=<currency>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Valuation comparison list + x-description-zh: 股票估值对比列表 + x-description-zh-hk: 股票估值對比列表 + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ name + type: string + required: false + description: Security name + x-description-zh: 证券名称 + x-description-zh-hk: 證券名稱 + - name: └ market + type: string + required: false + description: 'Market: `US`, `HK`, `CN`, `SG`' + x-description-zh: 市场:`US`、`HK`、`CN`、`SG` + x-description-zh-hk: 市場:`US`、`HK`、`CN`、`SG` + - name: └ currency + type: string + required: false + description: Currency of the values + x-description-zh: 数值所用货币 + x-description-zh-hk: 數值所用貨幣 + - name: └ price_close + type: string + required: false + description: Latest closing price + x-description-zh: 最新收盘价 + x-description-zh-hk: 最新收盤價 + - name: └ market_value + type: string + required: false + description: Market capitalisation + x-description-zh: 市值 + x-description-zh-hk: 市值 + - name: └ volume + type: string + required: false + description: Volume. + x-description-zh: 成交量。 + x-description-zh-hk: 成交量。 + - name: └ pe + type: string + required: false + description: P/E ratio (TTM) + x-description-zh: 市盈率(TTM) + x-description-zh-hk: 市盈率(TTM) + - name: └ pb + type: string + required: false + description: P/B ratio + x-description-zh: 市净率 + x-description-zh-hk: 市淨率 + - name: └ ps + type: string + required: false + description: P/S ratio (TTM) + x-description-zh: 市销率(TTM) + x-description-zh-hk: 市銷率(TTM) + - name: └ div_yld + type: string + required: false + description: Dividend yield (%) + x-description-zh: 股息率(%) + x-description-zh-hk: 股息率(%) + - name: └ roa + type: string + required: false + description: Return on assets (ROA). + x-description-zh: 资产回报率(ROA)。 + x-description-zh-hk: 資產回報率(ROA)。 + - name: └ roe + type: string + required: false + description: Return on equity (%) + x-description-zh: 净资产收益率(%) + x-description-zh-hk: 淨資產收益率(%) + - name: └ turnover + type: string + required: false + description: Turnover. + x-description-zh: 换手率/成交额。 + x-description-zh-hk: 換手率/成交額。 + - name: └ net_margin + type: string + required: false + description: Net profit margin + x-description-zh: 净利润率 + x-description-zh-hk: 淨利潤率 + - name: └ leverage + type: string + required: false + description: Leverage ratio. + x-description-zh: 杠杆比率。 + x-description-zh-hk: 槓桿比率。 + - name: └ liabilities_assets + type: string + required: false + description: Liabilities-to-assets ratio. + x-description-zh: 资产负债率。 + x-description-zh-hk: 資產負債率。 + - name: └ eps + type: string + required: false + description: Earnings per share (TTM) + x-description-zh: 每股收益(TTM) + x-description-zh-hk: 每股收益(TTM) + - name: └ sales_ps + type: string + required: false + description: Sales per share. + x-description-zh: 每股营收。 + x-description-zh-hk: 每股營收。 + - name: └ bps + type: string + required: false + description: Book value per share + x-description-zh: 每股净资产 + x-description-zh-hk: 每股淨資產 + - name: └ dps + type: string + required: false + description: Dividends per share (TTM) + x-description-zh: 每股派息(TTM) + x-description-zh-hk: 每股派息(TTM) + - name: └ five_y_avg_dps + type: string + required: false + description: 5-year average DPS + x-description-zh: 5 年平均每股股息 + x-description-zh-hk: 5 年平均每股股息 + - name: └ div_payout_ratio + type: string + required: false + description: Dividend payout ratio + x-description-zh: 股息支付率 + x-description-zh-hk: 股息支付率 + - name: └ assets + type: string + required: false + description: Total assets + x-description-zh: 总资产 + x-description-zh-hk: 總資產 + - name: └ liabilities + type: string + required: false + description: Total liabilities. + x-description-zh: 总负债。 + x-description-zh-hk: 總負債。 + - name: └ sales + type: string + required: false + description: Sales / revenue. + x-description-zh: 营收。 + x-description-zh-hk: 營收。 + - name: └ net_income + type: string + required: false + description: Net income + x-description-zh: 净利润 + x-description-zh-hk: 淨利潤 + - name: └ history + type: object[] + required: false + description: Historical valuation time series + x-description-zh: 历史估值时间序列 + x-description-zh-hk: 歷史估值時間序列 + - name: └ ∟ date + type: string + required: false + description: Date as Unix timestamp (seconds) + x-description-zh: 日期(Unix 时间戳,秒) + x-description-zh-hk: 日期(Unix 時間戳,秒) + - name: └ ∟ pe + type: string + required: false + description: P/E ratio (TTM) + x-description-zh: 市盈率(TTM) + x-description-zh-hk: 市盈率(TTM) + - name: └ ∟ pb + type: string + required: false + description: P/B ratio + x-description-zh: 市净率 + x-description-zh-hk: 市淨率 + - name: └ ∟ ps + type: string + required: false + description: P/S ratio (TTM) + x-description-zh: 市销率(TTM) + x-description-zh-hk: 市銷率(TTM) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + - symbol: AAPL.US + name: Apple Inc. + currency: USD + market_value: '3241500000000' + price_close: '213.49' + pe: '32.15' + pb: '50.21' + ps: '8.04' + roe: '136.45' + eps: '6.43' + bps: '4.38' + dps: '0.99' + div_yld: '0.46' + assets: '371082000000' + history: + - date: '1622520000' + pe: '37.56' + pb: '30.16' + ps: '6.41' + - date: '1625112000' + pe: '41.49' + pb: '35.64' + ps: '6.60' + - symbol: MSFT.US + name: Microsoft + currency: USD + market_value: '3085000000000' + price_close: '415.32' + pe: '35.42' + pb: '12.87' + ps: '12.61' + roe: '38.21' + eps: '11.72' + bps: '32.28' + dps: '3.32' + div_yld: '0.80' + assets: '512163000000' + history: + - date: '1622520000' + pe: '33.12' + pb: '11.94' + ps: '11.84' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/industry-valuation-comparison: + get: + operationId: industry_valuation + summary: Industry Valuation + x-summary-zh: 行业估值对比 + x-summary-zh-hk: 行業估值對比 + description: | + Get peer valuation comparison within the same industry. + x-description-zh: | + 获取同行业内的同类公司估值对比数据。 + x-description-zh-hk: | + 獲取同行業內的同類公司估值對比數據。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `TSLA.US` + x-description-zh: 证券代码,例如 `TSLA.US` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge industry-valuation TSLA.US + longbridge industry-valuation AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/industry-valuation-comparison?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/industry-valuation-comparison", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/industry-valuation-comparison", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/industry-valuation-comparison") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/industry-valuation-comparison?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/industry-valuation-comparison") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/industry-valuation-comparison?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/industry-valuation-comparison?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: List of peer companies + x-description-zh: 同行公司列表, + x-description-zh-hk: 同行公司列表, + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ name + type: string + required: false + description: Company name + x-description-zh: 公司名称 + x-description-zh-hk: 公司名稱 + - name: └ market + type: string + required: false + description: 'Market: `US`, `HK`, `CN`, `SG`' + x-description-zh: 市场:`US`、`HK`、`CN`、`SG` + x-description-zh-hk: 市場:`US`、`HK`、`CN`、`SG` + - name: └ currency + type: string + required: false + description: Reporting currency + x-description-zh: 报告货币 + x-description-zh-hk: 報告貨幣 + - name: └ price_close + type: string + required: false + description: Latest closing price + x-description-zh: 最新收盘价 + x-description-zh-hk: 最新收盤價 + - name: └ market_value + type: string + required: false + description: Market capitalisation + x-description-zh: 市值 + x-description-zh-hk: 市值 + - name: └ volume + type: string + required: false + description: Volume. + x-description-zh: 成交量。 + x-description-zh-hk: 成交量。 + - name: └ pe + type: string + required: false + description: Price-to-Earnings ratio + x-description-zh: 市盈率 + x-description-zh-hk: 市盈率 + - name: └ pb + type: string + required: false + description: P/B ratio + x-description-zh: 市净率 + x-description-zh-hk: 市淨率 + - name: └ ps + type: string + required: false + description: P/S ratio (TTM) + x-description-zh: 市销率(TTM) + x-description-zh-hk: 市銷率(TTM) + - name: └ div_yld + type: string + required: false + description: Dividend yield + x-description-zh: 股息率 + x-description-zh-hk: 股息率 + - name: └ roa + type: string + required: false + description: Return on assets (ROA). + x-description-zh: 资产回报率(ROA)。 + x-description-zh-hk: 資產回報率(ROA)。 + - name: └ roe + type: string + required: false + description: Return on equity (%) + x-description-zh: 净资产收益率(%) + x-description-zh-hk: 淨資產收益率(%) + - name: └ turnover + type: string + required: false + description: Turnover. + x-description-zh: 换手率/成交额。 + x-description-zh-hk: 換手率/成交額。 + - name: └ net_margin + type: string + required: false + description: Net profit margin + x-description-zh: 净利润率 + x-description-zh-hk: 淨利潤率 + - name: └ leverage + type: string + required: false + description: Leverage ratio. + x-description-zh: 杠杆比率。 + x-description-zh-hk: 槓桿比率。 + - name: └ liabilities_assets + type: string + required: false + description: Liabilities-to-assets ratio. + x-description-zh: 资产负债率。 + x-description-zh-hk: 資產負債率。 + - name: └ eps + type: string + required: false + description: Earnings per share + x-description-zh: 每股收益 + x-description-zh-hk: 每股收益 + - name: └ sales_ps + type: string + required: false + description: Sales per share. + x-description-zh: 每股营收。 + x-description-zh-hk: 每股營收。 + - name: └ bps + type: string + required: false + description: Book value per share + x-description-zh: 每股净资产 + x-description-zh-hk: 每股淨資產 + - name: └ dps + type: string + required: false + description: Dividends per share + x-description-zh: 每股股息 + x-description-zh-hk: 每股股息 + - name: └ five_y_avg_dps + type: string + required: false + description: 5-year average DPS + x-description-zh: 5 年平均每股股息 + x-description-zh-hk: 5 年平均每股股息 + - name: └ div_payout_ratio + type: string + required: false + description: Dividend payout ratio + x-description-zh: 股息支付率 + x-description-zh-hk: 股息支付率 + - name: └ assets + type: string + required: false + description: Total assets + x-description-zh: 总资产 + x-description-zh-hk: 總資產 + - name: └ liabilities + type: string + required: false + description: Total liabilities. + x-description-zh: 总负债。 + x-description-zh-hk: 總負債。 + - name: └ sales + type: string + required: false + description: Sales / revenue. + x-description-zh: 营收。 + x-description-zh-hk: 營收。 + - name: └ net_income + type: string + required: false + description: Net income + x-description-zh: 净利润 + x-description-zh-hk: 淨利潤 + - name: └ history + type: object[] + required: false + description: Historical valuation data + x-description-zh: 历史估值数据 + x-description-zh-hk: 歷史估值數據 + - name: └ ∟ date + type: string + required: false + description: Date (e.g. `2026.05.13`) + x-description-zh: 日期(如 `2026.05.13`) + x-description-zh-hk: 日期(如 `2026.05.13`) + - name: └ ∟ pe + type: string + required: false + description: Price-to-Earnings ratio + x-description-zh: 市盈率 + x-description-zh-hk: 市盈率 + - name: └ ∟ pb + type: string + required: false + description: P/B ratio + x-description-zh: 市净率 + x-description-zh-hk: 市淨率 + - name: └ ∟ ps + type: string + required: false + description: P/S ratio (TTM) + x-description-zh: 市销率(TTM) + x-description-zh-hk: 市銷率(TTM) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + - symbol: AAPL.US + name: Apple Inc. + market: US + currency: USD + pe: '28.50' + pb: '45.2' + ps: '7.8' + eps: '6.08' + bps: '4.50' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/industry-valuation-distribution: + get: + operationId: industry_valuation_dist + summary: Industry Valuation Distribution + x-summary-zh: 行业估值分布 + x-summary-zh-hk: 行業估值分佈 + description: | + Get the valuation distribution histogram for the symbol's industry. + x-description-zh: | + 获取该证券所在行业的估值分布直方图。 + x-description-zh-hk: | + 獲取該證券所在行業的估值分佈直方圖。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/industry-valuation-distribution?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/industry-valuation-distribution", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/industry-valuation-distribution", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/industry-valuation-distribution") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/industry-valuation-distribution?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/industry-valuation-distribution") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/industry-valuation-distribution?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/industry-valuation-distribution?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: pe + type: object + required: false + description: P/E ratio distribution + x-description-zh: 市盈率分布 + x-description-zh-hk: 市盈率分佈 + - name: └ low + type: string + required: false + description: 5-year low + x-description-zh: 5 年最低值 + x-description-zh-hk: 5 年最低值 + - name: └ high + type: string + required: false + description: 5-year high + x-description-zh: 5 年最高值 + x-description-zh-hk: 5 年最高值 + - name: └ median + type: string + required: false + description: 5-year median + x-description-zh: 5 年中位值 + x-description-zh-hk: 5 年中位值 + - name: └ value + type: string + required: false + description: Indicator value as a string; empty when unavailable. + x-description-zh: 指标值(字符串),无数据时为空。 + x-description-zh-hk: 指標值(字符串),無數據時為空。 + - name: └ ranking + type: string + required: false + description: Ranking position. + x-description-zh: 排名。 + x-description-zh-hk: 排名。 + - name: └ rank_index + type: string + required: false + description: Rank index (position). + x-description-zh: 排名序号。 + x-description-zh-hk: 排名序號。 + - name: └ rank_total + type: string + required: false + description: Total number in the ranking. + x-description-zh: 排名总数。 + x-description-zh-hk: 排名總數。 + - name: pb + type: object + required: false + description: P/B ratio distribution + x-description-zh: 市净率分布 + x-description-zh-hk: 市淨率分佈 + - name: └ low + type: string + required: false + description: 5-year low + x-description-zh: 5 年最低值 + x-description-zh-hk: 5 年最低值 + - name: └ high + type: string + required: false + description: 5-year high + x-description-zh: 5 年最高值 + x-description-zh-hk: 5 年最高值 + - name: └ median + type: string + required: false + description: 5-year median + x-description-zh: 5 年中位值 + x-description-zh-hk: 5 年中位值 + - name: └ value + type: string + required: false + description: Indicator value as a string; empty when unavailable. + x-description-zh: 指标值(字符串),无数据时为空。 + x-description-zh-hk: 指標值(字符串),無數據時為空。 + - name: └ ranking + type: string + required: false + description: Ranking position. + x-description-zh: 排名。 + x-description-zh-hk: 排名。 + - name: └ rank_index + type: string + required: false + description: Rank index (position). + x-description-zh: 排名序号。 + x-description-zh-hk: 排名序號。 + - name: └ rank_total + type: string + required: false + description: Total number in the ranking. + x-description-zh: 排名总数。 + x-description-zh-hk: 排名總數。 + - name: ps + type: object + required: false + description: P/S ratio distribution + x-description-zh: 市销率分布 + x-description-zh-hk: 市銷率分佈 + - name: └ low + type: string + required: false + description: 5-year low + x-description-zh: 5 年最低值 + x-description-zh-hk: 5 年最低值 + - name: └ high + type: string + required: false + description: 5-year high + x-description-zh: 5 年最高值 + x-description-zh-hk: 5 年最高值 + - name: └ median + type: string + required: false + description: 5-year median + x-description-zh: 5 年中位值 + x-description-zh-hk: 5 年中位值 + - name: └ value + type: string + required: false + description: Indicator value as a string; empty when unavailable. + x-description-zh: 指标值(字符串),无数据时为空。 + x-description-zh-hk: 指標值(字符串),無數據時為空。 + - name: └ ranking + type: string + required: false + description: Ranking position. + x-description-zh: 排名。 + x-description-zh-hk: 排名。 + - name: └ rank_index + type: string + required: false + description: Rank index (position). + x-description-zh: 排名序号。 + x-description-zh-hk: 排名序號。 + - name: └ rank_total + type: string + required: false + description: Total number in the ranking. + x-description-zh: 排名总数。 + x-description-zh-hk: 排名總數。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + pe: + value: '28.5' + high: '120.0' + low: '5.0' + median: '22.0' + ranking: '35' + rank_index: '12' + rank_total: '30' + pb: + value: '45.2' + high: '200.0' + low: '1.0' + median: '8.0' + ranking: '85' + rank_index: '25' + rank_total: '30' + ps: + value: '7.8' + high: '30.0' + low: '0.5' + median: '4.0' + ranking: '70' + rank_index: '21' + rank_total: '30' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/industry/rank: + get: + operationId: industry_rank + summary: Industry Ranking + x-summary-zh: 行业排行榜 + x-summary-zh-hk: 行業排行榜 + description: | + Get the industry ranking list by market and indicator. The returned Counter ID can be passed directly to `industry_peers` to explore the sub-sector hierarchy. + x-description-zh: | + 按市场和指标获取行业排行榜。返回的 Counter ID 可直接传入 `industry_peers` 查询子行业树。 + x-description-zh-hk: | + 按市場和指標獲取行業排行榜。返回的 Counter ID 可直接傳入 `industry_peers` 查詢子行業樹。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: market + in: query + type: string + required: true + description: 'Market code: `US` / `HK` / `CN` / `SG`' + x-description-zh: 市场代码:`US` / `HK` / `CN` / `SG` + x-description-zh-hk: 市場代碼:`US` / `HK` / `CN` / `SG` + - name: indicator + in: query + type: string + required: true + description: 'Ranking indicator: `leading-gainer` / `today-trend` / `popularity` / `market-cap` / `revenue` / `revenue-growth` / `net-profit` / `net-profit-growth`' + x-description-zh: 排行指标:`leading-gainer` / `today-trend` / `popularity` / `market-cap` / `revenue` / `revenue-growth` / `net-profit` / `net-profit-growth` + x-description-zh-hk: 排行指標:`leading-gainer` / `today-trend` / `popularity` / `market-cap` / `revenue` / `revenue-growth` / `net-profit` / `net-profit-growth` + - name: sort_type + in: query + type: string + required: true + description: 'Sort mode: `single` / `multi`' + x-description-zh: 排序方式:`single`(单一排序)/ `multi`(多维排序) + x-description-zh-hk: 排序方式:`single`(單一排序)/ `multi`(多維排序) + - name: limit + in: query + type: integer + required: true + description: Number of results to return, default 20 + x-description-zh: 返回条数,默认 20 + x-description-zh-hk: 返回條數,預設 20 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge industry-rank --market US + longbridge industry-rank --market HK --indicator market-cap + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/industry/rank?market=<market>&indicator=<indicator>&sort_type=<sort_type>&limit=<limit>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/industry/rank", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>", "indicator": "<indicator>", "sort_type": "<sort_type>", "limit": "<limit>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/industry/rank", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>", "indicator": "<indicator>", "sort_type": "<sort_type>", "limit": "<limit>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/industry/rank") + url.searchParams.set("market", "<market>") + url.searchParams.set("indicator", "<indicator>") + url.searchParams.set("sort_type", "<sort_type>") + url.searchParams.set("limit", "<limit>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/industry/rank?market=<market>&indicator=<indicator>&sort_type=<sort_type>&limit=<limit>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/industry/rank") + .header("Authorization", "Bearer <access_token>") + .query(&[("market", "<market>"), ("indicator", "<indicator>"), ("sort_type", "<sort_type>"), ("limit", "<limit>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/industry/rank?market=<market>&indicator=<indicator>&sort_type=<sort_type>&limit=<limit>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/industry/rank?market=<market>&indicator=<indicator>&sort_type=<sort_type>&limit=<limit>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: items + type: object[] + required: false + description: Ranked group list + x-description-zh: 排行分组列表 + x-description-zh-hk: 排行分組列表 + - name: └ lists + type: object[] + required: false + description: Industry item list + x-description-zh: 行业条目列表 + x-description-zh-hk: 行業條目列表 + - name: └ ∟ name + type: string + required: false + description: Industry name + x-description-zh: 行业名称 + x-description-zh-hk: 行業名稱 + - name: └ ∟ symbol + type: string + required: false + description: Industry symbol, usable as `symbol` in `industry_peers` (e.g. `IN20245.HK`). + x-description-zh: 行业标识,可作为 `symbol` 传入 `industry_peers`(如 `IN20245.HK`)。 + x-description-zh-hk: 行業標識,可作為 `symbol` 傳入 `industry_peers`(如 `IN20245.HK`)。 + - name: └ ∟ chg + type: string + required: false + description: Daily change (decimal) + x-description-zh: 当日涨跌幅(小数) + x-description-zh-hk: 當日漲跌幅(小數) + - name: └ ∟ leading_name + type: string + required: false + description: Leading stock name + x-description-zh: 涨幅领先个股名称 + x-description-zh-hk: 漲幅領先個股名稱 + - name: └ ∟ leading_ticker + type: string + required: false + description: Leading stock ticker + x-description-zh: 涨幅领先个股代码 + x-description-zh-hk: 漲幅領先個股代碼 + - name: └ ∟ leading_chg + type: string + required: false + description: Leading stock daily change (decimal) + x-description-zh: 涨幅领先个股涨跌幅(小数) + x-description-zh-hk: 漲幅領先個股漲跌幅(小數) + - name: └ ∟ value_name + type: string + required: false + description: Indicator label (may be empty depending on indicator type) + x-description-zh: 指标名称(按指标类型填充,可能为空) + x-description-zh-hk: 指標名稱(按指標類型填充,可能為空) + - name: └ ∟ value_data + type: string + required: false + description: Indicator value (may be empty) + x-description-zh: 指标数值(可能为空) + x-description-zh-hk: 指標數值(可能為空) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + items: + - lists: + - name: Technology + symbol: IN00258.US + chg: '0.0231' + leading_name: NVIDIA + leading_ticker: NVDA.US + leading_chg: '0.0512' + value_name: '' + value_data: '' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/industries/peers: + get: + operationId: industry_peers + summary: Industry Peer Hierarchy + x-summary-zh: 行业子板块层级树 + x-summary-zh-hk: 行業子板塊層級樹 + description: | + Get the hierarchical sub-sector tree for an industry group, including stock count, daily change, and YTD change at each node. Counter IDs come from `industry_rank`. + x-description-zh: | + 获取行业分组的层级子板块树,含各节点股票数量、日涨跌幅和年初至今涨跌幅。Counter ID 可从 `industry_rank` 返回结果中获取。 + x-description-zh-hk: | + 獲取行業分組的層級子板塊樹,含各節點股票數量、日漲跌幅和年初至今漲跌幅。Counter ID 可從 `industry_rank` 返回結果中獲取。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Industry symbol, e.g. `IN20245.HK`. + x-description-zh: 行业标识,如 `IN20245.HK`。 + x-description-zh-hk: 行業標識,如 `IN20245.HK`。 + - name: market + in: query + type: string + required: true + description: 'Market code: `US` / `HK` / `CN` / `SG`' + x-description-zh: 市场代码:`US` / `HK` / `CN` / `SG` + x-description-zh-hk: 市場代碼:`US` / `HK` / `CN` / `SG` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge industry-peers BK/US/IN00258 + longbridge industry-peers BK/HK/IN20337 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/industries/peers?symbol=<symbol>&market=<market>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/industries/peers", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "market": "<market>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/industries/peers", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "market": "<market>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/industries/peers") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("market", "<market>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/industries/peers?symbol=<symbol>&market=<market>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/industries/peers") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("market", "<market>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/industries/peers?symbol=<symbol>&market=<market>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/industries/peers?symbol=<symbol>&market=<market>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: top + type: object + required: false + description: Top-level industry info + x-description-zh: 顶层行业信息, + x-description-zh-hk: 頂層行業資訊, + - name: chain + type: object + required: false + description: Industry hierarchy root node + x-description-zh: 行业层级树根节点, + x-description-zh-hk: 行業層級樹根節點, + - name: market + type: string + required: false + description: Market code + x-description-zh: 市场代码 + x-description-zh-hk: 市場代碼 + - name: symbol + type: string + required: false + description: Sector symbol (present on the root node; empty string on child nodes). + x-description-zh: 板块标识(根节点上有值,子节点为空字符串)。 + x-description-zh-hk: 板塊標識(根節點上有值,子節點為空字符串)。 + - name: stock_num + type: integer + required: false + description: Number of stocks in this sector + x-description-zh: 板块内股票数量 + x-description-zh-hk: 板塊內股票數量 + - name: chg + type: string + required: false + description: Daily change (decimal; may be empty string) + x-description-zh: 当日涨跌幅(小数,可能为空字符串) + x-description-zh-hk: 當日漲跌幅(小數,可能為空字串) + - name: ytd_chg + type: string + required: false + description: Year-to-date change (decimal; may be empty string) + x-description-zh: 年初至今涨跌幅(小数,可能为空字符串) + x-description-zh-hk: 年初至今漲跌幅(小數,可能為空字串) + - name: next + type: object[] + required: false + description: Child sector list with the same structure (recursive) + x-description-zh: 子板块列表,结构与当前节点相同(递归) + x-description-zh-hk: 子板塊列表,結構與當前節點相同(遞迴) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + top: + name: All Industries + market: US + chain: + name: Technology + symbol: IN00258.US + stock_num: 542 + chg: '0.0231' + ytd_chg: '0.0875' + next: + - name: 在线消费电子产品零售 + symbol: '' + stock_num: 4 + chg: '0.0268' + ytd_chg: '-0.1869' + next: [] + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/comp-overview: + get: + operationId: company_profile + summary: Company Profile + x-summary-zh: 公司概况 + x-summary-zh-hk: 公司概況 + description: | + Get a company's profile information including founding year, employee count, headquarters, and description. + x-description-zh: | + 获取公司基本资料,包括成立年份、员工人数、总部地址和业务描述。 + x-description-zh-hk: | + 獲取公司基本資料,包括成立年份、員工人數、總部地址和業務描述。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge company TSLA.US + longbridge company AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/comp-overview?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/comp-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/comp-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/comp-overview") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/comp-overview?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/comp-overview") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/comp-overview?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/comp-overview?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: company_name + type: string + required: false + description: Full company name + x-description-zh: 完整公司名称 + x-description-zh-hk: 完整公司名稱 + - name: founded + type: string + required: false + description: Founding year + x-description-zh: 成立年份 + x-description-zh-hk: 成立年份 + - name: listing_date + type: string + required: false + description: IPO listing date + x-description-zh: 上市日期 + x-description-zh-hk: 上市日期 + - name: market + type: string + required: false + description: Listing exchange + x-description-zh: 上市交易所 + x-description-zh-hk: 上市交易所 + - name: ticker + type: string + required: false + description: Ticker symbol + x-description-zh: 股票代码 + x-description-zh-hk: 股票代碼 + - name: region + type: string + required: false + description: Region + x-description-zh: 地区 + x-description-zh-hk: 地區 + - name: address + type: string + required: false + description: Registered address + x-description-zh: 注册地址 + x-description-zh-hk: 註冊地址 + - name: website + type: string + required: false + description: Company website + x-description-zh: 公司官网 + x-description-zh-hk: 公司官網 + - name: icon + type: string + required: false + description: Stock icon URL + x-description-zh: 股票图标 URL + x-description-zh-hk: 股票圖標 URL + - name: issue_price + type: string + required: false + description: IPO issue price + x-description-zh: 发行价格 + x-description-zh-hk: 發行價格 + - name: shares_offered + type: string + required: false + description: Total shares offered + x-description-zh: 发行总股数 + x-description-zh-hk: 發行總股數 + - name: chairman + type: string + required: false + description: Chairman + x-description-zh: 董事长 + x-description-zh-hk: 董事長 + - name: secretary + type: string + required: false + description: Company secretary + x-description-zh: 公司秘书 + x-description-zh-hk: 公司祕書 + - name: audit_inst + type: string + required: false + description: Audit institution + x-description-zh: 审计机构 + x-description-zh-hk: 審計機構 + - name: category + type: string + required: false + description: Company category + x-description-zh: 公司类别 + x-description-zh-hk: 公司類別 + - name: year_end + type: string + required: false + description: Fiscal year-end + x-description-zh: 财年截止日 + x-description-zh-hk: 財年截止日 + - name: employees + type: string + required: false + description: Number of employees + x-description-zh: 员工人数 + x-description-zh-hk: 員工人數 + - name: Phone + type: string + required: false + description: Phone number + x-description-zh: 电话号码 + x-description-zh-hk: 電話號碼 + - name: fax + type: string + required: false + description: Fax number + x-description-zh: 传真 + x-description-zh-hk: 傳真 + - name: email + type: string + required: false + description: Contact email + x-description-zh: 联系邮箱 + x-description-zh-hk: 聯繫郵箱 + - name: legal_repr + type: string + required: false + description: Legal representative + x-description-zh: 法定代表人 + x-description-zh-hk: 法定代表人 + - name: manager + type: string + required: false + description: CEO / General manager + x-description-zh: CEO / 总经理 + x-description-zh-hk: CEO / 總經理 + - name: bus_license + type: string + required: false + description: Business license number + x-description-zh: 营业执照号 + x-description-zh-hk: 營業執照號 + - name: accounting_firm + type: string + required: false + description: Accounting firm + x-description-zh: 会计师事务所 + x-description-zh-hk: 會計師事務所 + - name: securities_rep + type: string + required: false + description: Securities representative + x-description-zh: 证券代表 + x-description-zh-hk: 證券代表 + - name: legal_counsel + type: string + required: false + description: Legal counsel + x-description-zh: 法律顾问 + x-description-zh-hk: 法律顧問 + - name: office_address + type: string + required: false + description: Office address + x-description-zh: 办公地址 + x-description-zh-hk: 辦公地址 + - name: zip_code + type: string + required: false + description: Postal code + x-description-zh: 邮政编码 + x-description-zh-hk: 郵政編碼 + - name: ads_ratio + type: string + required: false + description: ADS ratio + x-description-zh: ADS 比例 + x-description-zh-hk: ADS 比例 + - name: profile + type: string + required: false + description: Business description + x-description-zh: 业务描述 + x-description-zh-hk: 業務描述 + - name: sector + type: integer + required: false + description: Industry sector + x-description-zh: 行业 + x-description-zh-hk: 行業 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + company_name: Apple Inc. + name: Apple + ticker: AAPL + market: NasdaqGS + founded: '1976' + employees: '166000' + manager: Timothy D. Cook + website: www.apple.com + phone: (408) 996-1010 + address: One Apple Park Way, Cupertino, California, United States + profile: Apple Inc. designs, manufactures, and markets smartphones, personal computers, tablets, wearables, a... + region: US + sector: 0 + year_end: September 27 + icon: https://assets.lbkrs.com/ticker/ST/US/AAPL.png + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/company-professionals: + get: + operationId: executives + summary: Executives + x-summary-zh: 高管团队 + x-summary-zh-hk: 高管團隊 + description: | + Get the list of key executives (CEO, CFO, etc.) for a company. + x-description-zh: | + 获取公司关键高管列表(CEO、CFO 等)。 + x-description-zh-hk: | + 獲取公司關鍵高管列表(CEO、CFO 等)。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge executive TSLA.US + longbridge executive AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/company-professionals?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/company-professionals", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/company-professionals", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/company-professionals") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/company-professionals?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/company-professionals") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/company-professionals?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/company-professionals?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: professional_list + type: object[] + required: false + description: List of executive groups + x-description-zh: 高管分组列表, + x-description-zh-hk: 高管分組列表, + - name: └ professionals + type: array + required: false + description: List of executives + x-description-zh: 高管列表, + x-description-zh-hk: 高管列表, + - name: └ forward_url + type: string + required: false + description: Company executives page URL + x-description-zh: 公司高管页面链接 + x-description-zh-hk: 公司高管頁面連結 + - name: └ total + type: integer + required: false + description: Total number of executives + x-description-zh: 高管总数 + x-description-zh-hk: 高管總數 + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + professional_list: + - forward_url: https://longbridge.com/wiki/stocks/ST.US.AAPL#company-manager + professionals: + - biography: Tim Cook is the CEO of Apple Inc. + id: '12345' + name: Timothy D. Cook + name_en: Timothy D. Cook + name_zhcn: 蒂姆·库克 + photo: https://cdn.example.com/timcook.jpg + title: Chief Executive Officer + wiki_url: https://en.wikipedia.org/wiki/Tim_Cook + symbol: AAPL.US + total: 9 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/company-act: + get: + operationId: corporate_actions + summary: Corporate Actions + x-summary-zh: 公司行动 + x-summary-zh-hk: 公司行動 + description: | + Get corporate action history (splits, mergers, spin-offs, rights issues) for a security. + x-description-zh: | + 获取指定证券的公司行动历史,包括拆股、合并、分拆和配股等。 + x-description-zh-hk: | + 獲取指定證券的公司行動歷史,包括拆股、合並、分拆和配股等。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: start_date + in: query + type: string + required: false + description: Start date in `YYYY-MM-DD` format + x-description-zh: 开始日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 開始日期,格式 `YYYY-MM-DD` + - name: end_date + in: query + type: string + required: false + description: End date in `YYYY-MM-DD` format + x-description-zh: 结束日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 結束日期,格式 `YYYY-MM-DD` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge corp-action TSLA.US + longbridge corp-action AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/company-act?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/company-act", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/company-act", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/company-act") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/company-act?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/company-act") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/company-act?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/company-act?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: items + type: object[] + required: true + description: Corporate action list + x-description-zh: 公司行动列表, + x-description-zh-hk: 公司行動列表 + - name: └ id + type: string + required: false + description: Action ID + x-description-zh: 行动 ID + x-description-zh-hk: 公司行動 ID + - name: └ act_desc + type: string + required: false + description: Action description + x-description-zh: 行动描述 + x-description-zh-hk: 公司行動描述 + - name: └ act_type + type: string + required: false + description: Action type category + x-description-zh: 行动类型分类 + x-description-zh-hk: 公司行動類型分類 + - name: └ action + type: string + required: false + description: Action code (e.g. `DividendExDate`) + x-description-zh: 行动代码(如 `DividendExDate`) + x-description-zh-hk: 公司行動代碼(如 `DividendExDate`) + - name: └ date + type: string + required: false + description: Event date (YYYYMMDD) + x-description-zh: 生效日期 + x-description-zh-hk: 生效日期 + - name: └ date_str + type: string + required: false + description: Short display date (MM.DD) + x-description-zh: 简短展示日期(MM.DD) + x-description-zh-hk: 簡短展示日期(MM.DD) + - name: └ date_type + type: string + required: false + description: Date type label (e.g. Payment Date) + x-description-zh: 日期类型标签(如 Payment Date) + x-description-zh-hk: 日期類型標籤(如 Payment Date) + - name: └ date_zone + type: string + required: false + description: Time zone (e.g. EST) + x-description-zh: 时区(如 EST) + x-description-zh-hk: 時區(如 EST) + - name: └ delay_content + type: string + required: false + description: Delay content description + x-description-zh: 延迟内容描述 + x-description-zh-hk: 延遲內容描述 + - name: └ is_delay + type: boolean + required: false + description: Whether the event is delayed + x-description-zh: 是否延迟 + x-description-zh-hk: 事件是否延遲 + - name: └ live + type: boolean + required: false + description: Whether currently live + x-description-zh: 是否实时 + x-description-zh-hk: 當前是否正在直播 + - name: └ recent + type: boolean + required: false + description: Whether this is a recent event + x-description-zh: 是否为近期事件 + x-description-zh-hk: 是否爲近期事件 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + items: + - id: '622620' + action: DividendExDate + act_type: Distribution Plan + act_desc: Cash dividend 0.27 USD + date: '20260514' + date_str: '05.14' + date_type: Payment Date + date_zone: EST + delay_content: '' + is_delay: false + recent: false + live: null + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/invest-relations: + get: + operationId: invest_relation + summary: Investment Relations + x-summary-zh: 投资关系 + x-summary-zh-hk: 投資關係 + description: | + Get investment relations including parent company, subsidiaries, and major holdings. + x-description-zh: | + 获取投资关系数据,包括母公司、子公司及主要持股。 + x-description-zh-hk: | + 獲取投資關係數據,包括母公司、子公司及主要持股。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `700.HK` + x-description-zh: 证券代码,例如 `700.HK` + x-description-zh-hk: 證券代碼,例如 `700.HK` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge invest-relation 700.HK + longbridge invest-relation AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/invest-relations?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/invest-relations", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/invest-relations", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/invest-relations") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/invest-relations?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/invest-relations") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/invest-relations?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/invest-relations?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: invest_securities + type: object[] + required: false + description: List of investment holdings + x-description-zh: 投资持仓列表 + x-description-zh-hk: 投資持倉列表 + - name: └ company_id + type: string + required: false + description: Company ID + x-description-zh: 公司 ID + x-description-zh-hk: 公司 ID + - name: └ company_name + type: string + required: false + description: Display company name + x-description-zh: 展示用公司名称 + x-description-zh-hk: 展示用公司名稱 + - name: └ company_name_zhcn + type: string + required: false + description: Chinese company name + x-description-zh: 公司中文名称 + x-description-zh-hk: 公司中文名稱 + - name: └ company_name_en + type: string + required: false + description: English company name + x-description-zh: 公司英文名称 + x-description-zh-hk: 公司英文名稱 + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ percent_of_shares + type: string + required: false + description: Ownership percentage + x-description-zh: 持股比例 + x-description-zh-hk: 持股比例 + - name: └ shares_value + type: string + required: false + description: Value of shares held + x-description-zh: 持股市值 + x-description-zh-hk: 持股市值 + - name: └ shares_rank + type: string + required: false + description: Rank by shares held + x-description-zh: 按持股数排名 + x-description-zh-hk: 按持股數排名 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: forward_url + type: string + required: false + description: Company investment relations page URL + x-description-zh: 公司投资者关系页面 URL + x-description-zh-hk: 公司投資者關係頁面 URL + - name: total + type: integer + required: false + description: Total number of matching stocks. + x-description-zh: 满足条件的股票总数。 + x-description-zh-hk: 滿足條件的股票總數。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + forward_url: https://longbridge.com/wiki/stocks/ST.HK.700#invest + invest_securities: + - symbol: HUYA.US + company_id: '12345' + company_name: 虎牙直播 + company_name_en: Huya Inc. + company_name_zhcn: 虎牙直播 + currency: USD + percent_of_shares: '19.00' + shares_rank: '1' + shares_value: '19000000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/operatings: + get: + operationId: operating + summary: Operating Metrics + x-summary-zh: 经营数据 + x-summary-zh-hk: 經營數據 + description: | + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Get operating metrics and financial indicator summaries by report period. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可查询任意标的,包括美股。 + ::: + + 按财报期获取经营数据及核心财务指标摘要。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可查詢任意標的,包括美股。 + ::: + + 按財報期獲取經營數據及核心財務指標摘要。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: period + in: query + type: string + required: false + description: Report period filter, e.g. `q1`, `q2`, `q3`, `q4`, `annual` + x-description-zh: 财报期筛选,如 `q1`、`q2`、`q3`、`q4`、`annual` + x-description-zh-hk: 財報期篩選,如 `q1`、`q2`、`q3`、`q4`、`annual` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge operating AAPL.US + longbridge operating TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/operatings?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/operatings", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/operatings", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/operatings") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/operatings?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/operatings") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/operatings?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/operatings?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: List of operating summary reports + x-description-zh: 经营数据报告列表, + x-description-zh-hk: 經營數據報告列表, + - name: └ id + type: string + required: false + description: Internal report ID + x-description-zh: 内部报告 ID + x-description-zh-hk: 內部報告 ID + - name: └ report + type: string + required: false + description: Report period code (e.g. `af` = annual) + x-description-zh: 报告期代码(如 `af` = 年报) + x-description-zh-hk: 報告期代碼(如 `af` = 年報) + - name: └ title + type: string + required: false + description: Report title + x-description-zh: 报告标题 + x-description-zh-hk: 報告標題 + - name: └ txt + type: string + required: false + description: Management discussion text + x-description-zh: 管理层讨论文本 + x-description-zh-hk: 管理層討論文本 + - name: └ latest + type: boolean + required: false + description: Whether this is the most recent report + x-description-zh: 是否为最新报告 + x-description-zh-hk: 是否為最新報告 + - name: └ web_url + type: string + required: false + description: URL to the full report page + x-description-zh: 完整报告页面链接 + x-description-zh-hk: 完整報告頁面連結 + - name: └ keywords + type: array + required: false + description: Keywords. + x-description-zh: 关键词。 + x-description-zh-hk: 關鍵詞。 + - name: └ financial + type: object + required: false + description: Key financial metrics + x-description-zh: 关键财务指标 + x-description-zh-hk: 關鍵財務指標 + - name: └ ∟ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ ∟ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ ∟ region + type: string + required: false + description: Market region + x-description-zh: 市场地区 + x-description-zh-hk: 市場地區 + - name: └ ∟ code + type: string + required: false + description: Ticker code + x-description-zh: 股票代码 + x-description-zh-hk: 股票代碼 + - name: └ ∟ currency + type: string + required: false + description: Reporting currency + x-description-zh: 报告货币 + x-description-zh-hk: 報告貨幣 + - name: └ ∟ report + type: string + required: false + description: Report period code (e.g. `af` = annual) + x-description-zh: 报告期代码(如 `af` = 年报) + x-description-zh-hk: 報告期代碼(如 `af` = 年報) + - name: └ ∟ report_txt + type: string + required: false + description: Period label (e.g. `FY2024`, `Q1 2024`) + x-description-zh: 报告期标签(如 `FY2024`、`Q1 2024`) + x-description-zh-hk: 報告期標籤(如 `FY2024`、`Q1 2024`) + - name: └ ∟ indicators + type: object[] + required: false + description: Financial indicators + x-description-zh: 财务指标列表, + x-description-zh-hk: 財務指標列表, + - name: └ ∟ ∟ field_name + type: string + required: false + description: Field name key (e.g. `operating_revenue`) + x-description-zh: 字段名称(如 `operating_revenue`) + x-description-zh-hk: 欄位名稱(如 `operating_revenue`) + - name: └ ∟ ∟ indicator_name + type: string + required: false + description: Display name + x-description-zh: 显示名称 + x-description-zh-hk: 顯示名稱 + - name: └ ∟ ∟ indicator_value + type: string + required: false + description: Formatted value + x-description-zh: 格式化数值 + x-description-zh-hk: 格式化數值 + - name: └ ∟ ∟ yoy + type: string + required: false + description: Year-over-year change rate + x-description-zh: 同比变化率 + x-description-zh-hk: 同比變化率 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + - id: '12345' + report: af + title: FY2025 Annual Report Summary + txt: Management discussion... + latest: true + web_url: https://longbridge.com/wiki/... + financial: + code: '700' + currency: HKD + name: Tencent + region: HK + report: af + indicators: + - field_name: operating_revenue + indicator_name: Revenue + indicator_value: 6786 亿 + yoy: '0.0800' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/buy-backs: + get: + operationId: buyback + summary: Buyback + x-summary-zh: 回购数据 + x-summary-zh-hk: 回購數據 + description: | + Get share buyback data including historical buyback amounts and ratios. + x-description-zh: | + 获取股票回购数据,包括历史回购金额及回购比例。 + x-description-zh-hk: | + 獲取股票回購數據,包括歷史回購金額及回購比例。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/buy-backs?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/buy-backs", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/buy-backs", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/buy-backs") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/buy-backs?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/buy-backs") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/buy-backs?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/buy-backs?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: recent_buybacks + type: object + required: false + description: Trailing 12-month buyback summary + x-description-zh: 近 12 个月回购汇总 + x-description-zh-hk: 近 12 個月回購匯總 + - name: └ net_buyback_ttm + type: string + required: false + description: Net buyback (trailing 12 months) + x-description-zh: 净回购金额(近 12 个月) + x-description-zh-hk: 淨回購金額(近 12 個月) + - name: └ net_buyback_yield_ttm + type: string + required: false + description: Buyback yield (trailing 12 months) + x-description-zh: 回购收益率(近 12 个月) + x-description-zh-hk: 回購收益率(近 12 個月) + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: buyback_history + type: object[] + required: false + description: Annual buyback history + x-description-zh: 年度回购历史, + x-description-zh-hk: 年度回購歷史, + - name: └ fiscal_year + type: string + required: false + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: └ fiscal_year_range + type: string + required: false + description: Fiscal year date range + x-description-zh: 财年日期区间 + x-description-zh-hk: 財年日期區間 + - name: └ net_buyback + type: string + required: false + description: Net buyback amount + x-description-zh: 净回购金额 + x-description-zh-hk: 淨回購金額 + - name: └ net_buyback_yield + type: string + required: false + description: Buyback yield + x-description-zh: 回购收益率 + x-description-zh-hk: 回購收益率 + - name: └ net_buyback_growth_rate + type: string + required: false + description: Buyback growth rate + x-description-zh: 回购增长率 + x-description-zh-hk: 回購增長率 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: └ total_shareholder_yield + type: string + required: false + description: Total shareholder yield. + x-description-zh: 股东总回报率。 + x-description-zh-hk: 股東總回報率。 + - name: └ dividend + type: string + required: false + description: Dividend per share. + x-description-zh: 每股股息。 + x-description-zh-hk: 每股股息。 + - name: └ dividend_yield + type: string + required: false + description: Dividend yield. + x-description-zh: 股息率。 + x-description-zh-hk: 股息率。 + - name: └ dividend_growth_rate + type: string + required: false + description: Dividend growth rate. + x-description-zh: 股息增长率。 + x-description-zh-hk: 股息增長率。 + - name: └ dividend_payout_ratio + type: string + required: false + description: Dividend payout ratio. + x-description-zh: 股息支付率。 + x-description-zh-hk: 股息支付率。 + - name: └ dividend_to_cashflow_ratio + type: string + required: false + description: Dividend-to-cashflow ratio. + x-description-zh: 股息与现金流比率。 + x-description-zh-hk: 股息與現金流比率。 + - name: └ net_buyback_payout_ratio + type: string + required: false + description: Buyback payout ratio + x-description-zh: 回购支付比率 + x-description-zh-hk: 回購支付比率 + - name: └ net_buyback_to_cashflow_ratio + type: string + required: false + description: Buyback to free cash flow ratio + x-description-zh: 回购占自由现金流比率 + x-description-zh-hk: 回購佔自由現金流比率 + - name: buyback_ratios + type: object[] + required: false + description: Buyback ratio history + x-description-zh: 回购比率历史, + x-description-zh-hk: 回購比率歷史, + - name: └ fiscal_year + type: string + required: false + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: └ fiscal_year_range + type: string + required: false + description: Fiscal year date range + x-description-zh: 财年日期区间 + x-description-zh-hk: 財年日期區間 + - name: └ net_buyback_payout_ratio + type: string + required: false + description: Buyback payout ratio + x-description-zh: 回购支付比率 + x-description-zh-hk: 回購支付比率 + - name: └ net_buyback_to_cashflow_ratio + type: string + required: false + description: Buyback to free cash flow ratio + x-description-zh: 回购占自由现金流比率 + x-description-zh-hk: 回購佔自由現金流比率 + - name: └ total_shareholder_yield + type: string + required: false + description: Total shareholder yield. + x-description-zh: 股东总回报率。 + x-description-zh-hk: 股東總回報率。 + - name: └ dividend + type: string + required: false + description: Dividend per share. + x-description-zh: 每股股息。 + x-description-zh-hk: 每股股息。 + - name: └ dividend_yield + type: string + required: false + description: Dividend yield. + x-description-zh: 股息率。 + x-description-zh-hk: 股息率。 + - name: └ dividend_growth_rate + type: string + required: false + description: Dividend growth rate. + x-description-zh: 股息增长率。 + x-description-zh-hk: 股息增長率。 + - name: └ dividend_payout_ratio + type: string + required: false + description: Dividend payout ratio. + x-description-zh: 股息支付率。 + x-description-zh-hk: 股息支付率。 + - name: └ dividend_to_cashflow_ratio + type: string + required: false + description: Dividend-to-cashflow ratio. + x-description-zh: 股息与现金流比率。 + x-description-zh-hk: 股息與現金流比率。 + - name: └ net_buyback + type: string + required: false + description: Net buyback amount + x-description-zh: 净回购金额 + x-description-zh-hk: 淨回購金額 + - name: └ net_buyback_yield + type: string + required: false + description: Buyback yield + x-description-zh: 回购收益率 + x-description-zh-hk: 回購收益率 + - name: └ net_buyback_growth_rate + type: string + required: false + description: Buyback growth rate + x-description-zh: 回购增长率 + x-description-zh-hk: 回購增長率 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + buyback_history: + - fiscal_year: FY2024 + fiscal_year_range: 2024-01-01~2024-12-31 + net_buyback: '94949000000' + net_buyback_yield: '0.0241' + net_buyback_growth_rate: '-0.1233' + buyback_ratios: + - net_buyback_payout_ratio: '0.9502' + net_buyback_to_cashflow_ratio: '0.8821' + recent_buybacks: + currency: USD + net_buyback_ttm: '94949000000' + net_buyback_yield_ttm: '0.0241' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/dividends: + get: + operationId: dividends + summary: Dividends + x-summary-zh: 分红历史 + x-summary-zh-hk: 股息歷史 + description: | + Get dividend history and upcoming dividend announcements for a security. + x-description-zh: | + 获取指定证券的分红历史及即将公布的分红信息。 + x-description-zh-hk: | + 獲取指定證券的股息歷史及即將公佈的股息信息。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: start_date + in: query + type: string + required: false + description: Start date in `YYYY-MM-DD` format + x-description-zh: 开始日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 開始日期,格式 `YYYY-MM-DD` + - name: end_date + in: query + type: string + required: false + description: End date in `YYYY-MM-DD` format + x-description-zh: 结束日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 結束日期,格式 `YYYY-MM-DD` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge dividend TSLA.US + longbridge dividend AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/dividends?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/dividends", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/dividends", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/dividends") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/dividends?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/dividends") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/dividends?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/dividends?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Dividend records + x-description-zh: 分红记录列表, + x-description-zh-hk: 股息記錄列表, + - name: └ id + type: string + required: false + description: Dividend event ID + x-description-zh: 派息事件 ID + x-description-zh-hk: 派息事件 ID + - name: └ desc + type: string + required: false + description: Dividend description + x-description-zh: 派息描述 + x-description-zh-hk: 派息描述 + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ record_date + type: string + required: false + description: Record date + x-description-zh: 股权登记日 + x-description-zh-hk: 股權登記日 + - name: └ ex_date + type: string + required: false + description: Ex-dividend date + x-description-zh: 除息日 + x-description-zh-hk: 除息日 + - name: └ payment_date + type: string + required: false + description: Payment date + x-description-zh: 派息日 + x-description-zh-hk: 派息日 + - name: └ dividend_summary + type: object + required: false + description: Dividend summary. + x-description-zh: 股息摘要。 + x-description-zh-hk: 股息摘要。 + - name: └ ∟ title + type: string + required: false + description: Topic title + x-description-zh: 标题 + x-description-zh-hk: 標題 + - name: └ ∟ desc + type: string + required: false + description: Dividend description + x-description-zh: 派息描述 + x-description-zh-hk: 派息描述 + - name: total + type: string + required: false + description: Total number of matching stocks. + x-description-zh: 满足条件的股票总数。 + x-description-zh-hk: 滿足條件的股票總數。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + - id: '12345' + symbol: AAPL.US + ex_date: '2026-02-07' + payment_date: '2026-02-13' + record_date: '2026-02-10' + desc: Cash dividend 0.25 USD + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/dividends/details: + get: + operationId: dividend_detail + summary: Dividend Detail + x-summary-zh: 分红详情 + x-summary-zh-hk: 分紅詳情 + description: | + Get detailed dividend information including declared, ex-dividend, and payment dates. + x-description-zh: | + 获取详细分红信息,包括宣告日、除息日和派发日。 + x-description-zh-hk: | + 獲取詳細分紅資訊,包括宣告日、除息日和派發日。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/dividends/details?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/dividends/details", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/dividends/details", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/dividends/details") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/dividends/details?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/dividends/details") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/dividends/details?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/dividends/details?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: List of dividend records + x-description-zh: 分红记录列表, + x-description-zh-hk: 分紅記錄列表, + - name: └ desc + type: string + required: false + description: Dividend description + x-description-zh: 派息描述 + x-description-zh-hk: 派息描述 + - name: └ record_date + type: string + required: false + description: Record date (YYYY-MM-DD) + x-description-zh: 股权登记日(YYYY-MM-DD) + x-description-zh-hk: 股權登記日(YYYY-MM-DD) + - name: └ ex_date + type: string + required: false + description: Ex-dividend date (YYYY-MM-DD) + x-description-zh: 除息日(YYYY-MM-DD) + x-description-zh-hk: 除息日(YYYY-MM-DD) + - name: └ payment_date + type: string + required: false + description: Payment date (YYYY-MM-DD) + x-description-zh: 派息日(YYYY-MM-DD) + x-description-zh-hk: 派息日(YYYY-MM-DD) + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + - id: '12345' + symbol: AAPL.US + ex_date: '2026-02-07' + payment_date: '2026-02-13' + record_date: '2026-02-10' + desc: Cash dividend 0.25 USD + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/shareholders: + get: + operationId: shareholders + summary: Shareholders + x-summary-zh: 主要股东 + x-summary-zh-hk: 主要股東 + description: | + Get the top institutional and individual shareholders of a company. + x-description-zh: | + 获取公司主要机构股东和个人股东信息。 + x-description-zh-hk: | + 獲取公司主要機構股東和個人股東信息。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge shareholder TSLA.US + longbridge shareholder AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/shareholders?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/shareholders", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/shareholders", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/shareholders") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/shareholders?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/shareholders") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/shareholders?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/shareholders?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: shareholder_list + type: object[] + required: false + description: List of shareholders + x-description-zh: 股东列表, + x-description-zh-hk: 股東列表, + - name: └ shareholder_id + type: string + required: false + description: Shareholder ID + x-description-zh: 股东 ID + x-description-zh-hk: 股東 ID + - name: └ shareholder_name + type: string + required: false + description: Shareholder name + x-description-zh: 股东名称 + x-description-zh-hk: 股東名稱 + - name: └ percent_of_shares + type: string + required: false + description: Percentage of shares held + x-description-zh: 持股比例 + x-description-zh-hk: 持股比例 + - name: └ shares_changed + type: string + required: false + description: Change in shares held + x-description-zh: 持股变动 + x-description-zh-hk: 持股變動 + - name: └ report_date + type: string + required: false + description: Report date + x-description-zh: 报告日期 + x-description-zh-hk: 報告日期 + - name: └ stocks + type: array + required: false + description: Associated stocks + x-description-zh: 关联标的 + x-description-zh-hk: 關聯標的 + - name: └ institution_type + type: string + required: false + description: Institution type + x-description-zh: 机构类型 + x-description-zh-hk: 機構類型 + - name: total + type: integer + required: false + description: Total number of shareholders + x-description-zh: 股东总数 + x-description-zh-hk: 股東總數 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + forward_url: '' + total: 33 + shareholder_list: + - shareholder_name: Timothy D. Cook + percent_of_shares: '2.84' + institution_type: '' + report_date: '2026-04-21' + shareholder_id: '0' + shares_changed: '0' + stocks: [] + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/shareholders/top: + get: + operationId: shareholder_top + summary: Top Shareholders + x-summary-zh: 大股东排行 + x-summary-zh-hk: 大股東排行 + description: | + Get the top 20 major shareholders (institutional, individual, and insider) for a listed company, with support for multi-period comparison. `object_id` can be passed to `shareholder_detail` to retrieve full holding history. + x-description-zh: | + 获取上市公司前 20 大股东(机构、个人、内部人)的持股数据,支持多报告期对比。`object_id` 可传入 `shareholder_detail` 查看详情。 + x-description-zh-hk: | + 獲取上市公司前 20 大股東(機構、個人、內部人)的持股數據,支持多報告期對比。`object_id` 可傳入 `shareholder_detail` 查看詳情。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge shareholder AAPL.US --top + longbridge shareholder 700.HK --top + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/shareholders/top?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/shareholders/top", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/shareholders/top", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/shareholders/top") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/shareholders/top?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/shareholders/top") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/shareholders/top?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/shareholders/top?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: periods + type: array + required: false + description: Available reporting periods + x-description-zh: 可用报告期列表 + x-description-zh-hk: 可用報告期列表 + - name: info + type: object[] + required: false + description: Shareholder data per reporting period + x-description-zh: 各报告期的股东数据 + x-description-zh-hk: 各報告期的股東數據 + - name: └ period + type: string + required: false + description: Reporting period label (e.g. `Latest`) + x-description-zh: 报告期标签(如 `Latest`) + x-description-zh-hk: 報告期標籤(如 `Latest`) + - name: └ share_holders + type: object[] + required: false + description: List of shareholders (up to 20) + x-description-zh: 股东列表(最多 20 条) + x-description-zh-hk: 股東列表(最多 20 條) + - name: └ ∟ object_id + type: string + required: false + description: Unique shareholder ID; pass to `shareholder_detail` + x-description-zh: 股东唯一 ID,可传入 `shareholder_detail` + x-description-zh-hk: 股東唯一 ID,可傳入 `shareholder_detail` + - name: └ ∟ name + type: string + required: false + description: Shareholder name + x-description-zh: 股东名称 + x-description-zh-hk: 股東名稱 + - name: └ ∟ shares_held + type: string + required: false + description: Number of shares held + x-description-zh: 持股数量 + x-description-zh-hk: 持股數量 + - name: └ ∟ percent_shares_held + type: string + required: false + description: Ownership percentage, including `%` sign (e.g. `9.71%`) + x-description-zh: 持股比例,含 `%` 符号(如 `9.71%`) + x-description-zh-hk: 持股比例,含 `%` 符號(如 `9.71%`) + - name: └ ∟ shares_changed + type: string + required: false + description: Net share count change (positive = bought, negative = sold) + x-description-zh: 持股变动数量(正增负减) + x-description-zh-hk: 持股變動數量(正增負減) + - name: └ ∟ percent_shares_changed + type: string + required: false + description: Change in ownership percentage, including `%` sign + x-description-zh: 持股比例变动,含 `%` 符号 + x-description-zh-hk: 持股比例變動,含 `%` 符號 + - name: └ ∟ filing_date + type: string + required: false + description: Filing date + x-description-zh: 申报日期 + x-description-zh-hk: 申報日期 + - name: └ ∟ period + type: string + required: false + description: Reporting period label (e.g. `Latest`) + x-description-zh: 报告期标签(如 `Latest`) + x-description-zh-hk: 報告期標籤(如 `Latest`) + - name: └ ∟ title + type: string + required: false + description: Shareholder type (Institution / Individual / Insider) + x-description-zh: 股东类型(机构 / 个人 / 内部人) + x-description-zh-hk: 股東類型(機構 / 個人 / 內部人) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + info: + - period: Latest + share_holders: + - object_id: '148057' + name: The Vanguard Group, Inc. + title: '' + shares_held: '1426283914.00' + percent_shares_held: 9.71% + percent_shares_changed: 0.01% + shares_changed: '0.00' + period: Latest + filing_date: 2025/12/31 + - object_id: '452583' + name: BlackRock, Inc. + title: '' + shares_held: '1138572603.00' + percent_shares_held: 7.75% + percent_shares_changed: '-0.06%' + shares_changed: '-10565359.00' + period: Latest + filing_date: 2026/03/31 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/shareholders/holding: + get: + operationId: shareholder_detail + summary: Shareholder Detail + x-summary-zh: 股东持仓详情 + x-summary-zh-hk: 股東持倉詳情 + description: | + Get holding history and trade details for a specific shareholder. The `object_id` comes from the `shareholder_top` response. + x-description-zh: | + 获取单个股东的持仓历史及交易明细。`object_id` 来自 `shareholder_top` 返回结果。 + x-description-zh-hk: | + 獲取單個股東的持倉歷史及交易明細。`object_id` 來自 `shareholder_top` 返回結果。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: object_id + in: query + type: integer + required: true + description: Shareholder ID from `shareholder_top` `object_id` field + x-description-zh: 股东 ID,来自 `shareholder_top` 的 `object_id` 字段 + x-description-zh-hk: 股東 ID,來自 `shareholder_top` 的 `object_id` 字段 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge shareholder AAPL.US --object-id 19463 + longbridge shareholder 700.HK --object-id 20181 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/shareholders/holding?symbol=<symbol>&object_id=<object_id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/shareholders/holding", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "object_id": "<object_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/shareholders/holding", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "object_id": "<object_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/shareholders/holding") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("object_id", "<object_id>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/shareholders/holding?symbol=<symbol>&object_id=<object_id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/shareholders/holding") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("object_id", "<object_id>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/shareholders/holding?symbol=<symbol>&object_id=<object_id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/shareholders/holding?symbol=<symbol>&object_id=<object_id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: title + type: string + required: false + description: Shareholder title / type + x-description-zh: 股东头衔 / 类型 + x-description-zh-hk: 股東頭銜 / 類型 + - name: avatar + type: string + required: false + description: Avatar URL + x-description-zh: 头像 URL + x-description-zh-hk: 頭像 URL + - name: owner_source + type: string + required: false + description: 'Shareholder type: `Company`, `Institution`, `Person`, `Insider`' + x-description-zh: 股东类型:`Company`、`Institution`、`Person`、`Insider` + x-description-zh-hk: 股東類型:`Company`、`Institution`、`Person`、`Insider` + - name: holding_periods + type: string[] + required: false + description: Available holding periods + x-description-zh: 可用的持仓报告期列表 + x-description-zh-hk: 可用的持倉報告期列表 + - name: holding_details + type: object[] + required: false + description: Holding detail records + x-description-zh: 持仓明细记录 + x-description-zh-hk: 持倉明細記錄 + - name: holding_summary + type: object[] + required: false + description: Holding summary records + x-description-zh: 持仓汇总记录 + x-description-zh-hk: 持倉匯總記錄 + - name: trading_periods + type: string[] + required: false + description: Available trading periods (e.g. `Past 1 Month`, `Past 3 Months`) + x-description-zh: 可用的交易统计区间(如 `Past 1 Month`、`Past 3 Months`) + x-description-zh-hk: 可用的交易統計區間(如 `Past 1 Month`、`Past 3 Months`) + - name: tradings + type: object[] + required: false + description: Trade aggregates per period + x-description-zh: 各区间交易汇总 + x-description-zh-hk: 各區間交易匯總 + - name: └ period + type: string + required: false + description: Period label (e.g. `Past 1 Month`) + x-description-zh: 区间标签(如 `Past 1 Month`) + x-description-zh-hk: 區間標籤(如 `Past 1 Month`) + - name: └ accum_buy + type: string + required: false + description: Cumulative shares bought in this period + x-description-zh: 该区间累计买入股数 + x-description-zh-hk: 該區間累計買入股數 + - name: └ accum_sell + type: string + required: false + description: Cumulative shares sold in this period + x-description-zh: 该区间累计卖出股数 + x-description-zh-hk: 該區間累計賣出股數 + - name: └ net_buy + type: string + required: false + description: Net shares bought (buy minus sell) in this period + x-description-zh: 该区间净买入股数(买入减卖出) + x-description-zh-hk: 該區間淨買入股數(買入減賣出) + - name: └ trading_details + type: object[] + required: false + description: Individual transactions within the period + x-description-zh: 该区间内的具体交易记录 + x-description-zh-hk: 該區間內的具體交易記錄 + - name: └ ∟ trading_date + type: string + required: false + description: Trade date + x-description-zh: 交易日期 + x-description-zh-hk: 交易日期 + - name: └ ∟ trading_shares + type: string + required: false + description: Number of shares traded + x-description-zh: 交易股数 + x-description-zh-hk: 交易股數 + - name: └ ∟ trading_price + type: string + required: false + description: Trade price + x-description-zh: 交易价格 + x-description-zh-hk: 交易價格 + - name: └ ∟ trading_type + type: string + required: false + description: 'Trade direction: `Buy` or `Sell`' + x-description-zh: 交易方向:`Buy` 或 `Sell` + x-description-zh-hk: 交易方向:`Buy` 或 `Sell` + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + name: The Vanguard Group, Inc. + title: '' + avatar: '' + owner_source: Institution + holding_periods: [] + holding_details: [] + holding_summary: [] + trading_periods: + - Past 1 Month + - Past 3 Months + - Past 1 Year + - Past 3 Years + tradings: + - period: Past 1 Month + accum_buy: '8500000.00' + accum_sell: '2687264.00' + net_buy: '5812736.00' + trading_details: + - trading_date: '2025-12-18' + trading_shares: '5200000' + trading_price: '248.12' + trading_type: Buy + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/fund-holders: + get: + operationId: fund_holdings + summary: Fund Holdings + x-summary-zh: 基金持仓 + x-summary-zh-hk: 基金持倉 + description: | + Get the list of funds that hold a given security, with the number of shares and ownership percentage. + x-description-zh: | + 获取持有指定证券的基金列表,含持股数量和持股比例。 + x-description-zh-hk: | + 獲取持有指定證券的基金列表,含持股數量和持股比例。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge fund-holder TSLA.US + longbridge fund-holder AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/fund-holders?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/fund-holders", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/fund-holders", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/fund-holders") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/fund-holders?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/fund-holders") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/fund-holders?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/fund-holders?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: lists + type: object[] + required: false + description: List of fund holders + x-description-zh: 基金持仓列表, + x-description-zh-hk: 基金持倉列表, + - name: └ symbol + type: string + required: false + description: Fund symbol + x-description-zh: 基金代码(含市场后缀) + x-description-zh-hk: 基金代碼(含市場後綴) + - name: └ name + type: string + required: false + description: Fund name + x-description-zh: 基金名称 + x-description-zh-hk: 基金名稱 + - name: └ code + type: string + required: false + description: Fund code + x-description-zh: 基金简码 + x-description-zh-hk: 基金簡碼 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: └ position_ratio + type: string + required: false + description: Position ratio (%) + x-description-zh: 持仓占比(%) + x-description-zh-hk: 持倉佔比(%) + - name: └ report_date + type: string + required: false + description: Report date + x-description-zh: 报告日期 + x-description-zh-hk: 報告日期 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + lists: + - symbol: TSLT.US + code: TSLT + name: 2x Long TSLA ETF + position_ratio: '101.02' + report_date: '2026-05-07' + currency: USD + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/institution-rating-latest: + get: + operationId: institution_rating + summary: Institution Rating + x-summary-zh: 机构评级 + x-summary-zh-hk: 機構評級 + description: | + Get analyst institution rating snapshot (rating distribution and average target price). + x-description-zh: | + 获取分析师机构评级快照(评级分布及平均目标价)。 + x-description-zh-hk: | + 獲取分析師機構評級快照(評級分佈及平均目標價)。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `TSLA.US` + x-description-zh: 证券代码,例如 `TSLA.US` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge institution-rating TSLA.US + longbridge institution-rating AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/institution-rating-latest?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/institution-rating-latest", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/institution-rating-latest", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/institution-rating-latest") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/institution-rating-latest?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/institution-rating-latest") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/institution-rating-latest?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/institution-rating-latest?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: evaluate + type: object + required: false + description: Analyst rating distribution. + x-description-zh: 分析师评级分布。 + x-description-zh-hk: 分析師評級分佈。 + - name: └ buy + type: integer + required: false + description: Number of Buy ratings. + x-description-zh: 买入评级数。 + x-description-zh-hk: 買入評級數。 + - name: └ over + type: integer + required: false + description: Number of Overweight ratings. + x-description-zh: 增持评级数。 + x-description-zh-hk: 增持評級數。 + - name: └ hold + type: integer + required: false + description: Number of Hold ratings. + x-description-zh: 持有评级数。 + x-description-zh-hk: 持有評級數。 + - name: └ under + type: integer + required: false + description: Number of Underweight ratings. + x-description-zh: 减持评级数。 + x-description-zh-hk: 減持評級數。 + - name: └ sell + type: integer + required: false + description: Number of Sell ratings. + x-description-zh: 卖出评级数。 + x-description-zh-hk: 賣出評級數。 + - name: └ total + type: integer + required: false + description: Total number of ratings. + x-description-zh: 评级总数。 + x-description-zh-hk: 評級總數。 + - name: └ start_date + type: string + required: false + description: Statistics start time (Unix seconds). + x-description-zh: 统计起始时间(Unix 秒)。 + x-description-zh-hk: 統計起始時間(Unix 秒)。 + - name: └ end_date + type: string + required: false + description: Statistics end time (Unix seconds). + x-description-zh: 统计结束时间(Unix 秒)。 + x-description-zh-hk: 統計結束時間(Unix 秒)。 + - name: └ no_opinion + type: integer + required: false + description: Number of analysts with no opinion. + x-description-zh: 无观点的分析师数。 + x-description-zh-hk: 無觀點的分析師數。 + - name: target + type: object + required: false + description: Target-price consensus. + x-description-zh: 目标价一致预期。 + x-description-zh-hk: 目標價一致預期。 + - name: └ highest_price + type: string + required: false + description: Highest target price. + x-description-zh: 最高目标价。 + x-description-zh-hk: 最高目標價。 + - name: └ lowest_price + type: string + required: false + description: Lowest target price. + x-description-zh: 最低目标价。 + x-description-zh-hk: 最低目標價。 + - name: └ prev_close + type: string + required: false + description: Previous close price. + x-description-zh: 前收盘价。 + x-description-zh-hk: 前收盤價。 + - name: └ start_date + type: string + required: false + description: Statistics start time (Unix seconds). + x-description-zh: 统计起始时间(Unix 秒)。 + x-description-zh-hk: 統計起始時間(Unix 秒)。 + - name: └ end_date + type: string + required: false + description: Statistics end time (Unix seconds). + x-description-zh: 统计结束时间(Unix 秒)。 + x-description-zh-hk: 統計結束時間(Unix 秒)。 + - name: industry_id + type: integer + required: false + description: Industry ID. + x-description-zh: 行业 ID。 + x-description-zh-hk: 行業 ID。 + - name: industry_name + type: string + required: false + description: Industry name. + x-description-zh: 行业名称。 + x-description-zh-hk: 行業名稱。 + - name: industry_rank + type: integer + required: false + description: The security's rank within its industry. + x-description-zh: 标的在行业内的排名。 + x-description-zh-hk: 標的在行業內的排名。 + - name: industry_total + type: integer + required: false + description: Number of securities in the industry. + x-description-zh: 行业内标的总数。 + x-description-zh-hk: 行業內標的總數。 + - name: industry_mean + type: integer + required: false + description: Industry mean rating rank. + x-description-zh: 行业平均评级排名。 + x-description-zh-hk: 行業平均評級排名。 + - name: industry_median + type: integer + required: false + description: Industry median rating rank. + x-description-zh: 行业评级排名中位数。 + x-description-zh-hk: 行業評級排名中位數。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + latest: + evaluate: + buy: 18 + hold: 17 + sell: 4 + no_opinion: 4 + over: 5 + under: 3 + total: 51 + start_date: '1778198400' + end_date: '0' + industry_id: 87676 + industry_mean: 10 + industry_median: 4 + industry_name: Automobiles + industry_rank: 1 + industry_total: 30 + target: + highest_price: '600.000' + lowest_price: '123.000' + prev_close: '428.35' + start_date: '1778198400' + end_date: '0' + summary: + ccy_symbol: $ + change: '0' + recommend: Buy + updated_at: '1778198400' + evaluate: + buy: 18 + hold: 17 + sell: 4 + target: + average_target: '350.00' + highest_price: '600.000' + lowest_price: '123.000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/institution-ratings/detail: + get: + operationId: institution_rating_detail + summary: Institution Rating Detail + x-summary-zh: 机构评级详情 + x-summary-zh-hk: 機構評級詳情 + description: | + Get historical analyst rating and target price details. + x-description-zh: | + 获取历史分析师评级及目标价详情。 + x-description-zh-hk: | + 獲取歷史分析師評級及目標價詳情。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `TSLA.US` + x-description-zh: 证券代码,例如 `TSLA.US` + x-description-zh-hk: 證券代碼,例如 `TSLA.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge institution-rating detail TSLA.US + longbridge institution-rating detail AAPL.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/institution-ratings/detail?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/institution-ratings/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/institution-ratings/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/institution-ratings/detail") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/institution-ratings/detail?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/institution-ratings/detail") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/institution-ratings/detail?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/institution-ratings/detail?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: evaluate + type: object + required: false + description: Rating distribution history + x-description-zh: 评级分布历史 + x-description-zh-hk: 評級分佈歷史 + - name: └ list + type: object[] + required: false + description: Daily holding history records + x-description-zh: 每日持仓历史记录, + x-description-zh-hk: 每日持倉歷史紀錄, + - name: └ ∟ strong_buy + type: integer + required: false + description: Strong buy count + x-description-zh: 强烈买入评级数量 + x-description-zh-hk: 強烈買入評級數量 + - name: └ ∟ buy + type: integer + required: false + description: Buy count + x-description-zh: 买入评级数量 + x-description-zh-hk: 買入評級數量 + - name: └ ∟ hold + type: integer + required: false + description: Hold count + x-description-zh: 持有评级数量 + x-description-zh-hk: 持有評級數量 + - name: └ ∟ sell + type: integer + required: false + description: Sell count + x-description-zh: 卖出评级数量 + x-description-zh-hk: 賣出評級數量 + - name: └ ∟ under + type: integer + required: false + description: Underperform count + x-description-zh: 跑输市场评级数量 + x-description-zh-hk: 跑輸市場評級數量 + - name: └ ∟ date + type: string + required: false + description: Date + x-description-zh: 日期 + x-description-zh-hk: 日期 + - name: target + type: object + required: false + description: Target price history + x-description-zh: 目标价历史 + x-description-zh-hk: 目標價歷史 + - name: └ updated_at + type: string + required: false + description: Unix timestamp (seconds) of last update + x-description-zh: 最近更新时间,Unix 时间戳(秒) + x-description-zh-hk: 最近更新時間,Unix 時間戳(秒) + - name: └ prediction_accuracy + type: string + required: false + description: Prediction accuracy. + x-description-zh: 预测准确率。 + x-description-zh-hk: 預測準確率。 + - name: └ list + type: object[] + required: false + description: Daily holding history records + x-description-zh: 每日持仓历史记录, + x-description-zh-hk: 每日持倉歷史紀錄, + - name: └ ∟ timestamp + type: string + required: false + description: Unix timestamp + x-description-zh: Unix 时间戳 + x-description-zh-hk: Unix 時間戳 + - name: └ ∟ price + type: string + required: false + description: Closing price on that date + x-description-zh: 当日收盘价 + x-description-zh-hk: 當日收盤價 + - name: └ ∟ meet + type: boolean + required: false + description: Whether price met target + x-description-zh: 价格是否达到目标 + x-description-zh-hk: 價格是否達到目標 + - name: └ ∟ avg_target + type: string + required: false + description: Average target price + x-description-zh: 平均目标价 + x-description-zh-hk: 平均目標價 + - name: └ ∟ min_target + type: string + required: false + description: Lowest target price + x-description-zh: 最低目标价 + x-description-zh-hk: 最低目標價 + - name: └ ∟ max_target + type: string + required: false + description: Highest target price + x-description-zh: 最高目标价 + x-description-zh-hk: 最高目標價 + - name: └ ∟ date + type: string + required: false + description: Date + x-description-zh: 日期 + x-description-zh-hk: 日期 + - name: └ data_percent + type: string + required: false + description: Data completeness percentage. + x-description-zh: 数据完整度百分比。 + x-description-zh-hk: 數據完整度百分比。 + - name: ccy_symbol + type: string + required: false + description: Currency symbol + x-description-zh: 货币符号 + x-description-zh-hk: 貨幣符號 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + ccy_symbol: $ + evaluate: + list: + - date: 2021/05/14 + buy: 3 + hold: 11 + sell: 2 + strong_buy: 9 + under: 6 + target: + list: + - broker_name: Goldman Sachs + date: '2026-04-30' + rating: Buy + target_price: '250.00' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/fundamentals/business-segments: + get: + operationId: business_segments + summary: Business Segments + x-summary-zh: 业务分部(当前期) + x-summary-zh-hk: 業務分部(當前期) + description: | + Get the current-period revenue segment breakdown for a listed company. + x-description-zh: | + 获取上市公司当前报告期的业务分部收入占比。 + x-description-zh-hk: | + 獲取上市公司當前報告期的業務分部收入佔比。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge business-segments AAPL.US + longbridge business-segments 700.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/fundamentals/business-segments?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/fundamentals/business-segments") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/fundamentals/business-segments?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/fundamentals/business-segments?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: date + type: string + required: false + description: Report period in YYYYMMDD format, e.g. `20260331` + x-description-zh: 报告期,格式 YYYYMMDD,例如 `20260331` + x-description-zh-hk: 報告期,格式 YYYYMMDD,例如 `20260331` + - name: total + type: string + required: false + description: Total revenue for the period + x-description-zh: 当期总收入 + x-description-zh-hk: 當期總收入 + - name: currency + type: string + required: false + description: Currency code, e.g. `USD` + x-description-zh: 货币代码,例如 `USD` + x-description-zh-hk: 貨幣代碼,例如 `USD` + - name: business + type: object[] + required: false + description: Business segment list + x-description-zh: 业务分部列表 + x-description-zh-hk: 業務分部列表 + - name: └ name + type: string + required: false + description: Segment name + x-description-zh: 业务分部名称 + x-description-zh-hk: 業務分部名稱 + - name: └ percent + type: string + required: false + description: Revenue share percentage, e.g. `40.56` + x-description-zh: 收入占比(百分比,例如 `40.56`) + x-description-zh-hk: 收入佔比(百分比,例如 `40.56`) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + date: '20260331' + total: '124300000000' + currency: USD + business: + - name: iPhone + percent: '56.19' + - name: Services + percent: '21.96' + - name: Mac + percent: '8.04' + - name: iPad + percent: '7.00' + - name: Wearables + percent: '6.81' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/quote/fundamentals/business-segments/history: + get: + operationId: business_segments_history + summary: Business Segments History + x-summary-zh: 业务分部(历史趋势) + x-summary-zh-hk: 業務分部(歷史趨勢) + description: | + Get historical business segment revenue trends across reporting periods. + x-description-zh: | + 获取上市公司按报告期的历史业务分部收入趋势。 + x-description-zh-hk: | + 獲取上市公司按報告期的歷史業務分部收入趨勢。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: report + in: query + type: string + required: false + description: 'Report type: `qf` (quarterly) / `saf` (semi-annual) / `af` (annual)' + x-description-zh: 报告类型:`qf`(季报)/ `saf`(半年报)/ `af`(年报) + x-description-zh-hk: 報告類型:`qf`(季報)/ `saf`(半年報)/ `af`(年報) + - name: cate + in: query + type: string + required: false + description: Segment category filter + x-description-zh: 分部类别过滤 + x-description-zh-hk: 分部類別過濾 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge business-segments AAPL.US --history + longbridge business-segments AAPL.US --history --report qf + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/fundamentals/business-segments/history?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: historical + type: object[] + required: false + description: Historical period snapshots + x-description-zh: 历史报告期列表 + x-description-zh-hk: 歷史報告期列表 + - name: └ total + type: string + required: false + description: Total revenue for the period + x-description-zh: 当期总收入 + x-description-zh-hk: 當期總收入 + - name: └ currency + type: string + required: false + description: Currency code + x-description-zh: 货币代码 + x-description-zh-hk: 貨幣代碼 + - name: └ date + type: string + required: false + description: Report period in YYYYMMDD format, e.g. `20260331` + x-description-zh: 报告期,格式 YYYYMMDD,例如 `20260331` + x-description-zh-hk: 報告期,格式 YYYYMMDD,例如 `20260331` + - name: └ report + type: string + required: false + description: Report period code (e.g. `af` = annual) + x-description-zh: 报告期代码(如 `af` = 年报) + x-description-zh-hk: 報告期代碼(如 `af` = 年報) + - name: └ report_txt + type: string + required: false + description: Period label (e.g. `FY2024`, `Q1 2024`) + x-description-zh: 报告期标签(如 `FY2024`、`Q1 2024`) + x-description-zh-hk: 報告期標籤(如 `FY2024`、`Q1 2024`) + - name: └ fp_start + type: string + required: false + description: Fiscal period start date in `YYYY.MM.DD` format + x-description-zh: 财政期开始日期,格式 `YYYY.MM.DD` + x-description-zh-hk: 財政期開始日期,格式 `YYYY.MM.DD` + - name: └ fp_end + type: string + required: false + description: Period end timestamp + x-description-zh: 报告期结束时间戳 + x-description-zh-hk: 報告期結束時間戳 + - name: └ rpt_date + type: string + required: false + description: Report release date (YYYY-MM-DD) + x-description-zh: 财报发布日期 + x-description-zh-hk: 財報發布日期 + - name: └ business + type: object[] + required: false + description: Business segment list + x-description-zh: 业务分部列表 + x-description-zh-hk: 業務分部列表 + - name: └ ∟ name + type: string + required: false + description: Segment name + x-description-zh: 业务分部名称 + x-description-zh-hk: 業務分部名稱 + - name: └ ∟ percent + type: string + required: false + description: Revenue share percentage, e.g. `40.80` + x-description-zh: 收入占比(百分比,例如 `40.80`) + x-description-zh-hk: 收入佔比(百分比,例如 `40.80`) + - name: └ ∟ value + type: string + required: false + description: Absolute revenue value + x-description-zh: 绝对收入数值 + x-description-zh-hk: 絕對收入數值 + - name: └ ∟ id + type: string + required: false + description: ID of the newly created alert + x-description-zh: 新创建提醒的 ID + x-description-zh-hk: 新創建提醒的 ID + - name: └ ∟ yoy + type: string + required: false + description: Year-over-year growth rate + x-description-zh: 同比增长率 + x-description-zh-hk: 同比增長率 + - name: └ regionals + type: array + required: false + description: Regional segment list (typically empty) + x-description-zh: 地区分部列表(当前通常为空数组) + x-description-zh-hk: 地區分部列表(當前通常為空數組) + - name: └ bus_ids + type: array + required: false + description: Business-segment IDs. + x-description-zh: 业务分部 ID 列表。 + x-description-zh-hk: 業務分部 ID 列表。 + - name: └ reg_ids + type: array + required: false + description: Regional-segment IDs. + x-description-zh: 区域分部 ID 列表。 + x-description-zh-hk: 區域分部 ID 列表。 + - name: └ yoy + type: string + required: false + description: Year-over-year growth rate + x-description-zh: 同比增长率 + x-description-zh-hk: 同比增長率 + - name: bus_ids + type: array + required: false + description: Business-segment IDs. + x-description-zh: 业务分部 ID 列表。 + x-description-zh-hk: 業務分部 ID 列表。 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + historical: + - date: '20260331' + total: '124300000000' + currency: USD + business: + - name: 美洲 + percent: '40.80' + value: '31968000000' + - name: 欧洲 + percent: '23.64' + value: '18521000000' + - name: 大中华区 + percent: '20.72' + value: '16233000000' + regionals: [] + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v2/quote/macrodata: + get: + operationId: macrodata + summary: Macroeconomic Historical Data + x-summary-zh: 宏观经济历史数据 + x-summary-zh-hk: 宏觀經濟歷史數據 + description: | + Get historical releases for a specific macroeconomic indicator — actual values, forecasts, previous values, and next release dates. + x-description-zh: | + 获取指定宏观经济指标的历史发布数据,包括实际值、预测值、前值和下次发布时间。 + x-description-zh-hk: | + 獲取指定宏觀經濟指標的歷史發布數據,包括實際值、預測值、前值和下次發布時間。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: indicator_code + in: query + type: string + required: true + description: Indicator code from `macroeconomic_indicators` + x-description-zh: 来自 `macroeconomic_indicators` 的指标代码 + x-description-zh-hk: 來自 `macroeconomic_indicators` 的指標代碼 + - name: start_date + in: query + type: string + required: false + description: Start date in `YYYY-MM-DD` format + x-description-zh: 开始日期,`YYYY-MM-DD` 格式 + x-description-zh-hk: 開始日期,`YYYY-MM-DD` 格式 + - name: end_date + in: query + type: string + required: false + description: End date in `YYYY-MM-DD` format + x-description-zh: 结束日期,`YYYY-MM-DD` 格式 + x-description-zh-hk: 結束日期,`YYYY-MM-DD` 格式 + - name: offset + in: query + type: integer + required: false + description: 'Pagination offset. Default: 0' + x-description-zh: '分页偏移量。默认:0' + x-description-zh-hk: '分頁偏移量。默認:0' + - name: limit + in: query + type: integer + required: false + description: 'Max records. Default: 100, max: 100' + x-description-zh: '最大记录数。默认:100,最大:100' + x-description-zh-hk: '最大記錄數。默認:100,最大:100' + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Historical data for Non-Farm Payroll + longbridge macrodata 61744 + # Date range filter + longbridge macrodata 61744 --start 2024-01-01 --end 2024-12-31 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v2/quote/macrodata?indicator_code=<indicator_code>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v2/quote/macrodata", + headers={"Authorization": "Bearer <access_token>"}, + params={"indicator_code": "<indicator_code>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v2/quote/macrodata", + headers={"Authorization": "Bearer <access_token>"}, + params={"indicator_code": "<indicator_code>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v2/quote/macrodata") + url.searchParams.set("indicator_code", "<indicator_code>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v2/quote/macrodata?indicator_code=<indicator_code>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v2/quote/macrodata") + .header("Authorization", "Bearer <access_token>") + .query(&[("indicator_code", "<indicator_code>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v2/quote/macrodata?indicator_code=<indicator_code>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v2/quote/macrodata?indicator_code=<indicator_code>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: info + type: MacroeconomicIndicator + required: true + description: Indicator metadata + x-description-zh: 指标元数据 + x-description-zh-hk: 指標元數據 + - name: data + type: Macroeconomic[] + required: true + description: Historical data points + x-description-zh: 历史数据点 + x-description-zh-hk: 歷史數據點 + - name: count + type: int + required: true + description: Total number of data points + x-description-zh: 数据点总数 + x-description-zh-hk: 數據點總數 + - name: period + type: string + required: true + description: Statistical period (e.g. `2024-12-01`, `2024-Q4`) + x-description-zh: 统计周期(如 `2024-12-01`、`2024-Q4`) + x-description-zh-hk: 統計週期(如 `2024-12-01`、`2024-Q4`) + - name: release_at + type: int + required: false + description: Unix timestamp of release datetime + x-description-zh: 发布时间的 Unix 时间戳 + x-description-zh-hk: 發佈時間的 Unix 時間戳 + - name: actual_value + type: string + required: true + description: Actual released value + x-description-zh: 实际公布值 + x-description-zh-hk: 實際公佈值 + - name: previous_value + type: string + required: true + description: Previous period value + x-description-zh: 上一周期值 + x-description-zh-hk: 上一週期值 + - name: forecast_value + type: string + required: true + description: Market consensus forecast + x-description-zh: 市场一致预期 + x-description-zh-hk: 市場一致預期 + responses: + '200': + description: Successful response + content: + application/json: + example: + count: 24 + info: + indicator_code: '61744' + country: US + name: Non-Farm Payroll + periodicity: Monthly + describe: ... + importance: 3 + data: + - period: '2024-12-01' + release_at: 1735900200 + actual_value: '256000' + previous_value: '212000' + forecast_value: '165000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v2/quote/macrodata/{indicator_id}: + get: + operationId: macrodata_indicator + summary: Macroeconomic Indicators + x-summary-zh: 宏观经济指标列表 + x-summary-zh-hk: 宏觀經濟指標列表 + description: | + List macroeconomic indicators available through Longbridge, optionally filtered by country. + x-description-zh: | + 列出 Longbridge 支持的宏观经济指标,可按国家/地区筛选。 + x-description-zh-hk: | + 列出 Longbridge 支持的宏觀經濟指標,可按國家/地區篩選。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: country + in: query + type: string + required: false + description: Filter by country. Omit for all countries. + x-description-zh: 按国家筛选。省略则返回所有国家。 + x-description-zh-hk: 按國家篩選。省略則返回所有國家。 + - name: keyword + in: query + type: string + required: false + description: Fuzzy search by indicator name (case-insensitive) + x-description-zh: 按指标名称模糊搜索(不区分大小写) + x-description-zh-hk: 按指標名稱模糊搜索(不區分大小寫) + - name: offset + in: query + type: integer + required: false + description: 'Pagination offset. Default: 0' + x-description-zh: '分页偏移量。默认:0' + x-description-zh-hk: '分頁偏移量。默認:0' + - name: limit + in: query + type: integer + required: false + description: 'Max records per page. Default: 100, max: 1000' + x-description-zh: '每页最大记录数。默认:100,最大:1000' + x-description-zh-hk: '每頁最大記錄數。默認:100,最大:1000' + x-codeSamples: + - lang: Shell + label: CLI + source: | + # List all indicators + longbridge macrodata + # Filter by US indicators + longbridge macrodata --country US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v2/quote/macrodata/{indicator_id}\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: indicator + type: object + required: true + description: 'Indicator detail' + x-description-zh: '指标详情' + x-description-zh-hk: '指標詳情' + - name: indicator.indicator_id + type: integer + required: false + description: 'Indicator ID' + x-description-zh: '指标 ID' + x-description-zh-hk: '指標 ID' + - name: indicator.indicator_name + type: string + required: false + description: 'Indicator name' + x-description-zh: '指标名称' + x-description-zh-hk: '指標名稱' + - name: indicator.unit + type: string + required: false + description: 'Value unit' + x-description-zh: '数值单位' + x-description-zh-hk: '數值單位' + - name: indicator.description + type: string + required: false + description: 'Indicator description' + x-description-zh: '指标描述' + x-description-zh-hk: '指標描述' + - name: indicator.market + type: string + required: false + description: 'Market / country code' + x-description-zh: '市场/国家代码' + x-description-zh-hk: '市場/國家代碼' + - name: indicator.frequence + type: string + required: false + description: 'Release frequency, e.g. `day`, `month`' + x-description-zh: '发布频率,如 `day`、`month`' + x-description-zh-hk: '發佈頻率,如 `day`、`month`' + - name: indicator.importance + type: integer + required: false + description: 'Importance level (1 = Low, 2 = Medium, 3 = High)' + x-description-zh: '重要程度(1=低,2=中,3=高)' + x-description-zh-hk: '重要程度(1=低,2=中,3=高)' + - name: indicator.indicator_data + type: object[] + required: false + description: 'Time-series data points' + x-description-zh: '时间序列数据点' + x-description-zh-hk: '時間序列數據點' + - name: indicator.indicator_data[].actual_data + type: string + required: false + description: 'Actual value' + x-description-zh: '实际值' + x-description-zh-hk: '實際值' + - name: indicator.indicator_data[].previous_data + type: string + required: false + description: 'Previous value' + x-description-zh: '前值' + x-description-zh-hk: '前值' + - name: indicator.indicator_data[].estimated_data + type: string + required: false + description: 'Forecast value' + x-description-zh: '预期值' + x-description-zh-hk: '預期值' + - name: indicator.indicator_data[].published_time + type: string + required: false + description: 'Publish time (ISO 8601)' + x-description-zh: '发布时间(ISO 8601)' + x-description-zh-hk: '發佈時間(ISO 8601)' + - name: indicator.indicator_data[].observation_date + type: string + required: false + description: 'Observation date (YYYY-MM-DD)' + x-description-zh: '观测日期(YYYY-MM-DD)' + x-description-zh-hk: '觀測日期(YYYY-MM-DD)' + - name: total + type: integer + required: false + description: 'Total number of matching indicators' + x-description-zh: '匹配的指标总数' + x-description-zh-hk: '匹配的指標總數' + responses: + '200': + description: Successful response + content: + application/json: + example: + count: 619 + list: + - indicator_code: '61744' + country: US + name: Non-Farm Payroll + periodicity: Monthly + describe: Employment situation report... + importance: 3 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/company-overview: + get: + operationId: us_company_overview + summary: US Company Overview + x-summary-zh: 美股公司概览 + x-summary-zh-hk: 美股公司概覽 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get company overview for a US stock — introduction, market cap, ranking tags, and detail URL. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股公司概览信息——简介、市值、排名标签和详情链接。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股公司概覽資訊——簡介、市值、排名標籤和詳情連結。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Stock symbol, e.g. `AAPL.US` + x-description-zh: 股票代码,如 `AAPL.US` + x-description-zh-hk: 股票代碼,如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # US company overview + longbridge company AAPL.US + longbridge company TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/company-overview?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/company-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/company-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/company-overview") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/company-overview?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/company-overview") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/company-overview?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/company-overview?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: intro + type: string + required: false + description: Company introduction + x-description-zh: 公司简介 + x-description-zh-hk: 公司簡介 + - name: market_cap + type: string + required: false + description: Market capitalization + x-description-zh: 市值 + x-description-zh-hk: 市值 + - name: top_rank_tags + type: object[] + required: false + description: Ranking tag labels + x-description-zh: 排名标签列表 + x-description-zh-hk: 排名標籤列表 + - name: └ key + type: string + required: false + description: Indicator key, e.g. `filter_pettm`. + x-description-zh: 指标键名,如 `filter_pettm`。 + x-description-zh-hk: 指標鍵名,如 `filter_pettm`。 + - name: └ location + type: integer + required: false + description: Location within the layout. + x-description-zh: 布局位置。 + x-description-zh-hk: 佈局位置。 + - name: └ text + type: string + required: false + description: Display text + x-description-zh: 显示文本 + x-description-zh-hk: 顯示文本 + - name: └ rank_type + type: integer + required: false + description: Ranking type. + x-description-zh: 排名类型。 + x-description-zh-hk: 排名類型。 + - name: └ highlight_text + type: string + required: false + description: Highlighted text. + x-description-zh: 高亮文本。 + x-description-zh-hk: 高亮文本。 + - name: └ title + type: string + required: false + description: Topic title + x-description-zh: 标题 + x-description-zh-hk: 標題 + - name: sharelist + type: object[] + required: false + description: Related sharelist items + x-description-zh: 相关自选列表 + x-description-zh-hk: 相關自選列表 + - name: └ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ chg + type: string + required: false + description: Day change amount + x-description-zh: 当日涨跌额 + x-description-zh-hk: 當日漲跌額 + - name: └ id + type: string + required: false + description: 'Sharelist item ID' + x-description-zh: '自选列表项 ID' + x-description-zh-hk: '自選列表項 ID' + - name: ccy_symbol + type: string + required: false + description: Currency symbol + x-description-zh: 货币符号 + x-description-zh-hk: 貨幣符號 + - name: detail_url + type: string + required: false + description: Link to full company detail page + x-description-zh: 公司详情页链接 + x-description-zh-hk: 公司詳情頁連結 + responses: + '200': + description: Successful response + content: + application/json: + example: + intro: Apple Inc. designs, manufactures, and markets smartphones, personal computers... + market_cap: '3150000000000' + ccy_symbol: USD + top_rank_tags: + - key: sp500 + title: S&P 500 + text: S&P 500 + rank_type: 1 + detail_url: https://longbridge.com/stocks/AAPL.US + sharelist: [] + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/valuation-overview: + get: + operationId: us_valuation_overview + summary: US Valuation Overview + x-summary-zh: 美股估值概览 + x-summary-zh-hk: 美股估值概覽 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get valuation overview for a US stock — current valuation indicators and historical range. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股估值概览——当前估值指标及历史区间。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股估值概覽——當前估值指標及歷史區間。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Stock symbol, e.g. `AAPL.US` + x-description-zh: 股票代码,如 `AAPL.US` + x-description-zh-hk: 股票代碼,如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # US valuation overview + longbridge valuation AAPL.US + longbridge valuation NVDA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/valuation-overview?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/valuation-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/valuation-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/valuation-overview") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/valuation-overview?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/valuation-overview") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/valuation-overview?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/valuation-overview?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: metrics + type: object + required: false + description: Valuation metrics keyed by indicator name + x-description-zh: 以指标名称为键的估值指标字典 + x-description-zh-hk: 以指標名稱為鍵的估值指標字典 + - name: └ pe + type: object + required: false + description: P/E ratio data + x-description-zh: 市盈率数据 + x-description-zh-hk: 市盈率數據 + - name: └ ∟ circle + type: string + required: false + description: Circle/section grouping. + x-description-zh: 环形分区。 + x-description-zh-hk: 環形分區。 + - name: └ ∟ part + type: string + required: false + description: Part/section identifier. + x-description-zh: 分区标识。 + x-description-zh-hk: 分區標識。 + - name: └ ∟ metric + type: string + required: false + description: Metric identifier. + x-description-zh: 指标标识。 + x-description-zh-hk: 指標標識。 + - name: └ ∟ desc + type: string + required: false + description: Description of current valuation + x-description-zh: 当前估值描述 + x-description-zh-hk: 當前估值描述 + - name: └ ∟ industry_median + type: string + required: false + description: Industry median rating rank. + x-description-zh: 行业评级排名中位数。 + x-description-zh-hk: 行業評級排名中位數。 + - name: indicator + type: string + required: false + description: Primary valuation indicator name (e.g. `PE`) + x-description-zh: 主要估值指标名称(如 `PE`) + x-description-zh-hk: 主要估值指標名稱(如 `PE`) + - name: range + type: integer + required: false + description: Historical percentile (0–100) + x-description-zh: 历史百分位(0–100) + x-description-zh-hk: 歷史百分位(0–100) + - name: date + type: string + required: false + description: Valuation date + x-description-zh: 估值日期 + x-description-zh-hk: 估值日期 + - name: ccy_symbol + type: string + required: false + description: Currency symbol + x-description-zh: 货币符号 + x-description-zh-hk: 貨幣符號 + - name: aichat_data + type: object + required: false + description: AI chat context data + x-description-zh: AI 对话上下文数据 + x-description-zh-hk: AI 對話上下文數據 + - name: └ agent_id + type: string + required: false + description: AI agent ID. + x-description-zh: AI 智能体 ID。 + x-description-zh-hk: AI 智能體 ID。 + - name: └ handoff_agent_id + type: string + required: false + description: Handoff AI agent ID. + x-description-zh: 转接 AI 智能体 ID。 + x-description-zh-hk: 轉接 AI 智能體 ID。 + - name: └ symbol + type: string + required: false + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ text + type: string + required: false + description: Display text + x-description-zh: 显示文本 + x-description-zh-hk: 顯示文本 + - name: └ type + type: string + required: false + description: '`"platform"` for preset strategies' + x-description-zh: '`"platform"` 表示平台预设策略' + x-description-zh-hk: '`"platform"` 表示平台預設策略' + - name: └ workflow_type + type: string + required: false + description: Workflow type. + x-description-zh: 工作流类型。 + x-description-zh-hk: 工作流類型。 + - name: ai_summary + type: string + required: false + description: AI-generated valuation summary + x-description-zh: AI 生成的估值摘要 + x-description-zh-hk: AI 生成的估值摘要 + responses: + '200': + description: Successful response + content: + application/json: + example: + indicator: PE + metrics: + pe: + circle: '35.2' + part: '72' + metric: PE + desc: Price-to-Earnings ratio + industry_median: '28.4' + range: 72 + date: '2026-07-01' + ccy_symbol: USD + ai_summary: Apple's PE ratio is in the 72nd percentile... + aichat_data: + agent_id: valuation_aapl + handoff_agent_id: '' + symbol: AAPL.US + text: Valuation overview for AAPL + type: valuation + workflow_type: valuation + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/finn-overview: + get: + operationId: us_financial_overview + summary: US Financial Overview + x-summary-zh: 美股财务概览 + x-summary-zh-hk: 美股財務概覽 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get financial overview for a US stock by reporting period — income, balance sheet, and cash flow summary. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 按报告周期获取美股财务概览——损益、资产负债和现金流摘要。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 按報告週期獲取美股財務概覽——損益、資產負債和現金流摘要。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Stock symbol, e.g. `AAPL.US` + x-description-zh: 股票代码,如 `AAPL.US` + x-description-zh-hk: 股票代碼,如 `AAPL.US` + - name: report + in: query + type: string + required: true + description: 'Period: `annual` or `quarterly` (default: annual)' + x-description-zh: 报告周期:`annual` 或 `quarterly`(默认:annual) + x-description-zh-hk: 報告週期:`annual` 或 `quarterly`(默認:annual) + x-codeSamples: + - lang: Shell + label: CLI + source: | + # US financial overview + longbridge financial-report AAPL.US + longbridge financial-report TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/finn-overview?symbol=<symbol>&report=<report>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/finn-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "report": "<report>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/finn-overview", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "report": "<report>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/finn-overview") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("report", "<report>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/finn-overview?symbol=<symbol>&report=<report>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/finn-overview") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("report", "<report>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/finn-overview?symbol=<symbol>&report=<report>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/finn-overview?symbol=<symbol>&report=<report>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: is_list + type: object[] + required: false + description: Income statement items + x-description-zh: 损益表条目列表 + x-description-zh-hk: 損益表條目列表 + - name: └ report + type: object + required: false + description: Reporting period info + x-description-zh: 报告期信息 + x-description-zh-hk: 報告期信息 + - name: └ ∟ report_txt + type: string + required: false + description: Period label (e.g. `FY2024`, `Q1 2024`) + x-description-zh: 报告期标签(如 `FY2024`、`Q1 2024`) + x-description-zh-hk: 報告期標籤(如 `FY2024`、`Q1 2024`) + - name: └ ∟ start_date + type: string + required: false + description: Period start date (YYYY-MM-DD) + x-description-zh: 报告期开始日期 + x-description-zh-hk: 報告期開始日期 + - name: └ ∟ end_date + type: string + required: false + description: Period end date (YYYY-MM-DD) + x-description-zh: 报告期结束日期 + x-description-zh-hk: 報告期結束日期 + - name: └ revenue + type: string + required: false + description: Total revenue + x-description-zh: 总营收 + x-description-zh-hk: 總營收 + - name: └ net_income + type: string + required: false + description: Net income + x-description-zh: 净利润 + x-description-zh-hk: 淨利潤 + - name: └ net_margin + type: string + required: false + description: Net profit margin + x-description-zh: 净利润率 + x-description-zh-hk: 淨利潤率 + - name: bs_list + type: object[] + required: false + description: Balance sheet items + x-description-zh: 资产负债表条目列表 + x-description-zh-hk: 資產負債表條目列表 + - name: └ report + type: object + required: false + description: Reporting period info + x-description-zh: 报告期信息 + x-description-zh-hk: 報告期信息 + - name: └ ∟ report_txt + type: string + required: false + description: Period label (e.g. `FY2024`, `Q1 2024`) + x-description-zh: 报告期标签(如 `FY2024`、`Q1 2024`) + x-description-zh-hk: 報告期標籤(如 `FY2024`、`Q1 2024`) + - name: └ ∟ start_date + type: string + required: false + description: Period start date (YYYY-MM-DD) + x-description-zh: 报告期开始日期 + x-description-zh-hk: 報告期開始日期 + - name: └ ∟ end_date + type: string + required: false + description: Period end date (YYYY-MM-DD) + x-description-zh: 报告期结束日期 + x-description-zh-hk: 報告期結束日期 + - name: └ total_assets + type: string + required: false + description: Total assets + x-description-zh: 总资产 + x-description-zh-hk: 總資產 + - name: └ total_liabilities + type: string + required: false + description: Total liabilities + x-description-zh: 总负债 + x-description-zh-hk: 總負債 + - name: └ debt_assets_ratio + type: string + required: false + description: Debt-to-assets ratio + x-description-zh: 资产负债率 + x-description-zh-hk: 資產負債率 + - name: cf_list + type: object[] + required: false + description: Cash flow items + x-description-zh: 现金流量表条目列表 + x-description-zh-hk: 現金流量表條目列表 + - name: └ report + type: object + required: false + description: Reporting period info + x-description-zh: 报告期信息 + x-description-zh-hk: 報告期信息 + - name: └ ∟ report_txt + type: string + required: false + description: Period label (e.g. `FY2024`, `Q1 2024`) + x-description-zh: 报告期标签(如 `FY2024`、`Q1 2024`) + x-description-zh-hk: 報告期標籤(如 `FY2024`、`Q1 2024`) + - name: └ ∟ start_date + type: string + required: false + description: Period start date (YYYY-MM-DD) + x-description-zh: 报告期开始日期 + x-description-zh-hk: 報告期開始日期 + - name: └ ∟ end_date + type: string + required: false + description: Period end date (YYYY-MM-DD) + x-description-zh: 报告期结束日期 + x-description-zh-hk: 報告期結束日期 + - name: └ operating + type: string + required: false + description: Operating cash flow + x-description-zh: 经营活动现金流 + x-description-zh-hk: 經營活動現金流 + - name: └ investing + type: string + required: false + description: Investing cash flow + x-description-zh: 投资活动现金流 + x-description-zh-hk: 投資活動現金流 + - name: └ financing + type: string + required: false + description: Financing cash flow + x-description-zh: 筹资活动现金流 + x-description-zh-hk: 籌資活動現金流 + - name: ccy_symbol + type: string + required: false + description: Currency symbol + x-description-zh: 货币符号 + x-description-zh-hk: 貨幣符號 + - name: report_type + type: string + required: false + description: Report type (e.g. `annual`, `quarterly`) + x-description-zh: 报告类型(如 `annual`、`quarterly`) + x-description-zh-hk: 報告類型(如 `annual`、`quarterly`) + responses: + '200': + description: Successful response + content: + application/json: + example: + ccy_symbol: USD + report_type: annual + is_list: + - revenue: '391035000000' + net_income: '93736000000' + net_margin: '0.2397' + report: + start_date: '2023-10-01' + end_date: '2024-09-28' + report_txt: FY2024 + bs_list: + - debt_assets_ratio: '0.8193' + total_assets: '364840000000' + total_liabilities: '308927000000' + report: + start_date: '2023-10-01' + end_date: '2024-09-28' + report_txt: FY2024 + cf_list: + - operating: '118254000000' + investing: '-21013000000' + financing: '-89831000000' + report: + start_date: '2023-10-01' + end_date: '2024-09-28' + report_txt: FY2024 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/quote/financials/statements: + get: + operationId: us_financial_statement + summary: US Financial Statement + x-summary-zh: 美股财务报表 + x-summary-zh-hk: 美股財務報表 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get a specific financial statement (income statement, balance sheet, or cash flow) for a US stock. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股指定财务报表(损益表、资产负债表或现金流量表)。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股指定財務報表(損益表、資產負債表或現金流量表)。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Stock symbol, e.g. `AAPL.US` + x-description-zh: 股票代码,如 `AAPL.US` + x-description-zh-hk: 股票代碼,如 `AAPL.US` + - name: kind + in: query + type: string + required: true + description: 'Statement type: `IS` (income), `BS` (balance sheet), `CF` (cash flow)' + x-description-zh: 报表类型:`IS`(损益表)、`BS`(资产负债表)、`CF`(现金流量表) + x-description-zh-hk: 報表類型:`IS`(損益表)、`BS`(資產負債表)、`CF`(現金流量表) + - name: report + in: query + type: string + required: true + description: 'Period: `af` (annual), `saf` (semi-annual), `qf` (quarterly), `q1` (Q1), `3q` (Q3)' + x-description-zh: 报告周期 + x-description-zh-hk: 報告週期 + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Income statement + longbridge financial-statement AAPL.US --kind IS + # Balance sheet + longbridge financial-statement AAPL.US --kind BS + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/quote/financials/statements?symbol=<symbol>&kind=<kind>&report=<report>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/quote/financials/statements", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "kind": "<kind>", "report": "<report>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/quote/financials/statements", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "kind": "<kind>", "report": "<report>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/quote/financials/statements") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("kind", "<kind>") + url.searchParams.set("report", "<report>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/quote/financials/statements?symbol=<symbol>&kind=<kind>&report=<report>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/quote/financials/statements") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("kind", "<kind>"), ("report", "<report>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/quote/financials/statements?symbol=<symbol>&kind=<kind>&report=<report>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/quote/financials/statements?symbol=<symbol>&kind=<kind>&report=<report>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: currency + type: string + required: true + description: Currency code (e.g. `USD`) + x-description-zh: 货币代码,如 `USD` + x-description-zh-hk: 貨幣代碼,如 `USD` + - name: report + type: string + required: true + description: Report period type (e.g. `annual`, `quarterly`) + x-description-zh: 报告周期类型(如 `annual`、`quarterly`) + x-description-zh-hk: 報告週期類型(如 `annual`、`quarterly`) + - name: empty_fields + type: string[] + required: false + description: Fields with no data for this period + x-description-zh: 本期无数据的字段列表 + x-description-zh-hk: 本期無數據的字段列表 + - name: list + type: USFinancialStatementPeriod[] + required: true + description: Statement data by period + x-description-zh: 按报告期排列的报表数据 + x-description-zh-hk: 按報告期排列的報表數據 + - name: ff_period + type: string + required: true + description: Period type code (e.g. `A`=annual, `Q`=quarterly) + x-description-zh: 报告周期代码(如 `A`=年报、`Q`=季报) + x-description-zh-hk: 報告週期代碼(如 `A`=年報、`Q`=季報) + - name: ff_year + type: int + required: true + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: fp_end + type: string + required: true + description: Period end date (YYYY-MM-DD) + x-description-zh: 报告期结束日期 + x-description-zh-hk: 報告期結束日期 + - name: report_txt + type: string + required: true + description: Period label (e.g. `FY2024`) + x-description-zh: 报告期标签(如 `FY2024`) + x-description-zh-hk: 報告期標籤(如 `FY2024`) + - name: rpt_date + type: string + required: true + description: Report release date (YYYY-MM-DD) + x-description-zh: 财报发布日期 + x-description-zh-hk: 財報發布日期 + - name: fields + type: USFinancialStatementField[] + required: true + description: Financial line items + x-description-zh: 财务行项目列表 + x-description-zh-hk: 財務行項目列表 + - name: id + type: string + required: true + description: Field identifier + x-description-zh: 字段标识 + x-description-zh-hk: 字段標識 + - name: value + type: string + required: true + description: Field value + x-description-zh: 字段值 + x-description-zh-hk: 字段值 + - name: yoy + type: string + required: false + description: Year-over-year change rate + x-description-zh: 同比变动率 + x-description-zh-hk: 同比變動率 + - name: level + type: int + required: true + description: Hierarchy level (1=top level) + x-description-zh: 层级(1=顶层) + x-description-zh-hk: 層級(1=頂層) + - name: display_order + type: int + required: true + description: Display order + x-description-zh: 显示顺序 + x-description-zh-hk: 顯示順序 + - name: field + type: string + required: true + description: Field key name + x-description-zh: 字段键名 + x-description-zh-hk: 字段鍵名 + - name: value_type + type: string + required: true + description: Value type (e.g. `amount`, `ratio`) + x-description-zh: 值类型(如 `amount`、`ratio`) + x-description-zh-hk: 值類型(如 `amount`、`ratio`) + responses: + '200': + description: Successful response + content: + application/json: + example: + currency: USD + report: af + empty_fields: [] + list: + - ff_period: A + ff_year: 2024 + fp_end: '2024-09-28' + report_txt: FY2024 + rpt_date: '2024-11-01' + fields: + - id: revenue + name: Total Revenue + value: '391035000000' + yoy: '0.0198' + level: 1 + display_order: 1 + field: revenue + value_type: amount + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/fin-keyfactor: + get: + operationId: us_key_financial_metrics + summary: US Key Financial Metrics + x-summary-zh: 美股关键财务指标 + x-summary-zh-hk: 美股關鍵財務指標 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get key financial metrics for a US stock — revenue, net income, EPS, margins, and growth rates. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股关键财务指标——营收、净利润、EPS、利润率和增长率。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股關鍵財務指標——營收、淨利潤、EPS、利潤率和增長率。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Stock symbol, e.g. `AAPL.US` + x-description-zh: 股票代码,如 `AAPL.US` + x-description-zh-hk: 股票代碼,如 `AAPL.US` + - name: report + in: query + type: string + required: false + description: 'Period: `af` (annual), `saf` (semi-annual), `qf` (quarterly), `q1` (Q1), `3q` (Q3)' + x-description-zh: 报告周期:`af`(年报)、`saf`(半年报)、`qf`(季报)、`q1`(Q1)、`3q`(Q3) + x-description-zh-hk: 報告週期:`af`(年報)、`saf`(半年報)、`qf`(季報)、`q1`(Q1)、`3q`(Q3) + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Key financial metrics (US accounts) + longbridge financial-report key-metrics AAPL.US + longbridge financial-report key-metrics AAPL.US --report quarterly + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/fin-keyfactor?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: currency + type: string + required: true + description: Currency code (e.g. `USD`) + x-description-zh: 货币代码,如 `USD` + x-description-zh-hk: 貨幣代碼,如 `USD` + - name: report + type: string + required: true + description: Report period type (e.g. `annual`, `quarterly`) + x-description-zh: 报告周期类型(如 `annual`、`quarterly`) + x-description-zh-hk: 報告週期類型(如 `annual`、`quarterly`) + - name: empty_fields + type: string[] + required: false + description: Fields with no data for this period + x-description-zh: 本期无数据的字段列表 + x-description-zh-hk: 本期無數據的字段列表 + - name: list + type: USKeyMetricItem[] + required: true + description: Key metric records by period + x-description-zh: 按报告期排列的关键指标数据 + x-description-zh-hk: 按報告期排列的關鍵指標數據 + - name: ff_period + type: string + required: true + description: Period type code (e.g. `A`=annual, `Q`=quarterly) + x-description-zh: 报告周期代码(如 `A`=年报、`Q`=季报) + x-description-zh-hk: 報告週期代碼(如 `A`=年報、`Q`=季報) + - name: ff_year + type: int + required: true + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: fp_end + type: string + required: true + description: Period end date (YYYY-MM-DD) + x-description-zh: 报告期结束日期 + x-description-zh-hk: 報告期結束日期 + - name: report_txt + type: string + required: true + description: Period label (e.g. `FY2024`) + x-description-zh: 报告期标签(如 `FY2024`) + x-description-zh-hk: 報告期標籤(如 `FY2024`) + - name: rpt_date + type: string + required: true + description: Report release date (YYYY-MM-DD) + x-description-zh: 财报发布日期 + x-description-zh-hk: 財報發佈日期 + - name: fields + type: object[] + required: true + description: Key financial metric values (structure varies by company) + x-description-zh: 关键财务指标(结构因公司而异) + x-description-zh-hk: 關鍵財務指標(結構因公司而異) + responses: + '200': + description: Successful response + content: + application/json: + example: + currency: USD + report: af + empty_fields: [] + list: + - ff_period: A + ff_year: 2024 + fp_end: '2024-09-28' + report_txt: FY2024 + rpt_date: '2024-11-01' + fields: + - key: revenue + value: '391035000000' + - key: gross_margin + value: '0.4621' + - key: net_margin + value: '0.2397' + - key: eps + value: '6.07' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/fin-consensus: + get: + operationId: us_analyst_consensus + summary: US Analyst Consensus + x-summary-zh: 美股分析师一致预期 + x-summary-zh-hk: 美股分析師一致預期 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get analyst consensus estimates for a US stock — revenue, EPS forecasts, and buy/hold/sell ratings. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股分析师一致预期——营收、EPS 预测及买入/持有/卖出评级。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股分析師一致預期——營收、EPS 預測及買入/持有/賣出評級。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Stock symbol, e.g. `AAPL.US` + x-description-zh: 股票代码,如 `AAPL.US` + x-description-zh-hk: 股票代碼,如 `AAPL.US` + - name: report + in: query + type: string + required: false + description: 'Period: `af` (annual), `saf` (semi-annual), `qf` (quarterly), `q1` (Q1), `3q` (Q3)' + x-description-zh: 报告周期:`af`(年报)、`saf`(半年报)、`qf`(季报)、`q1`(Q1)、`3q`(Q3) + x-description-zh-hk: 報告週期:`af`(年報)、`saf`(半年報)、`qf`(季報)、`q1`(Q1)、`3q`(Q3) + x-codeSamples: + - lang: Shell + label: CLI + source: | + # US analyst consensus + longbridge consensus AAPL.US + longbridge consensus NVDA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/fin-consensus?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/fin-consensus", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/fin-consensus", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/fin-consensus") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/fin-consensus?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/fin-consensus") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/fin-consensus?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/fin-consensus?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: ai_summary + type: string + required: true + description: AI-generated analyst consensus summary + x-description-zh: AI 生成的分析师一致预期摘要 + x-description-zh-hk: AI 生成的分析師一致預期摘要 + - name: aichat_data + type: USAIChatData + required: true + description: AI chat context data + x-description-zh: AI 对话上下文数据 + x-description-zh-hk: AI 對話上下文數據 + - name: currency + type: string + required: true + description: Currency code (e.g. `USD`) + x-description-zh: 货币代码,如 `USD` + x-description-zh-hk: 貨幣代碼,如 `USD` + - name: report + type: string + required: true + description: Report period type + x-description-zh: 报告周期类型 + x-description-zh-hk: 報告週期類型 + - name: list + type: USConsensusItem[] + required: true + description: Consensus estimates by fiscal year + x-description-zh: 按财年排列的一致预期数据 + x-description-zh-hk: 按財年排列的一致預期數據 + - name: opt_reports + type: string[] + required: false + description: Optional available report periods + x-description-zh: 可选的报告期列表 + x-description-zh-hk: 可選的報告期列表 + - name: h5_data + type: any + required: false + description: H5 display data + x-description-zh: H5 展示数据 + x-description-zh-hk: H5 展示數據 + responses: + '200': + description: Successful response + content: + application/json: + example: + ai_summary: Analysts remain broadly bullish on AAPL with 35 Buy ratings... + aichat_data: + agent_id: analyst_aapl + handoff_agent_id: '' + symbol: AAPL.US + text: Analyst consensus summary for AAPL + type: consensus + workflow_type: analyst + currency: USD + report: af + list: + - fiscal_year: 2025 + report_txt: FY2025 + revenue: + actual: '391035000000' + estimate: '388000000000' + eps: + actual: '6.42' + estimate: '6.29' + ebit: + actual: '125820000000' + estimate: '122000000000' + opt_reports: + - af + - qf + h5_data: null + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/etf-dividend-info: + get: + operationId: us_etf_dividend_info + summary: US ETF Dividend Info + x-summary-zh: 美股 ETF 分红信息 + x-summary-zh-hk: 美股 ETF 分紅資訊 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get dividend information for a US ETF — TTM dividend yield, payout frequency, and fiscal year breakdown. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股 ETF 分红信息——TTM 股息率、派息频率及财年明细。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股 ETF 分紅資訊——TTM 股息率、派息頻率及財年明細。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: ETF symbol, e.g. `IVV.US` + x-description-zh: ETF 代码,如 `IVV.US` + x-description-zh-hk: ETF 代碼,如 `IVV.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # ETF dividend info (US accounts) + longbridge dividend IVV.US + longbridge dividend SPY.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/etf-dividend-info?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: dividend_ttm + type: string + required: false + description: TTM dividend per share + x-description-zh: 过去 12 个月每股股息 + x-description-zh-hk: 過去 12 個月每股股息 + - name: dividend_yield_ttm + type: string + required: false + description: TTM dividend yield (%) + x-description-zh: TTM 股息率(%) + x-description-zh-hk: TTM 股息率(%) + - name: dividend_frequency + type: string + required: false + description: Payout frequency (e.g. `Quarterly`) + x-description-zh: 派息频率(如 `Quarterly`) + x-description-zh-hk: 派息頻率(如 `Quarterly`) + - name: currency + type: string + required: false + description: Currency code (e.g. `USD`) + x-description-zh: 货币代码,如 `USD` + x-description-zh-hk: 貨幣代碼,如 `USD` + - name: fiscal_year_info + type: object[] + required: false + description: Annual dividend breakdown by fiscal year + x-description-zh: 按财年分列的年度分红明细 + x-description-zh-hk: 按財年分列的年度分紅明細 + - name: └ dividend + type: string + required: false + description: Dividend per share. + x-description-zh: 每股股息。 + x-description-zh-hk: 每股股息。 + - name: └ dividend_yield + type: string + required: false + description: Dividend yield. + x-description-zh: 股息率。 + x-description-zh-hk: 股息率。 + - name: └ fiscal_year + type: string + required: false + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: └ currency + type: string + required: false + description: Currency code (e.g. `USD`) + x-description-zh: 货币代码,如 `USD` + x-description-zh-hk: 貨幣代碼,如 `USD` + - name: └ fiscal_year_range + type: string + required: false + description: Fiscal year date range + x-description-zh: 财年日期区间 + x-description-zh-hk: 財年日期區間 + responses: + '200': + description: Successful response + content: + application/json: + example: + dividend_ttm: '6.84' + dividend_yield_ttm: '0.0134' + dividend_frequency: Quarterly + currency: USD + fiscal_year_info: + - fiscal_year: '2025' + fiscal_year_range: 2025-01-01 ~ 2025-12-31 + dividend: '6.52' + dividend_yield: '0.0134' + currency: USD + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/company-dividends: + get: + operationId: us_company_dividends + summary: US Company Dividends + x-summary-zh: 美股公司分红 + x-summary-zh-hk: 美股公司分紅 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get dividend history for a US stock — TTM yield, payout count, and individual dividend records. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股股票分红历史——TTM 股息率、派息次数及逐笔分红记录。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股股票分紅歷史——TTM 股息率、派息次數及逐筆分紅記錄。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: Stock symbol, e.g. `AAPL.US` + x-description-zh: 股票代码,如 `AAPL.US` + x-description-zh-hk: 股票代碼,如 `AAPL.US` + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Company dividends (US accounts) + longbridge dividend AAPL.US + longbridge dividend MSFT.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/company-dividends?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/company-dividends", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/company-dividends", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/company-dividends") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/company-dividends?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/company-dividends") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/company-dividends?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/company-dividends?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: recent_dividends + type: object + required: false + description: Recent dividend summary + x-description-zh: 近期分红摘要 + x-description-zh-hk: 近期分紅摘要 + - name: └ dividend_ttm + type: string + required: false + description: TTM dividend per share + x-description-zh: 过去 12 个月每股股息 + x-description-zh-hk: 過去 12 個月每股股息 + - name: └ dividend_yield_ttm + type: string + required: false + description: TTM dividend yield (%) + x-description-zh: TTM 股息率(%) + x-description-zh-hk: TTM 股息率(%) + - name: └ payouts + type: string + required: false + description: Payout records. + x-description-zh: 派息记录。 + x-description-zh-hk: 派息記錄。 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: dividend_history + type: object[] + required: false + description: Annual dividend history + x-description-zh: 历年分红历史 + x-description-zh-hk: 歷年分紅歷史 + - name: └ fiscal_year + type: string + required: false + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: └ fiscal_year_range + type: string + required: false + description: Fiscal year date range + x-description-zh: 财年日期区间 + x-description-zh-hk: 財年日期區間 + - name: └ dividend + type: string + required: false + description: Dividend per share. + x-description-zh: 每股股息。 + x-description-zh-hk: 每股股息。 + - name: └ dividend_yield + type: string + required: false + description: Dividend yield. + x-description-zh: 股息率。 + x-description-zh-hk: 股息率。 + - name: └ dividend_growth_rate + type: string + required: false + description: Dividend growth rate. + x-description-zh: 股息增长率。 + x-description-zh-hk: 股息增長率。 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: └ total_shareholder_yield + type: string + required: false + description: Total shareholder yield. + x-description-zh: 股东总回报率。 + x-description-zh-hk: 股東總回報率。 + - name: └ dividend_payout_ratio + type: string + required: false + description: Dividend payout ratio. + x-description-zh: 股息支付率。 + x-description-zh-hk: 股息支付率。 + - name: └ dividend_to_cashflow_ratio + type: string + required: false + description: Dividend-to-cashflow ratio. + x-description-zh: 股息与现金流比率。 + x-description-zh-hk: 股息與現金流比率。 + - name: └ net_buyback + type: string + required: false + description: Net buyback amount + x-description-zh: 净回购金额 + x-description-zh-hk: 淨回購金額 + - name: └ net_buyback_yield + type: string + required: false + description: Buyback yield + x-description-zh: 回购收益率 + x-description-zh-hk: 回購收益率 + - name: └ net_buyback_growth_rate + type: string + required: false + description: Buyback growth rate + x-description-zh: 回购增长率 + x-description-zh-hk: 回購增長率 + - name: └ net_buyback_payout_ratio + type: string + required: false + description: Buyback payout ratio + x-description-zh: 回购支付比率 + x-description-zh-hk: 回購支付比率 + - name: └ net_buyback_to_cashflow_ratio + type: string + required: false + description: Buyback to free cash flow ratio + x-description-zh: 回购占自由现金流比率 + x-description-zh-hk: 回購佔自由現金流比率 + - name: payout_ratios + type: object[] + required: false + description: Payout ratio history + x-description-zh: 派息率历史 + x-description-zh-hk: 派息率歷史 + - name: └ fiscal_year + type: string + required: false + description: Fiscal year + x-description-zh: 财年 + x-description-zh-hk: 財年 + - name: └ fiscal_year_range + type: string + required: false + description: Fiscal year date range + x-description-zh: 财年日期区间 + x-description-zh-hk: 財年日期區間 + - name: └ dividend_payout_ratio + type: string + required: false + description: Dividend payout ratio. + x-description-zh: 股息支付率。 + x-description-zh-hk: 股息支付率。 + - name: └ dividend_to_cashflow_ratio + type: string + required: false + description: Dividend-to-cashflow ratio. + x-description-zh: 股息与现金流比率。 + x-description-zh-hk: 股息與現金流比率。 + - name: └ total_shareholder_yield + type: string + required: false + description: Total shareholder yield. + x-description-zh: 股东总回报率。 + x-description-zh-hk: 股東總回報率。 + - name: └ dividend + type: string + required: false + description: Dividend per share. + x-description-zh: 每股股息。 + x-description-zh-hk: 每股股息。 + - name: └ dividend_yield + type: string + required: false + description: Dividend yield. + x-description-zh: 股息率。 + x-description-zh-hk: 股息率。 + - name: └ dividend_growth_rate + type: string + required: false + description: Dividend growth rate. + x-description-zh: 股息增长率。 + x-description-zh-hk: 股息增長率。 + - name: └ net_buyback + type: string + required: false + description: Net buyback amount + x-description-zh: 净回购金额 + x-description-zh-hk: 淨回購金額 + - name: └ net_buyback_yield + type: string + required: false + description: Buyback yield + x-description-zh: 回购收益率 + x-description-zh-hk: 回購收益率 + - name: └ net_buyback_growth_rate + type: string + required: false + description: Buyback growth rate + x-description-zh: 回购增长率 + x-description-zh-hk: 回購增長率 + - name: └ net_buyback_payout_ratio + type: string + required: false + description: Buyback payout ratio + x-description-zh: 回购支付比率 + x-description-zh-hk: 回購支付比率 + - name: └ net_buyback_to_cashflow_ratio + type: string + required: false + description: Buyback to free cash flow ratio + x-description-zh: 回购占自由现金流比率 + x-description-zh-hk: 回購佔自由現金流比率 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: dividend_payout_history + type: object[] + required: false + description: Individual payout records + x-description-zh: 逐笔分红派发记录 + x-description-zh-hk: 逐筆分紅派發記錄 + - name: └ dividend + type: string + required: false + description: Dividend per share. + x-description-zh: 每股股息。 + x-description-zh-hk: 每股股息。 + - name: └ dividend_type + type: string + required: false + description: Dividend type. + x-description-zh: 股息类型。 + x-description-zh-hk: 股息類型。 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: └ ex_date + type: string + required: false + description: Ex-dividend date + x-description-zh: 除息日 + x-description-zh-hk: 除息日 + - name: └ payment_date + type: string + required: false + description: Payment date + x-description-zh: 派息日 + x-description-zh-hk: 派息日 + - name: └ record_date + type: string + required: false + description: Record date + x-description-zh: 股权登记日 + x-description-zh-hk: 股權登記日 + - name: └ title + type: string + required: false + description: Topic title + x-description-zh: 标题 + x-description-zh-hk: 標題 + - name: └ start_time_unix + type: string + required: false + description: Start time (Unix seconds). + x-description-zh: 开始时间(Unix 秒)。 + x-description-zh-hk: 開始時間(Unix 秒)。 + responses: + '200': + description: Successful response + content: + application/json: + example: + recent_dividends: + dividend_ttm: '1.00' + dividend_yield_ttm: '0.0053' + payouts: '4' + currency: USD + dividend_history: + - fiscal_year: '2024' + fiscal_year_range: 2024-01-01 ~ 2024-12-31 + dividend: '1.00' + dividend_yield: '0.0053' + dividend_growth_rate: '0.0408' + dividend_payout_ratio: '0.1497' + total_shareholder_yield: '0.0163' + currency: USD + payout_ratios: + - fiscal_year: '2024' + fiscal_year_range: 2024-01-01 ~ 2024-12-31 + dividend_payout_ratio: '0.1497' + currency: USD + dividend_payout_history: + - dividend: '0.25' + dividend_type: Cash + currency: USD + ex_date: '2024-11-08' + payment_date: '2024-11-14' + record_date: '2024-11-11' + title: Q4 FY2024 Dividend + start_time_unix: '1730000000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/us/stock-info/etf-files: + get: + operationId: us_etf_files + summary: US ETF Files + x-summary-zh: 美股 ETF 文件 + x-summary-zh-hk: 美股 ETF 文件 + description: | + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + List regulatory documents for a US ETF — prospectus, fact sheets, and annual reports. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 列出美股 ETF 的监管文件——招股书、事实说明书和年报。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 列出美股 ETF 的監管文件——招股書、事實說明書和年報。 + x-subgroup: Fundamentals + x-subgroup-zh: 基本面数据 + x-subgroup-zh-hk: 基本面數據 + tags: + - Fundamental + x-parameters: + - name: symbol + in: query + type: string + required: true + description: ETF symbol, e.g. `IVV.US` + x-description-zh: ETF 代码,如 `IVV.US` + x-description-zh-hk: ETF 代碼,如 `IVV.US` + - name: size + in: query + type: integer + required: false + description: Maximum number of files to return; omit to return all + x-description-zh: 最大返回文件数,不填则返回全部 + x-description-zh-hk: 最大返回文件數,不填則返回全部 + x-codeSamples: + - lang: Shell + label: CLI + source: | + # US ETF regulatory documents + longbridge etf-docs IVV.US + longbridge etf-docs SPY.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/stock-info/etf-files?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/stock-info/etf-files", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/stock-info/etf-files", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/stock-info/etf-files") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/stock-info/etf-files?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/stock-info/etf-files") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/stock-info/etf-files?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/stock-info/etf-files?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: files + type: object[] + required: false + description: List of ETF regulatory documents + x-description-zh: ETF 监管文件列表 + x-description-zh-hk: ETF 監管文件列表 + - name: └ file_name + type: string + required: false + description: File name. + x-description-zh: 文件名。 + x-description-zh-hk: 文件名。 + - name: └ file_path + type: string + required: false + description: File download path/URL. + x-description-zh: 文件下载路径/链接。 + x-description-zh-hk: 文件下載路徑/連結。 + - name: └ update_date + type: string + required: false + description: Last update date. + x-description-zh: 最后更新日期。 + x-description-zh-hk: 最後更新日期。 + - name: └ code + type: string + required: false + description: Ticker code (e.g. `TSLA`) + x-description-zh: 股票代码(如 `TSLA`) + x-description-zh-hk: 股票代碼(如 `TSLA`) + - name: └ format + type: string + required: false + description: File format, e.g. `PDF`. + x-description-zh: 文件格式,如 `PDF`。 + x-description-zh-hk: 文件格式,如 `PDF`。 + responses: + '200': + description: Successful response + content: + application/json: + example: + files: + - file_name: iShares Core S&P 500 ETF Prospectus + file_path: https://www.iShares.com/content/dam/iShares/prospectus/en/IVV.pdf + update_date: '2024-01-15' + code: IVV_PROSPECTUS + format: pdf + - file_name: iShares Core S&P 500 ETF Annual Report + file_path: https://www.iShares.com/content/dam/iShares/reports/en/IVV_AR.pdf + update_date: '2024-02-01' + code: IVV_ANNUAL + format: pdf + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/ai/agents: + get: + operationId: ai_public_agents + summary: Public Agents + x-summary-zh: 公开 Agent + x-summary-zh-hk: 公開 Agent + description: | + List all publicly available Agents on the platform — the same catalog shown on the Explore page. The returned `uid` is the Agent identifier used in the [Start Conversation](/docs/ai/chat/conversation) endpoint path. + + Unlike [Agents in Workspace](/docs/ai/workspace/agents), this endpoint is not scoped to a Workspace: it returns every Agent that is published and publicly shared. + x-description-zh: | + 列出平台上所有公开可用的 Agent——与探索页展示的是同一份目录。返回的 `uid` 即[发起对话](/zh-CN/docs/ai/chat/conversation)接口路径中使用的 Agent 标识。 + + 与 [Workspace 下的 Agent](/zh-CN/docs/ai/workspace/agents) 不同,本接口不限定 Workspace:返回所有已发布且公开分享的 Agent。 + x-description-zh-hk: | + 列出平台上所有公開可用的 Agent——與探索頁展示的是同一份目錄。返回的 `uid` 即[發起對話](/zh-HK/docs/ai/chat/conversation)接口路徑中使用的 Agent 標識。 + + 與 [Workspace 下的 Agent](/zh-HK/docs/ai/workspace/agents) 不同,本接口不限定 Workspace:返回所有已發佈且公開分享的 Agent。 + tags: + - AI Agent + x-parameters: + - name: page + in: query + type: integer + required: false + description: Page number, starts at 1, default 1 + x-description-zh: 页码,从 1 开始,默认 1 + x-description-zh-hk: 頁碼,從 1 開始,默認 1 + - name: limit + in: query + type: integer + required: false + description: Page size, default 20, maximum 50 + x-description-zh: 每页条数,默认 20,最大 50 + x-description-zh-hk: 每頁條數,默認 20,最大 50 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/ai/agents' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/ai/agents", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/ai/agents", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/ai/agents", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/ai/agents")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/ai/agents") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/ai/agents"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/ai/agents\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: agents + type: object[] + required: false + description: Agent list, ordered by last updated time descending + x-description-zh: Agent 列表,按最后更新时间倒序 + x-description-zh-hk: Agent 列表,按最後更新時間倒序 + - name: └ uid + type: string + required: false + description: Agent UID, used as the path parameter of [Start Conversation](/docs/ai/chat/conversation) + x-description-zh: Agent UID,用作 [发起对话](/zh-CN/docs/ai/chat/conversation) 的路径参数 + x-description-zh-hk: Agent UID,用作 [發起對話](/zh-HK/docs/ai/chat/conversation) 的路徑參數 + - name: └ name + type: string + required: false + description: Agent name, localized by the `Accept-Language` header + x-description-zh: Agent 名称,按 `Accept-Language` 请求头本地化 + x-description-zh-hk: Agent 名稱,按 `Accept-Language` 請求頭本地化 + - name: └ description + type: string + required: false + description: Agent description, localized by the `Accept-Language` header + x-description-zh: Agent 描述,按 `Accept-Language` 请求头本地化 + x-description-zh-hk: Agent 描述,按 `Accept-Language` 請求頭本地化 + - name: └ mode + type: string + required: false + description: Agent mode, e.g. `chat` + x-description-zh: Agent 模式,如 `chat` + x-description-zh-hk: Agent 模式,如 `chat` + - name: └ icon + type: string + required: false + description: Icon URL + x-description-zh: 图标 URL + x-description-zh-hk: 圖標 URL + - name: └ is_published + type: boolean + required: false + description: Always `true` for this endpoint + x-description-zh: 本接口下恒为 `true` + x-description-zh-hk: 本接口下恆為 `true` + - name: └ published_at + type: integer + required: false + description: Publish time, Unix timestamp in seconds + x-description-zh: 发布时间,Unix 秒级时间戳 + x-description-zh-hk: 發佈時間,Unix 秒級時間戳 + - name: └ created_at + type: integer + required: false + description: Creation time, Unix timestamp in seconds + x-description-zh: 创建时间,Unix 秒级时间戳 + x-description-zh-hk: 創建時間,Unix 秒級時間戳 + - name: └ updated_at + type: integer + required: false + description: Last updated time, Unix timestamp in seconds + x-description-zh: 最后更新时间,Unix 秒级时间戳 + x-description-zh-hk: 最後更新時間,Unix 秒級時間戳 + - name: total + type: integer + required: false + description: Total number of public Agents matching the query + x-description-zh: 符合条件的公开 Agent 总数 + x-description-zh-hk: 符合條件的公開 Agent 總數 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + agents: + - uid: ag_7d3f9b2c + name: US Stock Analyst + description: Answers US stock questions with market and fundamental data + mode: chat + icon: https://cdn.longbridge.com/icons/agent.png + is_published: true + published_at: 1742000000 + created_at: 1741000000 + updated_at: 1742001000 + total: 35 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/ai/workspaces: + get: + operationId: ai_workspaces + summary: My Workspaces + x-summary-zh: 我的 Workspace + x-summary-zh-hk: 我的 Workspace + description: | + List all Workspaces the current account belongs to. A Workspace is the organizational unit for Agents: find the target Workspace via this endpoint first, then use [Agents in Workspace](/docs/ai/workspace/agents) to list the Agents available in it. + x-description-zh: | + 获取当前账户加入的全部 Workspace 列表。Workspace 是 Agent 的组织单位,先通过本接口找到目标 Workspace,再用 [Workspace 下的 Agent](/zh-CN/docs/ai/workspace/agents) 查询其中可用的 Agent。 + x-description-zh-hk: | + 獲取當前賬戶加入的全部 Workspace 列表。Workspace 是 Agent 的組織單位,先通過本接口找到目標 Workspace,再用 [Workspace 下的 Agent](/zh-HK/docs/ai/workspace/agents) 查詢其中可用的 Agent。 + x-subgroup: Workspace + x-subgroup-zh: 工作空间 + x-subgroup-zh-hk: 工作空間 + tags: + - AI Agent + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/ai/workspaces' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/ai/workspaces", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/ai/workspaces", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/ai/workspaces", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/ai/workspaces")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/ai/workspaces") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/ai/workspaces"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/ai/workspaces\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: workspaces + type: object[] + required: false + description: Workspaces the current account belongs to + x-description-zh: 当前账户加入的 Workspace 列表 + x-description-zh-hk: 當前賬戶加入的 Workspace 列表 + - name: └ id + type: string + required: false + description: Workspace ID + x-description-zh: 工作区 ID + x-description-zh-hk: 工作區 ID + - name: └ name + type: string + required: false + description: Workspace name + x-description-zh: Workspace 名称 + x-description-zh-hk: Workspace 名稱 + - name: └ created_at + type: integer + required: false + description: Creation time, Unix timestamp in seconds + x-description-zh: 创建时间,Unix 时间戳(秒) + x-description-zh-hk: 創建時間,Unix 時間戳(秒) + - name: └ updated_at + type: integer + required: false + description: Last updated time, Unix timestamp in seconds + x-description-zh: 最后更新时间,Unix 时间戳(秒) + x-description-zh-hk: 最後更新時間,Unix 時間戳(秒) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + workspaces: + - id: '1001' + name: My Workspace + created_at: 1742000000 + updated_at: 1742001000 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/ai/workspaces/{workspace_id}/agents: + get: + operationId: ai_workspace_agents + summary: Agents in Workspace + x-summary-zh: Workspace 下的 Agent + x-summary-zh-hk: Workspace 下的 Agent + description: | + List the Agents in the specified Workspace. The returned `uid` is the Agent identifier used in the [Start Conversation](/docs/ai/chat/conversation) endpoint path; only Agents with `is_published` set to `true` can start conversations. + x-description-zh: | + 获取指定 Workspace 下的 Agent 列表。返回的 `uid` 即 [发起对话](/zh-CN/docs/ai/chat/conversation) 接口路径中的 Agent 标识;只有 `is_published` 为 `true` 的 Agent 才能发起对话。 + x-description-zh-hk: | + 獲取指定 Workspace 下的 Agent 列表。返回的 `uid` 即 [發起對話](/zh-HK/docs/ai/chat/conversation) 接口路徑中的 Agent 標識;只有 `is_published` 為 `true` 的 Agent 才能發起對話。 + x-subgroup: Workspace + x-subgroup-zh: 工作空间 + x-subgroup-zh-hk: 工作空間 + tags: + - AI Agent + x-parameters: + - name: workspace_id + in: path + type: string + required: true + description: Workspace ID + x-description-zh: 工作区 ID + x-description-zh-hk: 工作區 ID + - name: page + in: query + type: integer + required: false + description: Page number, starts at 1, default 1 + x-description-zh: 页码,从 1 开始,默认 1 + x-description-zh-hk: 頁碼,從 1 開始,默認 1 + - name: limit + in: query + type: integer + required: false + description: Page size, default 20 + x-description-zh: 每页数量,默认 20 + x-description-zh-hk: 每頁數量,默認 20 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/ai/workspaces/<workspace_id>/agents\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: agents + type: object[] + required: true + description: Agent list + x-description-zh: Agent 列表 + x-description-zh-hk: Agent 列表 + - name: └ uid + type: string + required: true + description: Agent UID, used as the path parameter of [Start Conversation](/docs/ai/chat/conversation) + x-description-zh: Agent UID,用于 [发起对话](/zh-CN/docs/ai/chat/conversation) 的路径参数 + x-description-zh-hk: Agent UID,用於 [發起對話](/zh-HK/docs/ai/chat/conversation) 的路徑參數 + - name: └ name + type: string + required: true + description: Agent name + x-description-zh: Agent 名称 + x-description-zh-hk: Agent 名稱 + - name: └ description + type: string + required: false + description: Agent description + x-description-zh: Agent 描述 + x-description-zh-hk: Agent 描述 + - name: └ mode + type: string + required: true + description: Agent mode, e.g. `chat` + x-description-zh: Agent 模式,如 `chat` + x-description-zh-hk: Agent 模式,如 `chat` + - name: └ icon + type: string + required: false + description: Icon URL + x-description-zh: 图标 URL + x-description-zh-hk: 圖標 URL + - name: └ is_published + type: boolean + required: true + description: Whether published; only published Agents can start conversations + x-description-zh: 是否已发布,仅已发布的 Agent 可发起对话 + x-description-zh-hk: 是否已發佈,僅已發佈的 Agent 可發起對話 + - name: └ published_at + type: int64 + required: false + description: Publish time, Unix timestamp in seconds; 0 if unpublished + x-description-zh: 发布时间,Unix 时间戳(秒),未发布为 0 + x-description-zh-hk: 發佈時間,Unix 時間戳(秒),未發佈為 0 + - name: └ created_at + type: int64 + required: false + description: Creation time, Unix timestamp in seconds + x-description-zh: 创建时间,Unix 时间戳(秒) + x-description-zh-hk: 創建時間,Unix 時間戳(秒) + - name: └ updated_at + type: int64 + required: false + description: Last updated time, Unix timestamp in seconds + x-description-zh: 最后更新时间,Unix 时间戳(秒) + x-description-zh-hk: 最後更新時間,Unix 時間戳(秒) + - name: total + type: int32 + required: true + description: Total number of matching Agents + x-description-zh: 符合条件的 Agent 总数 + x-description-zh-hk: 符合條件的 Agent 總數 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + agents: + - uid: ag_7d3f9b2c + name: US Stock Analyst + description: Answers US stock questions with market and fundamental data + mode: chat + icon: https://cdn.longbridge.com/icons/agent.png + is_published: true + published_at: 1742000000 + created_at: 1741000000 + updated_at: 1742001000 + total: 12 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/content/{symbol}/news: + get: + operationId: list_news + summary: Security News + x-summary-zh: 个股资讯 + x-summary-zh-hk: 個股資訊 + description: | + Get the news list for a specified security. Browse the full feed on [News](https://longbridge.com/news). + x-description-zh: | + 获取指定股票的资讯列表。完整资讯流可访问 [资讯](https://longbridge.com/news)。 + x-description-zh-hk: | + 獲取指定股票的資訊列表。完整資訊流可瀏覽 [資訊](https://longbridge.com/news)。 + x-subgroup: News + x-subgroup-zh: 资讯 + x-subgroup-zh-hk: 資訊 + tags: + - News & Contents + x-parameters: + - name: symbol + in: path + type: string + required: true + description: Stock symbol, use `ticker.region` format, e.g. `AAPL.US` + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/content/<symbol>/news' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/content/<symbol>/news", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/content/<symbol>/news", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/content/<symbol>/news", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/content/<symbol>/news")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/content/<symbol>/news") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/content/<symbol>/news"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/content/<symbol>/news\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-codeSamples: + - lang: Shell + label: CLI + source: | + # latest news for Tesla + longbridge news TSLA.US + # latest news for Apple + longbridge news AAPL.US + # latest news for NVDA + longbridge news NVDA.US + x-response-properties: + - name: items + type: object[] + required: false + description: News list + x-description-zh: 资讯列表 + x-description-zh-hk: 資訊列表 + - name: └ id + type: string + required: false + description: News ID + x-description-zh: 资讯 ID + x-description-zh-hk: 資訊 ID + - name: └ title + type: string + required: false + description: Title + x-description-zh: 标题 + x-description-zh-hk: 標題 + - name: └ description + type: string + required: false + description: Summary/description + x-description-zh: 摘要/描述 + x-description-zh-hk: 摘要/描述 + - name: └ url + type: string + required: false + description: Detail page URL + x-description-zh: 资讯详情链接 + x-description-zh-hk: 資訊詳情鏈接 + - name: └ comments_count + type: integer + required: false + description: Comment count + x-description-zh: 评论数 + x-description-zh-hk: 評論數 + - name: └ likes_count + type: integer + required: false + description: Like count + x-description-zh: 点赞数 + x-description-zh-hk: 點贊數 + - name: └ shares_count + type: integer + required: false + description: Share count + x-description-zh: 分享数 + x-description-zh-hk: 分享數 + - name: └ published_at + type: string + required: false + description: Published time, Unix timestamp (seconds) + x-description-zh: 发布时间,Unix 时间戳(秒) + x-description-zh-hk: 發佈時間,Unix 時間戳(秒) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + items: + - id: '279528757' + title: Beats cross-industry collaboration breaks the circle with Nike! Apple aims to ignite a new wave of wearable consumer trends, while Nike bets on the narrative of "sports technology." + description: Apple's Beats has collaborated with Nike to launch a limited edition Powerbeats Pro 2 headphones, featuring Nike's Swoosh logo. The headphones will be available online and at select Apple Stores on March 20, priced at $250. This marks Beats' first collaboration with an external sports brand, signifying further synergy between the two companies in branding and product ecosystems. The headphones feature real-time heart rate tracking and a battery life of up to 45 hours + url: https://longbridge.com/news/279528757 + published_at: '1773805586' + comments_count: 0 + likes_count: 0 + shares_count: 0 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/content/{symbol}/topics: + get: + operationId: list_topics + summary: Topics by Symbol + x-summary-zh: 标的社区讨论 + x-summary-zh-hk: 標的社區討論 + description: | + Get the topic/discussion list for a specified security. Browse the full community on [Topics](https://longbridge.com/topics). + x-description-zh: | + 获取指定股票的讨论列表。完整社区讨论可访问 [社区](https://longbridge.com/topics)。 + x-description-zh-hk: | + 獲取指定股票的討論列表。完整社區討論可瀏覽 [社區](https://longbridge.com/topics)。 + x-subgroup: Topics + x-subgroup-zh: 话题 + x-subgroup-zh-hk: 話題 + tags: + - News & Contents + x-parameters: + - name: symbol + in: path + type: string + required: true + description: Stock symbol, use `ticker.region` format, e.g. `AAPL.US` + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/content/<symbol>/topics' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/content/<symbol>/topics", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/content/<symbol>/topics", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/content/<symbol>/topics", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/content/<symbol>/topics")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/content/<symbol>/topics") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/content/<symbol>/topics"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/content/<symbol>/topics\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-codeSamples: + - lang: Shell + label: CLI + source: | + # community discussion topics for Tesla + longbridge topic TSLA.US + # community discussion topics for Apple + longbridge topic AAPL.US + # community discussion topics for NVDA + longbridge topic NVDA.US + x-response-properties: + - name: items + type: object[] + required: false + description: Topic list + x-description-zh: 讨论列表 + x-description-zh-hk: 討論列表 + - name: └ id + type: string + required: false + description: Topic ID + x-description-zh: 讨论 ID + x-description-zh-hk: 討論 ID + - name: └ description + type: string + required: false + description: Summary/description + x-description-zh: 摘要/描述 + x-description-zh-hk: 摘要/描述 + - name: └ url + type: string + required: false + description: Detail page URL + x-description-zh: 讨论详情链接 + x-description-zh-hk: 討論詳情鏈接 + - name: └ comments_count + type: integer + required: false + description: Comment count + x-description-zh: 评论数 + x-description-zh-hk: 評論數 + - name: └ likes_count + type: integer + required: false + description: Like count + x-description-zh: 点赞数 + x-description-zh-hk: 點贊數 + - name: └ shares_count + type: integer + required: false + description: Share count + x-description-zh: 分享数 + x-description-zh-hk: 分享數 + - name: └ published_at + type: string + required: false + description: Published time, Unix timestamp (seconds) + x-description-zh: 发布时间,Unix 时间戳(秒) + x-description-zh-hk: 發佈時間,Unix 時間戳(秒) + - name: └ title + type: string + required: false + description: Title + x-description-zh: 标题 + x-description-zh-hk: 標題 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + items: + - id: '39304657' + title: NVDA GTC in focus; Alibaba 'Token strategy' ramps up | Daily News Recap + description: '0317 | Dolphin Research Focus: 🐬 Stock #1, $NVIDIA(NVDA.US) — NVIDIA''s GTC 2026 officially kicked off, and founder & CEO Jensen Huang delivered the keynote.He announced a Vera Rubin Space Module under the next-gen Vera Rubin architecture, designed for orbital data centers, delivering 25x performance vs. H100.He also unveiled a partnership with Groq to co-develop new LPU chips...' + url: https://longbridge.com/topics/39304657 + published_at: '1773736144' + comments_count: 1 + likes_count: 7 + shares_count: 4 + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/content/topics/mine: + get: + operationId: list_my_topics + summary: My Topics + x-summary-zh: 我的讨论 + x-summary-zh-hk: 我的討論 + description: | + Get the list of topics I have published. View them on [Topics](https://longbridge.com/topics). + x-description-zh: | + 获取当前登录用户发布的讨论列表,支持分页与类型过滤。可在 [社区](https://longbridge.com/topics) 查看。 + x-description-zh-hk: | + 獲取當前登錄用戶發布的討論列表,支持分頁與類型過濾。可在 [社區](https://longbridge.com/topics) 查看。 + x-subgroup: Topics + x-subgroup-zh: 话题 + x-subgroup-zh-hk: 話題 + tags: + - News & Contents + x-parameters: + - name: page + in: query + type: integer + required: false + description: Page number (1-based). Defaults to `1`. + x-description-zh: 页码,默认 1 + x-description-zh-hk: 頁碼,默認 1 + - name: size + in: query + type: integer + required: false + description: Number of items per page, range 1–500. Defaults to `50`. + x-description-zh: 每页数量,范围 1~500,默认 50 + x-description-zh-hk: 每頁數量,範圍 1~500,默認 50 + - name: topic_type + in: query + type: string + required: false + description: Filter by type. One of `article` (long-form), `post` (short post). Omit to return all types. + x-description-zh: 类型过滤,可选 `article`(长文)、`post`(短帖),不传返回全部 + x-description-zh-hk: 類型過濾,可選 `article`(長文)、`post`(短帖),不傳返回全部 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/content/topics/mine' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/content/topics/mine", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/content/topics/mine", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/content/topics/mine", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/content/topics/mine")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/content/topics/mine") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/content/topics/mine"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/content/topics/mine\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge topic mine + x-response-properties: + - name: items + type: object[] + required: false + description: Topic list + x-description-zh: 讨论列表 + x-description-zh-hk: 討論列表 + - name: └ id + type: string + required: false + description: Topic ID + x-description-zh: 讨论 ID + x-description-zh-hk: 討論 ID + - name: └ description + type: string + required: false + description: Plain-text summary of the topic body + x-description-zh: 纯文本摘要 + x-description-zh-hk: 純文本摘要 + - name: └ author + type: object + required: false + description: Author information + x-description-zh: 作者信息 + x-description-zh-hk: 作者信息 + - name: └ ∟ member_id + type: string + required: false + description: Author member ID + x-description-zh: 作者 member ID + x-description-zh-hk: 作者 member ID + - name: └ ∟ name + type: string + required: false + description: Author display name + x-description-zh: 作者昵称 + x-description-zh-hk: 作者暱稱 + - name: └ ∟ avatar + type: string + required: false + description: Author avatar URL + x-description-zh: 作者头像 URL + x-description-zh-hk: 作者頭像 URL + - name: └ tickers + type: array + required: false + description: Associated security symbols (e.g. `["AAPL.US", "700.HK"]`) + x-description-zh: 关联标的代码,如 `["AAPL.US", "700.HK"]` + x-description-zh-hk: 關聯標的代碼,如 `["AAPL.US", "700.HK"]` + - name: └ hashtags + type: array + required: false + description: Associated hashtag names + x-description-zh: 讨论标签名称列表 + x-description-zh-hk: 討論標籤名稱列表 + - name: └ images + type: array + required: false + description: Images attached to the topic + x-description-zh: 附图列表 + x-description-zh-hk: 附圖列表 + - name: └ likes_count + type: integer + required: false + description: Number of likes + x-description-zh: 点赞数 + x-description-zh-hk: 點讚數 + - name: └ comments_count + type: integer + required: false + description: Number of comments + x-description-zh: 评论数 + x-description-zh-hk: 評論數 + - name: └ views_count + type: integer + required: false + description: Number of views + x-description-zh: 浏览数 + x-description-zh-hk: 瀏覽數 + - name: └ shares_count + type: integer + required: false + description: Number of shares + x-description-zh: 分享数 + x-description-zh-hk: 分享數 + - name: └ topic_type + type: string + required: false + description: Topic type. One of `article`, `post` + x-description-zh: 内容类型,`article`(长文)或 `post`(短帖) + x-description-zh-hk: 內容類型,`article`(長文)或 `post`(短帖) + - name: └ detail_url + type: string + required: false + description: Link to the topic detail page + x-description-zh: 讨论详情页链接 + x-description-zh-hk: 討論詳情頁連結 + - name: └ created_at + type: string + required: false + description: Unix timestamp (seconds) when the topic was created + x-description-zh: 创建时间,Unix 时间戳(秒) + x-description-zh-hk: 創建時間,Unix 時間戳(秒) + - name: └ updated_at + type: string + required: false + description: Unix timestamp (seconds) when the topic was last updated + x-description-zh: 最后更新时间,Unix 时间戳(秒) + x-description-zh-hk: 最後更新時間,Unix 時間戳(秒) + - name: └ title + type: string + required: false + description: Topic title (may be empty for short posts) + x-description-zh: 标题(短帖可能为空) + x-description-zh-hk: 標題(短帖可能為空) + - name: └ body + type: string + required: false + description: Full topic body in Markdown format + x-description-zh: Markdown 格式正文 + x-description-zh-hk: Markdown 格式正文 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + items: + - id: '39304657' + title: My Analysis on AAPL + description: A brief summary of my article... + body: Full markdown content here... + topic_type: article + tickers: + - AAPL.US + hashtags: + - earnings + images: [] + likes_count: 12 + comments_count: 3 + views_count: 200 + shares_count: 1 + license: 1 + detail_url: https://longbridge.com/topics/39304657 + author: + member_id: '10086' + name: John + avatar: https://example.com/avatar.jpg + created_at: '1742000000' + updated_at: '1742000000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/content/topics/{id}: + get: + operationId: topic_detail + summary: Topic Detail + x-summary-zh: 讨论详情 + x-summary-zh-hk: 討論詳情 + description: | + Get the full details of a community topic by its ID, including the body (Markdown), author info, associated tickers and hashtags, engagement counts, and the direct URL. View the topic on [Topics](https://longbridge.com/topics). + x-description-zh: | + 根据讨论 ID 获取完整详情,包含正文(Markdown)、作者信息、关联标的与标签、互动数据及详情页链接。可在 [社区](https://longbridge.com/topics) 查看。 + x-description-zh-hk: | + 根據討論 ID 獲取完整詳情,包含正文(Markdown)、作者信息、關聯標的與標籤、互動數據及詳情頁鏈接。可在 [社區](https://longbridge.com/topics) 查看。 + x-subgroup: Topics + x-subgroup-zh: 话题 + x-subgroup-zh-hk: 話題 + tags: + - News & Contents + x-parameters: + - name: id + in: path + type: string + required: true + description: Topic ID (e.g. `6993508780031016960`) + x-description-zh: 讨论 ID,如 `6993508780031016960` + x-description-zh-hk: 討論 ID,如 `6993508780031016960` + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/content/topics/<id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/content/topics/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/content/topics/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/content/topics/<id>", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/content/topics/<id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/content/topics/<id>") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/content/topics/<id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/content/topics/<id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge topic detail 6993508780031016960 + x-response-properties: + - name: item + type: object + required: true + description: Topic detail object + x-description-zh: 讨论详情 + x-description-zh-hk: 討論詳情 + - name: └ id + type: string + required: true + description: Topic ID + x-description-zh: 讨论 ID + x-description-zh-hk: 討論 ID + - name: └ title + type: string + required: false + description: Title (may be empty for short posts) + x-description-zh: 标题(短帖可能为空) + x-description-zh-hk: 標題(短帖可能為空) + - name: └ description + type: string + required: false + description: Plain-text excerpt + x-description-zh: 纯文本摘要 + x-description-zh-hk: 純文本摘要 + - name: └ body + type: string + required: false + description: Markdown body + x-description-zh: Markdown 格式正文 + x-description-zh-hk: Markdown 格式正文 + - name: └ topic_type + type: string + required: true + description: 'Content type: `article` or `post`' + x-description-zh: 内容类型,`article`(长文)或 `post`(短帖) + x-description-zh-hk: 內容類型,`article`(長文)或 `post`(短帖) + - name: └ tickers + type: string[] + required: false + description: Associated security symbols (e.g. `["AAPL.US", "700.HK"]`) + x-description-zh: 关联标的代码,如 `["AAPL.US", "700.HK"]` + x-description-zh-hk: 關聯標的代碼,如 `["AAPL.US", "700.HK"]` + - name: └ hashtags + type: string[] + required: false + description: Hashtag names + x-description-zh: 讨论标签名称列表 + x-description-zh-hk: 討論標籤名稱列表 + - name: └ images + type: object[] + required: false + description: Attached images + x-description-zh: 附图列表 + x-description-zh-hk: 附圖列表 + - name: └ ∟ url + type: string + required: false + description: Original image URL + x-description-zh: 原始图片 URL + x-description-zh-hk: 原始圖片 URL + - name: └ ∟ sm + type: string + required: false + description: Small thumbnail URL + x-description-zh: 小缩略图 URL + x-description-zh-hk: 小縮略圖 URL + - name: └ ∟ lg + type: string + required: false + description: Large image URL + x-description-zh: 大缩略图 URL + x-description-zh-hk: 大縮略圖 URL + - name: └ likes_count + type: int32 + required: false + description: Likes count + x-description-zh: 点赞数 + x-description-zh-hk: 點讚數 + - name: └ comments_count + type: int32 + required: false + description: Replies count + x-description-zh: 回复数 + x-description-zh-hk: 回覆數 + - name: └ views_count + type: int32 + required: false + description: Views count + x-description-zh: 浏览数 + x-description-zh-hk: 瀏覽數 + - name: └ shares_count + type: int32 + required: false + description: Shares count + x-description-zh: 分享数 + x-description-zh-hk: 分享數 + - name: └ detail_url + type: string + required: false + description: URL to the topic detail page + x-description-zh: 讨论详情页链接 + x-description-zh-hk: 討論詳情頁鏈接 + - name: └ author + type: object + required: false + description: Author info + x-description-zh: 作者信息 + x-description-zh-hk: 作者信息 + - name: └ ∟ member_id + type: string + required: false + description: Author member ID + x-description-zh: 作者 member ID + x-description-zh-hk: 作者 member ID + - name: └ ∟ name + type: string + required: false + description: Author display name + x-description-zh: 作者昵称 + x-description-zh-hk: 作者暱稱 + - name: └ ∟ avatar + type: string + required: false + description: Author avatar URL + x-description-zh: 作者头像 URL + x-description-zh-hk: 作者頭像 URL + - name: └ created_at + type: string + required: true + description: Creation time as Unix timestamp (seconds) + x-description-zh: 创建时间,Unix 时间戳(秒) + x-description-zh-hk: 創建時間,Unix 時間戳(秒) + - name: └ updated_at + type: string + required: false + description: Last updated time as Unix timestamp (seconds) + x-description-zh: 最后更新时间,Unix 时间戳(秒) + x-description-zh-hk: 最後更新時間,Unix 時間戳(秒) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + item: + id: '6993508780031016960' + title: My Analysis on AAPL + description: Brief plain-text summary... + body: '**Bullish** on AAPL because...' + topic_type: article + tickers: + - AAPL.US + hashtags: + - earnings + images: + - url: https://cdn.longbridge.com/img/abc.jpg + sm: https://cdn.longbridge.com/img/abc_sm.jpg + lg: https://cdn.longbridge.com/img/abc_lg.jpg + likes_count: 42 + comments_count: 7 + views_count: 1500 + shares_count: 3 + detail_url: https://longbridge.com/topics/6993508780031016960 + author: + member_id: '10086' + name: Jane Doe + avatar: https://example.com/avatar.jpg + created_at: '1742000000' + updated_at: '1742001000' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/sharelists/popular: + get: + operationId: popular_sharelists + summary: Popular Sharelists + x-summary-zh: 热门股单 + x-summary-zh-hk: 熱門股單 + description: | + Get popular/trending sharelists from the community. + x-description-zh: | + 获取社区热门股单列表。 + x-description-zh-hk: | + 獲取社區熱門股單列表。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 + tags: + - News & Contents + x-parameters: + - name: count + in: query + type: integer + required: false + description: Maximum number of results, default 20 + x-description-zh: 返回数量上限,默认 20 + x-description-zh-hk: 返回數量上限,默認 20 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge sharelist popular --count 10 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/sharelists/popular' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/sharelists/popular", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/sharelists/popular", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/sharelists/popular", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists/popular")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/sharelists/popular") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists/popular"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/sharelists/popular\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: sharelists + type: object[] + required: false + description: User's own sharelists + x-description-zh: 用户自建股单列表, + x-description-zh-hk: 用戶自建股單列表, + - name: └ id + type: string + required: false + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID + - name: └ subscribers_count + type: integer + required: false + description: Number of subscribers + x-description-zh: 订阅人数 + x-description-zh-hk: 訂閱人數 + - name: └ stocks_count + type: integer + required: false + description: Number of securities in the list. + x-description-zh: 清单内标的数量。 + x-description-zh-hk: 清單內標的數量。 + - name: └ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ description + type: string + required: false + description: Description + x-description-zh: 描述 + x-description-zh-hk: 描述 + - name: └ cover + type: string + required: false + description: Cover image URL + x-description-zh: 封面图片 URL + x-description-zh-hk: 封面圖片 URL + - name: └ created_at + type: string + required: false + description: Unix timestamp (seconds) when the topic was created + x-description-zh: 创建时间,Unix 时间戳(秒) + x-description-zh-hk: 創建時間,Unix 時間戳(秒) + - name: └ this_year_chg + type: string + required: false + description: Year-to-date change percentage + x-description-zh: 今年以来涨跌幅 + x-description-zh-hk: 今年以來漲跌幅 + - name: └ status + type: integer + required: false + description: 'Final run status: `succeeded` / `interrupted` / `failed` / `stopped`' + x-description-zh: 运行终态:`succeeded` / `interrupted` / `failed` / `stopped` + x-description-zh-hk: 運行終態:`succeeded` / `interrupted` / `failed` / `stopped` + - name: └ edited_at + type: string + required: false + description: Last edit time. + x-description-zh: 最后编辑时间。 + x-description-zh-hk: 最後編輯時間。 + - name: └ digest + type: string + required: false + description: Short description / digest. + x-description-zh: 简介。 + x-description-zh-hk: 簡介。 + - name: └ creator + type: object + required: false + description: Creator user name. + x-description-zh: 创建者名称。 + x-description-zh-hk: 創建者名稱。 + - name: └ ∟ id + type: string + required: false + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID + - name: └ ∟ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ ∟ avatar + type: string + required: false + description: Author avatar URL + x-description-zh: 作者头像 URL + x-description-zh-hk: 作者頭像 URL + - name: └ ∟ member_id + type: string + required: false + description: Author member ID + x-description-zh: 作者 member ID + x-description-zh-hk: 作者 member ID + - name: └ ∟ scopes + type: object + required: false + description: Subscription scope info + x-description-zh: 订阅权限信息 + x-description-zh-hk: 訂閱權限資訊 + - name: └ ∟ ∟ followed + type: boolean + required: false + description: Whether the current user follows this list. + x-description-zh: 当前用户是否已关注。 + x-description-zh-hk: 當前用戶是否已關注。 + - name: └ ∟ ∟ following + type: boolean + required: false + description: Whether following the creator. + x-description-zh: 是否关注创建者。 + x-description-zh-hk: 是否關注創建者。 + - name: └ ∟ ∟ self + type: boolean + required: false + description: Whether the list belongs to the current user. + x-description-zh: 是否为当前用户本人。 + x-description-zh-hk: 是否為當前用戶本人。 + - name: └ ∟ ∟ blocked + type: boolean + required: false + description: Whether blocked by the current user. + x-description-zh: 是否被当前用户屏蔽。 + x-description-zh-hk: 是否被當前用戶屏蔽。 + - name: └ ∟ ∟ special_following + type: boolean + required: false + description: Whether specially followed. + x-description-zh: 是否特别关注。 + x-description-zh-hk: 是否特別關注。 + - name: └ ∟ ∟ be_blocked + type: boolean + required: false + description: Whether the current user is blocked. + x-description-zh: 当前用户是否被屏蔽。 + x-description-zh-hk: 當前用戶是否被屏蔽。 + - name: └ ∟ ∟ trade_following + type: boolean + required: false + description: Whether copy-trade following. + x-description-zh: 是否跟单关注。 + x-description-zh-hk: 是否跟單關注。 + - name: └ ∟ profile_id + type: string + required: false + description: Profile ID. + x-description-zh: 资料 ID。 + x-description-zh-hk: 資料 ID。 + - name: └ ∟ group_scopes + type: object + required: false + description: Group permission scopes. + x-description-zh: 分组权限范围。 + x-description-zh-hk: 分組權限範圍。 + - name: └ ∟ ∟ joined + type: boolean + required: false + description: Whether the user has joined. + x-description-zh: 是否已加入。 + x-description-zh-hk: 是否已加入。 + - name: └ ∟ group_member + type: string + required: false + description: Whether a group member. + x-description-zh: 是否群组成员。 + x-description-zh-hk: 是否羣組成員。 + - name: └ ∟ description + type: string + required: false + description: Description + x-description-zh: 描述 + x-description-zh-hk: 描述 + - name: └ ∟ invite_code + type: string + required: false + description: Invitation code. + x-description-zh: 邀请码。 + x-description-zh-hk: 邀請碼。 + - name: └ ∟ cert_scopes + type: object + required: false + description: Certification scopes. + x-description-zh: 认证范围。 + x-description-zh-hk: 認證範圍。 + - name: └ ∟ ∟ official + type: boolean + required: false + description: Whether an official list. + x-description-zh: 是否官方清单。 + x-description-zh-hk: 是否官方清單。 + - name: └ ∟ ∟ cert_Info + type: object + required: false + description: Certification info. + x-description-zh: 认证信息。 + x-description-zh-hk: 認證信息。 + - name: └ ∟ ∟ ∟ label + type: string + required: false + description: Label. + x-description-zh: 标签。 + x-description-zh-hk: 標籤。 + - name: └ ∟ ∟ ∟ name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: └ ∟ ∟ ∟ desc + type: string + required: false + description: Description of current valuation + x-description-zh: 当前估值描述 + x-description-zh-hk: 當前估值描述 + - name: └ ∟ ∟ ∟ icon + type: string + required: false + description: Icon URL + x-description-zh: 图标链接 + x-description-zh-hk: 圖標鏈接 + - name: └ ∟ ∟ security_live_info + type: string + required: false + description: Security live-stream info. + x-description-zh: 证券直播信息。 + x-description-zh-hk: 證券直播信息。 + - name: └ ∟ certification + type: boolean + required: false + description: Certification. + x-description-zh: 认证。 + x-description-zh-hk: 認證。 + - name: └ stocks + type: array + required: false + description: Constituent stocks + x-description-zh: 成份股列表, + x-description-zh-hk: 成份股列表, + - name: └ subscribed + type: boolean + required: false + description: Whether the current user is subscribed + x-description-zh: 当前用户是否已订阅 + x-description-zh-hk: 當前用戶是否已訂閱 + - name: └ stock_group_id + type: string + required: false + description: Stock-group ID. + x-description-zh: 股票分组 ID。 + x-description-zh-hk: 股票分組 ID。 + - name: └ last_read_log_id + type: string + required: false + description: Last-read change-log ID. + x-description-zh: 最后已读变更日志 ID。 + x-description-zh-hk: 最後已讀變更日誌 ID。 + - name: └ chg + type: string + required: false + description: Day change percentage + x-description-zh: 日涨跌幅 + x-description-zh-hk: 日漲跌幅 + - name: └ group_id + type: string + required: false + description: Group ID. + x-description-zh: 群组 ID。 + x-description-zh-hk: 羣組 ID。 + - name: └ sharelist_type + type: integer + required: false + description: 'Type: `0`=regular, `3`=official, `4`=industry' + x-description-zh: 类型:`0`=普通,`3`=官方,`4`=行业 + x-description-zh-hk: 類型:`0`=普通,`3`=官方,`4`=行業 + - name: └ industry_code + type: string + required: false + description: Industry code (for industry sharelists) + x-description-zh: 行业代码(行业股单适用) + x-description-zh-hk: 行業代碼(行業股單適用) + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: + list: + - id: 123 + name: AI Picks + description: Top AI infrastructure stocks + - id: 456 + name: EV Leaders + description: Electric vehicle sector leaders + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/sharelists/{id}: get: - operationId: list_watchlist_groups - summary: Get Watchlists - x-summary-zh: 获取自选股分组列表 + operationId: sharelist_detail + summary: Sharelist Detail + x-summary-zh: 股单详情 + x-summary-zh-hk: 股單詳情 description: | - Get all watchlist groups for the current user. Each group contains an ID, name, and a list - of securities with the price and timestamp at which each security was added. - x-description-zh: 获取当前用户的所有自选股分组,每个分组包含 ID、名称及其中的证券列表(含加入价格和时间戳)。 + Get sharelist detail including name, description, and constituent stocks. + x-description-zh: | + 获取股单详情,包括名称、描述及成分股列表。 + x-description-zh-hk: | + 獲取股單詳情,包括名稱、描述及成分股列表。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 tags: - - Watchlist Management + - News & Contents + x-parameters: + - name: id + in: path + type: integer + required: true + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID x-codeSamples: - lang: Shell label: CLI source: | - longbridge watchlist + longbridge sharelist detail 123 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/sharelists/<id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/sharelists/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/sharelists/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/sharelists/<id>", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists/<id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/sharelists/<id>") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists/<id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/sharelists/<id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: sharelist + type: object + required: true + description: Sharelist information + x-description-zh: 股单详情 + x-description-zh-hk: 股單詳情 + - name: scopes + type: object + required: false + description: Subscription scope info + x-description-zh: 订阅权限信息 + x-description-zh-hk: 訂閱權限資訊 + - name: id + type: integer + required: true + description: Sharelist ID + x-description-zh: 股单 ID + x-description-zh-hk: 股單 ID + - name: description + type: string + required: false + description: Description + x-description-zh: 描述 + x-description-zh-hk: 描述 + - name: cover + type: string + required: false + description: Cover image URL + x-description-zh: 封面图 URL + x-description-zh-hk: 封面圖 URL + - name: subscribers_count + type: integer + required: false + description: Number of subscribers + x-description-zh: 订阅人数 + x-description-zh-hk: 訂閱人數 + - name: chg + type: string + required: false + description: Day change percentage + x-description-zh: 当日涨跌幅 + x-description-zh-hk: 當日漲跌幅 + - name: this_year_chg + type: string + required: false + description: Year-to-date change + x-description-zh: 年初至今涨跌幅 + x-description-zh-hk: 年初至今漲跌幅 + - name: subscribed + type: boolean + required: false + description: Whether subscribed + x-description-zh: 是否已订阅 + x-description-zh-hk: 是否已訂閱 + - name: sharelist_type + type: integer + required: false + description: 'Type: `0`=regular, `3`=official, `4`=industry' + x-description-zh: 类型:`0`=普通,`3`=官方,`4`=行业 + x-description-zh-hk: 類型:`0`=普通,`3`=官方,`4`=行業 + - name: industry_code + type: string + required: false + description: Industry code + x-description-zh: 行业代码 + x-description-zh-hk: 行業代碼 + - name: is_self + type: boolean + required: false + description: Whether the current user is the creator + x-description-zh: 是否为创建者 + x-description-zh-hk: 是否為建立者 + - name: subscription + type: boolean + required: false + description: Whether the current user is subscribed + x-description-zh: 是否已订阅 + x-description-zh-hk: 是否已訂閱 responses: '200': description: Successful response @@ -524,126 +36148,636 @@ paths: code: 0 message: success data: - groups: - - id: '2630' - name: My Watchlist - securities: - - symbol: AAPL.US - market: US - name: Apple Inc. - watched_price: '211.59' - watched_at: '1741690995' - is_pinned: true - - symbol: 700.HK - market: HK - name: 腾讯控股 - watched_price: '460.00' - watched_at: '1725511157' - is_pinned: false + id: 123 + name: AI Picks + description: Top AI infrastructure stocks + securities: + - AAPL.US + - NVDA.US default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - post: - operationId: create_watchlist_group - summary: Create Watchlist - x-summary-zh: 创建自选股分组 - description: | - Create a new watchlist group, optionally pre-populated with securities. - x-description-zh: 创建新的自选股分组,可选择在创建时预填充证券。 - tags: - - Watchlist Management - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - name - properties: - name: - type: string - minLength: 1 - description: Group name. Must be at least 1 character and within the system name length limit. - securities: - type: array - nullable: true - description: Optional list of security symbols to pre-populate the group on creation (e.g. `["AAPL.US", "700.HK"]`). - items: - type: string + delete: + operationId: delete_sharelist + summary: Delete Sharelist + x-summary-zh: 删除股单 + x-summary-zh-hk: 刪除股單 + description: | + Permanently delete a sharelist you own. This action cannot be undone. + x-description-zh: | + 永久删除您创建的自选股列表,此操作不可撤销。 + x-description-zh-hk: | + 永久刪除您創建的自選股列表,此操作不可撤銷。 + x-subgroup: Sharelist + x-subgroup-zh: 股单 + x-subgroup-zh-hk: 股單 + tags: + - News & Contents + x-parameters: + - name: id + in: path + type: integer + required: true + description: Sharelist ID (path parameter) + x-description-zh: 股单 ID(路径参数) + x-description-zh-hk: 股單 ID(路徑參數) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge sharelist delete 15921 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request DELETE \ + --url 'https://openapi.longbridge.com/v1/sharelists/<id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.delete( + "https://openapi.longbridge.com/v1/sharelists/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.delete( + "https://openapi.longbridge.com/v1/sharelists/<id>", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/sharelists/<id>", { + method: "DELETE", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/sharelists/<id>")) + .header("Authorization", "Bearer <access_token>") + .method("DELETE", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::DELETE, "https://openapi.longbridge.com/v1/sharelists/<id>") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/sharelists/<id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"DELETE\", \"https://openapi.longbridge.com/v1/sharelists/<id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + message: success + data: {} + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/asset/account: + get: + operationId: account_balance + summary: Account Assets + x-summary-zh: 账户资金 + x-summary-zh-hk: 賬戶資金 + description: | + The API is used to obtain the available, desirable, frozen, to-be-settled, and in-transit + funds (fund purchase and redemption) information for each currency of the user. + x-description-zh: | + 该接口用于获取用户每个币种可用、可取、冻结、待结算金额、在途资金 (基金申购赎回) 信息。 + x-description-zh-hk: | + 該接口用於獲取用戶每個幣種可用、可取、凍結、待結算金額、在途資金 (基金申購贖回) 信息。 + x-subgroup: Assets + x-subgroup-zh: 资产 + x-subgroup-zh-hk: 資產 + tags: + - Trade + x-parameters: + - name: currency + in: query + type: string + required: false + description: Currency (HKD, USD, CNH) + x-description-zh: 币种(HKD、USD、CNH) + x-description-zh-hk: 幣種(HKD、USD、CNH) + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge assets + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/asset/account' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/asset/account", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/asset/account", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/asset/account", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/asset/account")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/asset/account") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/asset/account"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/asset/account\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Account Balance + x-description-zh: 账户资金信息 + x-description-zh-hk: 賬戶資金信息 + - name: └ total_cash + type: string + required: false + description: Total Cash + x-description-zh: 现金总额 + x-description-zh-hk: 現金總額 + - name: └ max_finance_amount + type: string + required: false + description: Maximum Financing Amount + x-description-zh: 最大融资金额 + x-description-zh-hk: 最大融資金額 + - name: └ remaining_finance_amount + type: string + required: false + description: Remaining Financing Amount + x-description-zh: 剩余融资金额 + x-description-zh-hk: 剩餘融資金額 + - name: └ risk_level + type: string + required: false + description: Risk control level. <b>Option:</b>, `0` - safe, `1` - medium risk, `2` - early warning, `3` - danger + x-description-zh: 风控等级。<b>可选值:</b>, `0` - 安全,`1` - 中风险,`2` - 预警,`3` - 危险 + x-description-zh-hk: 風控等級。<b>可選值:</b>, `0` - 安全,`1` - 中風險,`2` - 預警,`3` - 危險 + - name: └ margin_call + type: string + required: false + description: Margin Call + x-description-zh: 追缴保证金 + x-description-zh-hk: 追繳保證金 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 币种 + x-description-zh-hk: 幣種 + - name: └ cash_infos + type: object[] + required: false + description: Cash Details + x-description-zh: 现金详情 + x-description-zh-hk: 現金詳情 + - name: └ ∟ withdraw_cash + type: string + required: false + description: Withdraw Cash + x-description-zh: 可提现金 + x-description-zh-hk: 可提現金 + - name: └ ∟ available_cash + type: string + required: false + description: Available Cash + x-description-zh: 可用现金 + x-description-zh-hk: 可用現金 + - name: └ ∟ frozen_cash + type: string + required: false + description: Frozen Cash + x-description-zh: 冻结现金 + x-description-zh-hk: 凍結現金 + - name: └ ∟ settling_cash + type: string + required: false + description: Cash to be Settled + x-description-zh: 待结算现金 + x-description-zh-hk: 待結算現金 + - name: └ ∟ redemption_cash + type: string + required: false + description: Redeemable cash. + x-description-zh: 可赎回现金。 + x-description-zh-hk: 可贖回現金。 + - name: └ ∟ currency + type: string + required: false + description: Currency + x-description-zh: 币种 + x-description-zh-hk: 幣種 + - name: └ net_assets + type: string + required: false + description: net asset + x-description-zh: 净资产 + x-description-zh-hk: 淨資產 + - name: └ init_margin + type: string + required: false + description: initial margin + x-description-zh: 初始保证金 + x-description-zh-hk: 初始保證金 + - name: └ maintenance_margin + type: string + required: false + description: maintenance margin + x-description-zh: 维持保证金 + x-description-zh-hk: 維持保證金 + - name: └ buy_power + type: string + required: false + description: Buy Power + x-description-zh: 购买力 + x-description-zh-hk: 購買力 + - name: └ frozen_transaction_fees + type: array + required: false + description: frozen fees + x-description-zh: 冻结费用 + x-description-zh-hk: 凍結費用 + responses: + '200': + description: Successful response + content: + application/json: + example: + code: 0 + data: + list: + - total_cash: '1759070010.72' + max_finance_amount: '977582000' + remaining_finance_amount: '0' + risk_level: '1' + margin_call: '2598051051.50' + currency: HKD + net_assets: '24145.90' + init_margin: '1540.09' + maintenance_margin: '1540.09' + buy_power: '1759070.12' + cash_infos: + - withdraw_cash: '97592.30' + available_cash: '195902464.37' + frozen_cash: '11579339.13' + settling_cash: '207288537.81' + currency: HKD + - withdraw_cash: '199893416.74' + available_cash: '199893416.74' + frozen_cash: '28723.76' + settling_cash: '-276806.51' + currency: USD + frozen_transaction_fees: + - currency: USD + frozen_transaction_fee: '6.51' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /v1/asset/cashflow: + get: + operationId: cash_flow + summary: Cash Flow + x-summary-zh: 资金流水 + x-summary-zh-hk: 資金流水 + description: | + The API is used to obtain capital inflow/outflow direction, capital type, capital amount, occurrence time, + associated stock code and capital flow description information. + x-description-zh: | + 该接口用于获取资金流入/流出方向、资金类别、资金金额、发生时间、关联股票代码和资金流水说明信息。 + x-description-zh-hk: | + 該接口用於獲取資金流入/流出方向、資金類別、資金金額、發生時間、關聯股票代碼和資金流水說明信息。 + x-subgroup: Assets + x-subgroup-zh: 资产 + x-subgroup-zh-hk: 資產 + tags: + - Trade + x-parameters: + - name: start_time + in: query + type: string + required: true + description: start time timestamp, in `seconds`, E.g:`1650037563` + x-description-zh: 开始时间,时间戳,以 `秒` 为单位,例如:`1650037563` + x-description-zh-hk: 開始時間,時間戳,以 `秒` 爲單位,例如:`1650037563` + - name: end_time + in: query + type: string + required: true + description: end time timestamp, in `seconds`, E.g:`1650747581` + x-description-zh: 结束时间,时间戳,以 `秒` 为单位,例如:`1650747581` + x-description-zh-hk: 結束時間,時間戳,以 `秒` 爲單位,例如:`1650747581` + - name: business_type + in: query + type: string + required: false + description: Balance type. <b>Option:</b>, `1` - cash, `2` - stock, `3` - fund + x-description-zh: 资金类型。<b>可选值:</b>, `1` - 现金,`2` - 股票,`3` - 基金 + x-description-zh-hk: 資金類型。<b>可選值:</b>, `1` - 現金,`2` - 股票,`3` - 基金 + - name: symbol + in: query + type: string + required: false + description: Target code, E.g:`AAPL.US` + x-description-zh: 标的代码,例如:`AAPL.US` + x-description-zh-hk: 標的代碼,例如:`AAPL.US` + - name: page + in: query + type: string + required: false + description: start page. <b>Default value:</b> `1`, <b>Data validation rules:</b>, <b>Ranges:</b> `>=1` + x-description-zh: 起始页。<b>默认值:</b> `1`, <b>数据校验规则:</b>, <b>取值范围:</b> `>=1` + x-description-zh-hk: 起始頁。<b>默認值:</b> `1`, <b>數據校驗規則:</b>, <b>取值範圍:</b> `>=1` + - name: size + in: query + type: string + required: false + description: page size. <b>Default value:</b> `50`, <b>Data validation rules:</b> `1~10000` + x-description-zh: 每页大小。<b>默认值:</b> `50`, <b>数据校验规则:</b> `1~10000` + x-description-zh-hk: 每頁大小。<b>默認值:</b> `50`, <b>數據校驗規則:</b> `1~10000` x-codeSamples: - lang: Shell label: CLI source: | - longbridge watchlist create "NAME" - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: - id: '4303353' - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - put: - operationId: update_watchlist_group - summary: Update Watchlist - x-summary-zh: 更新自选股分组 - description: | - Update the name or member list of a watchlist group. Use `mode` to control how - `securities` are applied: `add` appends, `remove` removes, `replace` overwrites the entire list. - x-description-zh: 更新自选股分组的名称或成员列表。使用 `mode` 控制 `securities` 的操作方式:`add` 追加、`remove` 移除、`replace` 替换全部。 - tags: - - Watchlist Management - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: string - description: ID of the group to update (required). - name: - type: string - nullable: true - description: New group name. Omit to keep the existing name. - mode: - type: string - nullable: true - enum: - - add - - remove - - replace - description: | - Operation mode for the `securities` list. One of: - - `add` — append securities to the group - - `remove` — remove securities from the group - - `replace` — replace all securities in the group - securities: - type: array - nullable: true - description: List of security symbols affected by the operation (e.g. `["AAPL.US", "700.HK"]`). - items: - type: string - x-codeSamples: + longbridge cash-flow + x-request-examples: - lang: Shell - label: CLI + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/asset/cashflow?start_time=<start_time>&end_time=<end_time>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/asset/cashflow", + headers={"Authorization": "Bearer <access_token>"}, + params={"start_time": "<start_time>", "end_time": "<end_time>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/asset/cashflow", + headers={"Authorization": "Bearer <access_token>"}, + params={"start_time": "<start_time>", "end_time": "<end_time>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/asset/cashflow") + url.searchParams.set("start_time", "<start_time>") + url.searchParams.set("end_time", "<end_time>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/asset/cashflow?start_time=<start_time>&end_time=<end_time>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust source: | - longbridge watchlist update <ID> + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/asset/cashflow") + .header("Authorization", "Bearer <access_token>") + .query(&[("start_time", "<start_time>"), ("end_time", "<end_time>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/asset/cashflow?start_time=<start_time>&end_time=<end_time>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/asset/cashflow?start_time=<start_time>&end_time=<end_time>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Cash flow info + x-description-zh: 流水信息 + x-description-zh-hk: 流水信息 + - name: └ transaction_flow_name + type: string + required: true + description: Cash flow name + x-description-zh: 流水名称 + x-description-zh-hk: 流水名稱 + - name: └ direction + type: string + required: true + description: outflow direction. <b>Option:</b>, `1` - outflow, `2` - inflow + x-description-zh: 流出方向。<b>可选值:</b>, `1` - 流出,`2` - 流入 + x-description-zh-hk: 流出方向。<b>可選值:</b>, `1` - 流出,`2` - 流入 + - name: └ business_type + type: string + required: true + description: Funding Category. <b>Option:</b>, `1` - cash, `2` - stock, `3` - fund + x-description-zh: 资金类别。<b>可选值:</b>, `1` - 现金,`2` - 股票,`3` - 基金 + x-description-zh-hk: 資金類別。<b>可選值:</b>, `1` - 現金,`2` - 股票,`3` - 基金 + - name: └ balance + type: string + required: true + description: Cash amount + x-description-zh: 资金金额 + x-description-zh-hk: 資金金額 + - name: └ currency + type: string + required: true + description: Cash Currency + x-description-zh: 资金币种 + x-description-zh-hk: 資金幣種 + - name: └ business_time + type: string + required: true + description: business time + x-description-zh: 业务时间 + x-description-zh-hk: 業務時間 + - name: └ symbol + type: string + required: false + description: associated Stock code information + x-description-zh: 关联股票代码信息 + x-description-zh-hk: 關聯股票代碼信息 + - name: └ description + type: string + required: false + description: Cash flow description + x-description-zh: 资金流水说明 + x-description-zh-hk: 資金流水說明 responses: '200': description: Successful response @@ -651,43 +36785,207 @@ paths: application/json: example: code: 0 - message: success - data: {} + data: + list: + - transaction_flow_name: BuyContract-Stocks + direction: 1 + balance: '-248.60' + currency: USD + business_time: '1621507957' + symbol: AAPL.US + description: AAPL + - transaction_flow_name: BuyContract-Stocks + direction: 1 + balance: '-125.16' + currency: USD + business_time: '1621504824' + symbol: AAPL.US + description: AAPL default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - delete: - operationId: delete_watchlist_group - summary: Delete Watchlist - x-summary-zh: 删除自选股分组 + /v1/asset/fund: + get: + operationId: fund_positions + summary: Fund Positions + x-summary-zh: 基金持仓 + x-summary-zh-hk: 基金持倉 description: | - Delete the specified watchlist group. Set `purge=true` to also clear all securities - from the group before deletion. - x-description-zh: 删除指定的自选股分组。设置 `purge=true` 可在删除前清空分组中的所有证券。 + The API is used to obtain fund position information including account, fund code, holding share, cost net worth, + current net worth, and currency. + x-description-zh: | + 该接口用于获取包括账户、基金代码、持有份额、成本净值、当前净值、币种在内的基金持仓信息。 + x-description-zh-hk: | + 該接口用於獲取包括賬戶、基金代碼、持有份額、成本淨值、當前淨值、幣種在內的基金持倉信息。 + x-subgroup: Assets + x-subgroup-zh: 资产 + x-subgroup-zh-hk: 資產 tags: - - Watchlist Management - parameters: - - name: id - in: query - required: true - description: ID of the group to delete. - schema: - type: string - - name: purge + - Trade + x-parameters: + - name: symbol in: query + type: array required: false - description: If `true`, clears all securities from the group before deleting. Defaults to `false`. - schema: - type: boolean - nullable: true + description: Fund code, in `ISIN` format, E.g:`HK0000676327` <a href="https://en.wikipedia.org/wiki/International_Securities_Identification_Number">ISIN explain</a> + x-description-zh: 基金代码,使用 `ISIN` 格式,例如:`HK0000676327` <a href="https://en.wikipedia.org/wiki/International_Securities_Identification_Number">ISIN 解释</a> + x-description-zh-hk: 基金代碼,使用 `ISIN` 格式,例如:`HK0000676327` <a href="https://en.wikipedia.org/wiki/International_Securities_Identification_Number">ISIN 解釋</a> x-codeSamples: - lang: Shell label: CLI source: | - longbridge watchlist delete <ID> + longbridge fund-positions + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/asset/fund' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/asset/fund", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/asset/fund", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/asset/fund", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/asset/fund")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/asset/fund") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/asset/fund"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/asset/fund\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: stock holding information + x-description-zh: 股票持仓信息 + x-description-zh-hk: 股票持倉信息 + - name: └ account_channel + type: string + required: true + description: account type + x-description-zh: 账户类型 + x-description-zh-hk: 賬戶類型 + - name: └ fund_info + type: object[] + required: false + description: Fund Details + x-description-zh: 基金详情 + x-description-zh-hk: 基金詳情 + - name: └ ∟ symbol + type: string + required: true + description: Fund ISIN code + x-description-zh: 基金 ISIN 代码 + x-description-zh-hk: 基金 ISIN 代碼 + - name: └ ∟ current_net_asset_value + type: string + required: true + description: current Equity + x-description-zh: 当前净值 + x-description-zh-hk: 當前淨值 + - name: └ ∟ net_asset_value_day + type: string + required: true + description: current Equity time + x-description-zh: 当前净值时间 + x-description-zh-hk: 當前淨值時間 + - name: └ ∟ symbol_name + type: string + required: true + description: Fund name + x-description-zh: 基金名称 + x-description-zh-hk: 基金名稱 + - name: └ ∟ currency + type: string + required: true + description: Currency + x-description-zh: 币种 + x-description-zh-hk: 幣種 + - name: └ ∟ cost_net_asset_value + type: string + required: true + description: Net Cost + x-description-zh: 成本净值 + x-description-zh-hk: 成本淨值 + - name: └ ∟ holding_units + type: string + required: false + description: Number of fund units held. + x-description-zh: 持有基金份额。 + x-description-zh-hk: 持有基金份額。 responses: '200': description: Successful response @@ -695,46 +36993,208 @@ paths: application/json: example: code: 0 - message: success - data: {} + data: + list: + - account_channel: lb + fund_info: + - symbol: HK0000447943 + symbol_name: GAOTENG EMERGING MARKETS PLUS LONG/SHORT FIXED INCOME ALPHA FUND + currency: USD + holding_units: '5.000' + current_net_asset_value: '0' + cost_net_asset_value: '0.00' + net_asset_value_day: '1649865600' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/quote/get_security_list: + /v1/asset/stock: get: - operationId: list_securities - x-quote-command: security-list - summary: Query Tradable Securities List - x-summary-zh: 查询可交易证券列表 + operationId: stock_positions + summary: Stock Positions + x-summary-zh: 股票持仓 + x-summary-zh-hk: 股票持倉 description: | - Query the list of tradable securities filtered by market and category. Primarily used to - retrieve securities eligible for extended-hours (pre-market / after-hours) trading sessions. - Both `market` and `category` are required parameters. - x-description-zh: 按市场和类别筛选可交易证券列表,主要用于获取符合盘前/盘后延长交易时段条件的证券。`market` 和 `category` 均为必填参数。 + The API is used to obtain stock position information including account, stock code, number of shares held, + number of available shares, average position price (calculated according to account settings), and currency. + x-description-zh: | + 该接口用于获取包括账户、股票代码、持仓股数、可用股数、持仓均价(按账户设置计算均价方式)、币种在内的股票持仓信息。 + x-description-zh-hk: | + 該接口用於獲取包括賬戶、股票代碼、持倉股數、可用股數、持倉均價(按賬戶設置計算均價方式)、幣種在內的股票持倉信息。 + x-subgroup: Assets + x-subgroup-zh: 资产 + x-subgroup-zh-hk: 資產 tags: - - Watchlist Management - parameters: - - name: market - in: query - required: true - description: Market code. One of `US`, `HK`. - schema: - type: string - - name: category + - Trade + x-parameters: + - name: symbol in: query - required: true - description: Security category filter for the target trading session (e.g. `overnight` for US overnight-tradable securities). - schema: - type: string - nullable: true + type: array + required: false + description: Stock code, use `ticker.region` format, E.g:`AAPL.US` + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` x-codeSamples: - lang: Shell label: CLI source: | - longbridge security-list + longbridge positions + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/asset/stock' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/asset/stock", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/asset/stock", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/asset/stock", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/asset/stock")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/asset/stock") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/asset/stock"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/asset/stock\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Stock holding information + x-description-zh: 股票持仓信息 + x-description-zh-hk: 股票持倉信息 + - name: └ account_channel + type: string + required: false + description: Account type + x-description-zh: 账户类型 + x-description-zh-hk: 賬戶類型 + - name: └ stock_info + type: object[] + required: false + description: Stock list + x-description-zh: 股票列表 + x-description-zh-hk: 股票列表 + - name: └ ∟ symbol + type: string + required: false + description: Stock code + x-description-zh: 股票代码 + x-description-zh-hk: 股票代碼 + - name: └ ∟ symbol_name + type: string + required: false + description: Stock name + x-description-zh: 股票名称 + x-description-zh-hk: 股票名稱 + - name: └ ∟ currency + type: string + required: false + description: Currency + x-description-zh: 币种 + x-description-zh-hk: 幣種 + - name: └ ∟ quantity + type: string + required: false + description: The number of holdings + x-description-zh: 持仓股数 + x-description-zh-hk: 持倉股數 + - name: └ ∟ available_quantity + type: string + required: false + description: Available quantity + x-description-zh: 可用股数 + x-description-zh-hk: 可用股數 + - name: └ ∟ cost_price + type: string + required: false + description: Cost Price(According to the client's choice of average purchase or diluted cost) + x-description-zh: 成本价格 (具体根据客户端选择平均买入还是摊薄成本) + x-description-zh-hk: 成本價格 (具體根據客戶端選擇平均買入還是攤薄成本) + - name: └ ∟ market + type: string + required: false + description: market + x-description-zh: 市场 + x-description-zh-hk: 市場 + - name: └ ∟ init_quantity + type: string + required: false + description: Initial position before market opening + x-description-zh: 开盘前初始持仓 + x-description-zh-hk: 開盤前初始持倉 responses: '200': description: Successful response @@ -742,60 +37202,228 @@ paths: application/json: example: code: 0 - message: success data: list: - - symbol: AAPL.US - name_cn: 苹果 - name_hk: 蘋果 - name_en: Apple Inc. + - account_channel: lb + stock_info: + - symbol: 700.HK + symbol_name: TENCENT + currency: HKD + quantity: '650' + market: HK + available_quantity: '-450' + cost_price: '457.53' + init_quantity: '214' + - symbol: 9991.HK + symbol_name: BAOZUN-SW + currency: HKD + market: HK + quantity: '200' + available_quantity: '0' + cost_price: '32.25' + init_quantity: '214' + - symbol: TCEHY.US + symbol_name: Tencent (ADR) + currency: USD + market: US + quantity: '10' + available_quantity: '10' + init_quantity: '18' + - symbol: 2628.HK + symbol_name: CHINA LIFE + currency: HKD + market: HK + quantity: '9000' + available_quantity: '0' + init_quantity: '8000' + - symbol: 5.HK + symbol_name: HSBC HOLDINGS + currency: HKD + market: HK + quantity: '2400' + available_quantity: '2000' + init_quantity: '2000' + - symbol: BABA.US + symbol_name: Alibaba + currency: USD + market: US + quantity: '2000209' + available_quantity: '2000209' + init_quantity: '214' + - symbol: 2.HK + symbol_name: CLP HOLDINGS + currency: HKD + market: HK + quantity: '2000' + available_quantity: '2000' + init_quantity: '2000' + - symbol: NOK.US + symbol_name: Nokia + currency: USD + market: US + quantity: '1' + available_quantity: '0' + init_quantity: '1' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/quote/history_market_temperature: + /v1/asset/exchange_rates: get: - operationId: list_market_temperature - x-quote-command: market-temp - summary: Get Historical Market Temperature - x-summary-zh: 获取历史市场温度 + operationId: exchange_rate + summary: Exchange Rates + x-summary-zh: 汇率 + x-summary-zh-hk: 匯率 description: | - Get the historical market temperature time series for the specified market within a date range. - Each data point contains the daily temperature, valuation, and sentiment scores (all scored 0–100). - x-description-zh: 获取指定市场在日期范围内的历史市场温度时间序列,每个数据点包含当日情绪温度、估值和情绪分项评分(均为 0–100 分制)。 + Get current foreign exchange rates for all currency pairs used in your account. + x-description-zh: | + 获取账户中所有货币对的当前外汇汇率。 + x-description-zh-hk: | + 獲取賬戶中所有貨幣對的當前外匯匯率。 + x-subgroup: Portfolio + x-subgroup-zh: 投资组合 + x-subgroup-zh-hk: 投資組合 tags: - - Market Temperature - parameters: - - name: market - in: query - required: true - description: | - Market code. One of: - - `HK` — Hong Kong - - `US` — United States - - `CN` — A-shares - - `SG` — Singapore - schema: - type: string - - name: start_date - in: query - required: true - description: Start date in `YYYYMMDD` format (e.g. `20250101`). - schema: - type: string - - name: end_date + - Account + x-parameters: + - name: base in: query - required: true - description: End date in `YYYYMMDD` format (e.g. `20250110`). - schema: - type: string + type: string + required: false + description: Base currency, e.g. `USD`. Omit for all pairs. + x-description-zh: 基础货币,例如 `USD`,不传则返回所有货币对 + x-description-zh-hk: 基礎貨幣,例如 `USD`,不傳則返回所有貨幣對 x-codeSamples: - lang: Shell label: CLI source: | - longbridge market-temp [MARKET] --history --start YYYY-MM-DD --end YYYY-MM-DD + longbridge exchange-rate + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/asset/exchange_rates' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/asset/exchange_rates", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/asset/exchange_rates", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/asset/exchange_rates", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/asset/exchange_rates")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/asset/exchange_rates") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/asset/exchange_rates"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/asset/exchange_rates\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: exchanges + type: object[] + required: false + description: List of exchange rates + x-description-zh: 汇率列表, + x-description-zh-hk: 匯率列表, + - name: └ base_currency + type: string + required: false + description: Base currency + x-description-zh: 基准货币 + x-description-zh-hk: 基準貨幣 + - name: └ other_currency + type: string + required: false + description: Quote currency + x-description-zh: 报价货币 + x-description-zh-hk: 報價貨幣 + - name: └ bid_rate + type: integer + required: false + description: Bid rate + x-description-zh: 买入汇率 + x-description-zh-hk: 買入匯率 + - name: └ offer_rate + type: integer + required: false + description: Offer rate + x-description-zh: 卖出汇率 + x-description-zh-hk: 賣出匯率 + - name: └ average_rate + type: integer + required: false + description: Average exchange rate + x-description-zh: 平均汇率 + x-description-zh-hk: 平均匯率 responses: '200': description: Successful response @@ -805,51 +37433,203 @@ paths: code: 0 message: success data: - list: - - timestamp: '1735794000' - temperature: 58 - valuation: 54 - sentiment: 61 - - timestamp: '1735880400' - temperature: 59 - valuation: 56 - sentiment: 63 - type: day + exchanges: + - base_currency: USD + other_currency: HKD + bid_rate: 7.785 + offer_rate: 7.795 + average_rate: 7.79 default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/quote/market_temperature: + /v1/risk/margin-ratio: get: - operationId: market_temperature - x-quote-command: market-temp - summary: Get Current Market Temperature - x-summary-zh: 获取当前市场情绪 - description: | - Get the current sentiment temperature snapshot for the specified market. - Scores range from 0–100; a higher value indicates a more bullish market. - x-description-zh: 获取指定市场的当前情绪温度快照。评分范围 0–100,数值越高表示市场越乐观。 + operationId: margin_ratio + summary: Margin Ratio + x-summary-zh: 保证金比例 + x-summary-zh-hk: 保證金比例 + description: | + This API is used to obtain the initial margin ratio, maintain the margin ratio and strengthen the + margin ratio of stocks. + x-description-zh: | + 该接口用于获取股票初始保证金比例、维持保证金比例、强平保证金比例。 + x-description-zh-hk: | + 該接口用於獲取股票初始保證金比例、維持保證金比例、強平保證金比例。 + x-subgroup: Assets + x-subgroup-zh: 资产 + x-subgroup-zh-hk: 資產 tags: - - Market Temperature - parameters: - - name: market + - Trade + x-parameters: + - name: symbol in: query + type: string required: true - description: | - Market code. One of: - - `HK` — Hong Kong - - `US` — United States - - `CN` — A-shares - - `SG` — Singapore - schema: - type: string + description: 'Stock symbol, using the format `ticker.region`, for example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` x-codeSamples: - lang: Shell label: CLI source: | - longbridge market-temp [MARKET] + longbridge margin-ratio TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/risk/margin-ratio?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/risk/margin-ratio", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/risk/margin-ratio", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/risk/margin-ratio") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/risk/margin-ratio?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/risk/margin-ratio") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/risk/margin-ratio?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/risk/margin-ratio?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: im_factor + type: string + required: false + description: Initial margin ratio + x-description-zh: 初始保证金比例 + x-description-zh-hk: 初始保證金比例 + - name: mm_factor + type: string + required: false + description: Maintain the initial margin ratio + x-description-zh: 维持保证金比例 + x-description-zh-hk: 維持保證金比例 + - name: fm_factor + type: string + required: false + description: Forced close-out margin ratio + x-description-zh: 强平保证金比例 + x-description-zh-hk: 強平保證金比例 + - name: mcm_factor + type: string + required: false + description: Maintenance-call-margin factor. + x-description-zh: 维持追缴保证金系数。 + x-description-zh-hk: 維持追繳保證金系數。 + - name: short_im_factor + type: string + required: false + description: Short initial-margin factor. + x-description-zh: 沽空初始保证金系数。 + x-description-zh-hk: 沽空初始保證金系數。 + - name: short_mm_factor + type: string + required: false + description: Short maintenance-margin factor. + x-description-zh: 沽空维持保证金系数。 + x-description-zh-hk: 沽空維持保證金系數。 + - name: short_fm_factor + type: string + required: false + description: Short force-margin factor. + x-description-zh: 沽空强平保证金系数。 + x-description-zh-hk: 沽空強平保證金系數。 + - name: short_mcm_factor + type: string + required: false + description: Short maintenance-call-margin factor. + x-description-zh: 沽空维持追缴保证金系数。 + x-description-zh-hk: 沽空維持追繳保證金系數。 + - name: short_sellable + type: string + required: false + description: Whether the security is short-sellable. + x-description-zh: 是否可沽空。 + x-description-zh-hk: 是否可沽空。 + - name: short_sellable_v2 + type: boolean + required: false + description: Short-sellable status (v2). + x-description-zh: 可沽空状态(v2)。 + x-description-zh-hk: 可沽空狀態(v2)。 responses: '200': description: Successful response @@ -857,192 +37637,787 @@ paths: application/json: example: code: 0 - message: success data: - temperature: 70 - description: 温度温暖并快速上升中 - valuation: 59 - sentiment: 82 - updated_at: '1774317902' + im_factor: '0.1' + mm_factor: '0.1' + fm_factor: '0.1' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/asset/cashflow: + /v1/us/assets/overview: get: - operationId: list_cash_flow - summary: Cash Flow - x-summary-zh: 资金流水查询 + operationId: us_asset_overview + summary: US Asset Overview + x-summary-zh: 美股资产概览 + x-summary-zh-hk: 美股資產概覽 description: | - Query account cash flow history. Covers deposit, withdrawal, dividends, settlement, - and other business types. Supports time range and pagination. - x-description-zh: 查询账户资金流水历史,涵盖入金、出金、分红、结算等业务类型,支持时间范围筛选和分页。 + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get an overview of US account assets — buying power, cash, stocks, options, and crypto. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股账户资产概览——买入力、现金、股票、期权和加密货币。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股賬戶資產概覽——買入力、現金、股票、期權和加密貨幣。 + x-subgroup: Assets + x-subgroup-zh: 资产 + x-subgroup-zh-hk: 資產 tags: - - Portfolio & Cash - parameters: - - name: start_time - in: query - required: false - description: Query range start time as a Unix timestamp (seconds). - schema: - type: integer - nullable: true - - name: end_time - in: query - required: false - description: Query range end time as a Unix timestamp (seconds). - schema: - type: integer - nullable: true - - name: business_type - in: query - required: false - description: Business type filter (integer). Omit to return all types. - schema: - type: integer - nullable: true - - name: symbol - in: query - required: false - description: Filter by security symbol (e.g. `AAPL.US`). Supports multiple values. - schema: - type: array - nullable: true - items: - type: string - - name: page - in: query - required: false - description: Page number (1-based). Defaults to `1`. - schema: - type: integer - nullable: true - - name: size - in: query - required: false - description: Number of records per page. Defaults to `20`. - schema: - type: integer - nullable: true + - Trade x-codeSamples: - lang: Shell label: CLI source: | - longbridge cash-flow + # US account asset overview + longbridge positions + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/assets/overview' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/assets/overview", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/assets/overview", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/us/assets/overview", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/assets/overview")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/assets/overview") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/assets/overview"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/assets/overview\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: account_type + type: string + required: true + description: Account type identifier + x-description-zh: 账户类型标识 + x-description-zh-hk: 賬戶類型標識 + - name: asset_timestamp + type: string + required: true + description: Snapshot time (Unix seconds) + x-description-zh: 资产数据快照时间(Unix 秒) + x-description-zh-hk: 資產數據快照時間(Unix 秒) + - name: cash_buy_power + type: string + required: true + description: Available buying power (cash) + x-description-zh: 可用买入力(现金) + x-description-zh-hk: 可用買入力(現金) + - name: overnight_buy_power + type: string + required: true + description: Overnight buying power + x-description-zh: 隔夜买入力 + x-description-zh-hk: 隔夜買入力 + - name: currency + type: string + required: true + description: Base currency + x-description-zh: 基础货币 + x-description-zh-hk: 基礎貨幣 + - name: cash_list + type: USCashEntry[] + required: false + description: Cash balances by currency + x-description-zh: 按货币分列的现金余额 + x-description-zh-hk: 按貨幣分列的現金餘額 + - name: stock_list + type: USStockEntry[] + required: false + description: Stock positions + x-description-zh: 股票持仓 + x-description-zh-hk: 股票持倉 + - name: option_list + type: object[] + required: false + description: Option positions + x-description-zh: 期权持仓 + x-description-zh-hk: 期權持倉 + - name: crypto_list + type: USCryptoEntry[] + required: false + description: Crypto positions + x-description-zh: 加密货币持仓 + x-description-zh-hk: 加密貨幣持倉 responses: '200': description: Successful response content: application/json: example: - code: 0 - message: success - data: - list: - - transaction_flow_name: 在途分红 - direction: 1 - business_type: 0 - balance: '13.65' - currency: USD - business_time: '1771826509' - symbol: MSFT.US - description: 'MSFT.US Cash Dividend: 0.91 USD per share(in transit)' + account_type: US + asset_timestamp: 1751866334 + cash_buy_power: '12500.00' + overnight_buy_power: '10000.00' + currency: USD + cash_list: + - currency: USD + total_cash: '12500.00' + settled_cash: '12000.00' + total_amount: '12500.00' + outstanding: '500.00' + frozen_buy_cash: '0.00' + stock_list: + - symbol: AAPL.US + quantity: '10' + currency: USD + average_cost: '180.00' + last_done: '185.00' + prev_close: '183.00' + asset_type: stock + trade_status: Normal + crypto_list: + - symbol: BTCUSD.BKKT + average_cost: '50000.00' + currency: USD + asset_type: crypto + industry_name: Cryptocurrency default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/asset/account: + /v1/us/assets/pl/realized: get: - operationId: account_cash - summary: Account Cash - x-summary-zh: 账户现金 + operationId: us_realized_pl + summary: US Realized P&L + x-summary-zh: 美股已实现盈亏 + x-summary-zh-hk: 美股已實現盈虧 description: | - Query account cash balance, buying power, margin details, and per-currency cash breakdown. - x-description-zh: 查询账户现金余额、购买力、保证金详情及各币种资金明细。 + :::warning Longbridge US Accounts + This method is only available for US data-center accounts. + ::: + + Get realized profit and loss for a US account, broken down by asset category. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于美国数据中心账户。 + ::: + + 获取美股账户已实现盈亏,按资产类别(股票/期权/加密货币)分组。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於美國數據中心賬戶。 + ::: + + 獲取美股賬戶已實現盈虧,按資產類別(股票/期權/加密貨幣)分組。 + x-subgroup: Assets + x-subgroup-zh: 资产 + x-subgroup-zh-hk: 資產 tags: - - Portfolio & Cash - parameters: + - Trade + x-parameters: - name: currency in: query + type: string + required: true + description: Settlement currency, e.g. `USD` + x-description-zh: 结算货币,例如 `USD` + x-description-zh-hk: 結算貨幣,例如 `USD` + - name: category + in: query + type: string required: false - description: Filter by currency code (e.g. `USD`, `HKD`). Omit to return all currencies. - schema: - type: string - nullable: true + description: 'Asset category: `ALL` \' + x-description-zh: 资产类别:`ALL` \ + x-description-zh-hk: 資產類別:`ALL` \ x-codeSamples: - lang: Shell label: CLI source: | - longbridge balance + # US realized P&L + longbridge profit-analysis realized + # Filter by stock + longbridge profit-analysis realized --category stock + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/assets/pl/realized?currency=<currency>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/assets/pl/realized", + headers={"Authorization": "Bearer <access_token>"}, + params={"currency": "<currency>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/assets/pl/realized", + headers={"Authorization": "Bearer <access_token>"}, + params={"currency": "<currency>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/us/assets/pl/realized") + url.searchParams.set("currency", "<currency>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/assets/pl/realized?currency=<currency>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/assets/pl/realized") + .header("Authorization", "Bearer <access_token>") + .query(&[("currency", "<currency>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/assets/pl/realized?currency=<currency>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/assets/pl/realized?currency=<currency>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: realized_pl_list + type: USRealizedPLEntry[] + required: true + description: P&L breakdown by asset category + x-description-zh: 按资产类别分列的盈亏明细 + x-description-zh-hk: 按資產類別分列的盈虧明細 + - name: category + type: int + required: true + description: 'Asset category: `1`=stock, `2`=option, `3`=crypto' + x-description-zh: 资产类别:`1`=股票,`2`=期权,`3`=加密货币 + x-description-zh-hk: 資產類別:`1`=股票,`2`=期權,`3`=加密貨幣 + - name: currency + type: string + required: true + description: Currency code (e.g. `USD`) + x-description-zh: 货币代码,如 `USD` + x-description-zh-hk: 貨幣代碼,如 `USD` + - name: metrics + type: USRealizedPLMetric[] + required: true + description: P&L metrics by time period + x-description-zh: 按时期分列的盈亏指标 + x-description-zh-hk: 按時期分列的盈虧指標 + - name: amount + type: string + required: true + description: Realized P&L amount + x-description-zh: 已实现盈亏金额 + x-description-zh-hk: 已實現盈虧金額 + - name: period + type: int + required: true + description: Time period + x-description-zh: 时间周期 + x-description-zh-hk: 時間週期 + - name: rate + type: string + required: true + description: Return rate (%) + x-description-zh: 收益率(%) + x-description-zh-hk: 收益率(%) responses: '200': description: Successful response content: application/json: example: - code: 0 - message: success - data: - list: - - total_cash: '456943.18' - max_finance_amount: '3200000.00' - remaining_finance_amount: '3654289.90' - risk_level: '0' - margin_call: '0' - currency: HKD - net_assets: '962678.11' - init_margin: '141090.88' - maintenance_margin: '123141.91' - buy_power: '821587.22' - frozen_transaction_fees: [] - cash_infos: - - currency: USD - withdraw_cash: '-38665.68' - available_cash: '-38665.68' - frozen_cash: '332.19' - settling_cash: '-10108.02' - redemption_cash: '0.00' - - currency: HKD - withdraw_cash: '755592.21' - available_cash: '755592.21' - frozen_cash: '64.69' - settling_cash: '-27760.00' - redemption_cash: '0.00' + realized_pl_list: + - category: 1 + currency: USD + metrics: + - amount: '1250.50' + period: 1 + rate: '0.0312' + - category: 3 + currency: USD + metrics: + - amount: '-85.20' + period: 1 + rate: '-0.0215' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/asset/stock: + /v1/portfolio/profit-analysis-summary: get: - operationId: list_stock_positions - summary: Stock Position - x-summary-zh: 股票持仓 + operationId: profit_analysis_summary + summary: Profit Analysis Summary + x-summary-zh: 盈亏分析汇总 + x-summary-zh-hk: 盈虧分析匯總 description: | - Query all stock (equity) positions, grouped by sub-account channel. - x-description-zh: 查询所有股票(权益类)持仓,按子账户渠道分组返回。 + Get a P&L summary for the account including total asset, total P&L, and yield metrics. + x-description-zh: | + 获取账户盈亏汇总,包含总资产、总盈亏和收益率指标。 + x-description-zh-hk: | + 獲取賬戶盈虧匯總,包含總資產、總盈虧和收益率指標。 + x-subgroup: Portfolio + x-subgroup-zh: 投资组合 + x-subgroup-zh-hk: 投資組合 tags: - - Portfolio & Cash - parameters: - - name: symbol + - Account + x-parameters: + - name: start_date + in: query + type: string + required: false + description: Analysis start date in `YYYY-MM-DD` format + x-description-zh: 分析开始日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 分析開始日期,格式 `YYYY-MM-DD` + - name: end_date in: query + type: string required: false - description: Filter by security symbol (e.g. `AAPL.US`). Supports multiple values. Omit to return all positions. - schema: - type: array - nullable: true - items: - type: string + description: Analysis end date in `YYYY-MM-DD` format + x-description-zh: 分析结束日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 分析結束日期,格式 `YYYY-MM-DD` x-codeSamples: - lang: Shell label: CLI source: | - longbridge positions + longbridge profit-analysis + longbridge profit-analysis --start 2026-01-01 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/portfolio/profit-analysis-summary\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: updated_at + type: string + required: false + description: Last update timestamp + x-description-zh: 最后更新时间戳 + x-description-zh-hk: 最後更新時間戳 + - name: sum_profit + type: string + required: false + description: Total profit/loss + x-description-zh: 总盈亏 + x-description-zh-hk: 總盈虧 + - name: sum_profit_rate + type: string + required: false + description: Total profit/loss rate + x-description-zh: 总盈亏率 + x-description-zh-hk: 總盈虧率 + - name: profits + type: object + required: false + description: Profit breakdown by category + x-description-zh: 按类型分解的盈亏 + x-description-zh-hk: 按類型分解的盈虧 + - name: └ stock + type: string + required: false + description: Basic stock information + x-description-zh: 股票基本信息 + x-description-zh-hk: 股票基本信息 + - name: └ fund + type: string + required: false + description: Fund flag/amount. + x-description-zh: 资金标志/金额。 + x-description-zh-hk: 資金標誌/金額。 + - name: └ mmf + type: string + required: false + description: Maintenance-margin factor. + x-description-zh: 维持保证金系数。 + x-description-zh-hk: 維持保證金系數。 + - name: └ other + type: string + required: false + description: Other value/count. + x-description-zh: 其他值/数量。 + x-description-zh-hk: 其他值/數量。 + - name: └ summary_info + type: object[] + required: false + description: Summary information. + x-description-zh: 摘要信息。 + x-description-zh-hk: 摘要信息。 + - name: └ ∟ profit_max + type: string + required: false + description: Maximum profit amount. + x-description-zh: 最大盈利金额。 + x-description-zh-hk: 最大盈利金額。 + - name: └ ∟ loss_max + type: string + required: false + description: Maximum loss amount. + x-description-zh: 最大亏损金额。 + x-description-zh-hk: 最大虧損金額。 + - name: └ ∟ profit_max_name + type: string + required: false + description: Name of the max-profit security. + x-description-zh: 最大盈利标的名称。 + x-description-zh-hk: 最大盈利標的名稱。 + - name: └ ∟ loss_max_name + type: string + required: false + description: Name of the max-loss security. + x-description-zh: 最大亏损标的名称。 + x-description-zh-hk: 最大虧損標的名稱。 + - name: └ ∟ asset_type + type: string + required: false + description: Element type of this group (holdings / regional / asset class / industry) + x-description-zh: 本组的元素类型(持仓 / 地区 / 资产类别 / 行业) + x-description-zh-hk: 本組的元素類型(持倉 / 地區 / 資產類別 / 行業) + - name: └ ipo_subscription + type: integer + required: false + description: IPO subscription count. + x-description-zh: IPO 认购次数。 + x-description-zh-hk: IPO 認購次數。 + - name: └ ipo_hit + type: integer + required: false + description: IPO allotment (hit) count. + x-description-zh: IPO 中签次数。 + x-description-zh-hk: IPO 中籤次數。 + - name: └ cumulative_transaction_amount + type: string + required: false + description: Cumulative transaction amount. + x-description-zh: 累计成交额。 + x-description-zh-hk: 累計成交額。 + - name: └ crypto + type: string + required: false + description: Crypto flag. + x-description-zh: 加密货币标志。 + x-description-zh-hk: 加密貨幣標誌。 + - name: └ trade_order_num + type: string + required: false + description: Number of trade orders. + x-description-zh: 交易订单数。 + x-description-zh-hk: 交易訂單數。 + - name: └ trade_stock_num + type: string + required: false + description: Number of traded stocks. + x-description-zh: 交易标的数。 + x-description-zh-hk: 交易標的數。 + - name: is_traded + type: boolean + required: false + description: Whether any trades exist + x-description-zh: 是否有交易记录 + x-description-zh-hk: 是否有交易紀錄 + - name: currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: total_simple_earning_yield + type: string + required: false + description: Total simple earning yield. + x-description-zh: 总简单收益率。 + x-description-zh-hk: 總簡單收益率。 + - name: total_time_earning_yield + type: string + required: false + description: Total time-weighted earning yield. + x-description-zh: 总时间加权收益率。 + x-description-zh-hk: 總時間加權收益率。 + - name: invest_amount + type: string + required: false + description: Total invested amount + x-description-zh: 总投入金额 + x-description-zh-hk: 總投入金額 + - name: initial_asset_value + type: string + required: false + description: Initial asset value + x-description-zh: 初始资产价值 + x-description-zh-hk: 初始資產價值 + - name: ending_asset_value + type: string + required: false + description: Ending asset value + x-description-zh: 期末资产价值 + x-description-zh-hk: 期末資產價值 + - name: current_total_asset + type: string + required: false + description: Current total asset value + x-description-zh: 当前总资产 + x-description-zh-hk: 當前總資產 + - name: start_time + type: string + required: false + description: Period start timestamp + x-description-zh: 统计开始时间戳 + x-description-zh-hk: 統計開始時間戳 + - name: end_time + type: string + required: false + description: Period end timestamp + x-description-zh: 统计结束时间戳 + x-description-zh-hk: 統計結束時間戳 + - name: trade_update_time + type: string + required: false + description: Trade data update time. + x-description-zh: 交易数据更新时间。 + x-description-zh-hk: 交易數據更新時間。 + - name: updated_date + type: string + required: false + description: Last update date + x-description-zh: 最后更新日期 + x-description-zh-hk: 最後更新日期 + - name: start_date + type: string + required: false + description: Period start date + x-description-zh: 统计开始日期 + x-description-zh-hk: 統計開始日期 + - name: end_date + type: string + required: false + description: Period end date + x-description-zh: 统计结束日期 + x-description-zh-hk: 統計結束日期 + - name: trade_update_date + type: string + required: false + description: Trade data update date. + x-description-zh: 交易数据更新日期。 + x-description-zh-hk: 交易數據更新日期。 + - name: trade_stock_num + type: string + required: false + description: Number of traded stocks. + x-description-zh: 交易标的数。 + x-description-zh-hk: 交易標的數。 responses: '200': description: Successful response @@ -1052,56 +38427,224 @@ paths: code: 0 message: success data: - list: - - account_channel: lb_papertrading - stock_info: - - symbol: NVDA.US - symbol_name: 英伟达 - currency: USD - quantity: '101' - available_quantity: '101' - cost_price: '50.229' - market: US - init_quantity: '101' - - symbol: AAPL.US - symbol_name: 苹果 - currency: USD - quantity: '133' - available_quantity: '133' - cost_price: '211.589' - market: US - init_quantity: '133' + summary: + currency: USD + sum_profit: '62905.97' + sum_profit_rate: '0.6128' + invest_amount: '102659.74' + current_total_asset: '165565.71' + initial_asset_value: '0.00' + ending_asset_value: '165565.71' + is_traded: true + start_date: '2025-10-17' + start_time: '1760659200' + end_date: '2026-05-14' + end_time: '1778731947' + profits: + stock: '66370.84' + crypto: '0' + fund: null + ipo: null + mmf: null + other: null + cumulative_transaction_amount: '1244920.28' + sublist: + start: '2025-10-17' + start_date: '2025-10-17' + end: '2026-05-14' + end_date: '2026-05-14' + updated_at: '1778731947' + updated_date: '2026-05-14' + items: + - symbol: AAPL.US + name: Apple + market: US + currency: USD + profit: '100.00' + profit_rate: '0.05' + holding_period: '180' + clearance_times: 0 + is_holding: true + item_type: Stock + isin: '' + security_code: AAPL + underlying_profit: '100.00' + derivatives_profit: '0.00' + order_profit: null default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/asset/fund: + /v1/portfolio/profit-analysis/by-market: get: - operationId: list_fund_positions - summary: Fund Position - x-summary-zh: 基金持仓 + operationId: profit_analysis_by_market + summary: Profit Analysis by Market + x-summary-zh: 按市场盈亏分析 + x-summary-zh-hk: 按市場盈虧分析 description: | - Query all public fund positions, grouped by sub-account channel. - x-description-zh: 查询所有公募基金持仓,按子账户渠道分组返回。 + Get P&L breakdown grouped by market (US, HK, CN, SG). + x-description-zh: | + 获取按市场分组的盈亏分析(美股、港股、A 股、新加坡股)。 + x-description-zh-hk: | + 獲取按市場分組的盈虧分析(美股、港股、A 股、新加坡股)。 + x-subgroup: Portfolio + x-subgroup-zh: 投资组合 + x-subgroup-zh-hk: 投資組合 tags: - - Portfolio & Cash - parameters: - - name: symbol + - Account + x-parameters: + - name: start_date in: query + type: string + required: false + description: Analysis start date in `YYYY-MM-DD` format + x-description-zh: 分析开始日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 分析開始日期,格式 `YYYY-MM-DD` + - name: end_date + in: query + type: string required: false - description: Filter by fund symbol. Supports multiple values. Omit to return all fund positions. - schema: - type: array - nullable: true - items: - type: string + description: Analysis end date in `YYYY-MM-DD` format + x-description-zh: 分析结束日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 分析結束日期,格式 `YYYY-MM-DD` x-codeSamples: - lang: Shell label: CLI source: | - longbridge fund-positions + longbridge profit-analysis --format json + longbridge profit-analysis --start 2026-01-01 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/portfolio/profit-analysis/by-market\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: has_more + type: boolean + required: false + description: Whether there are more pages + x-description-zh: 是否有更多页 + x-description-zh-hk: 是否有更多頁 + - name: profit + type: string + required: false + description: Total profit/loss + x-description-zh: 总盈亏 + x-description-zh-hk: 總盈虧 + - name: stock_items + type: object[] + required: false + description: P&L breakdown by stock + x-description-zh: 按股票划分的盈亏明细 + x-description-zh-hk: 按股票劃分的盈虧明細 + - name: └ code + type: string + required: false + description: Stock code + x-description-zh: 股票代码 + x-description-zh-hk: 股票代碼 + - name: └ market + type: string + required: false + description: Market code + x-description-zh: 市场代码 + x-description-zh-hk: 市場代碼 + - name: └ name + type: string + required: false + description: Stock name + x-description-zh: 股票名称 + x-description-zh-hk: 股票名稱 + - name: └ profit + type: string + required: false + description: Profit/loss for this stock + x-description-zh: 该股票盈亏 + x-description-zh-hk: 該股票盈虧 responses: '200': description: Successful response @@ -1111,202 +38654,374 @@ paths: code: 0 message: success data: - list: - - fund_info: - - symbol: HK0000676533 - symbol_name: 某只基金 - holding_units: '1000.00' - current_net_asset_value: '1.2345' - cost_net_asset_value: '1.1000' - net_asset_value_day: '1774310400' - currency: HKD + has_more: false + profit: '-16325.26' + stock_items: + - code: AAPL + market: US + name: Apple + profit: '100.00' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/statement/list: + /v1/portfolio/profit-analysis/detail: get: - operationId: list_statements - summary: List Statements - x-summary-zh: 查询结单列表 + operationId: profit_analysis_detail + summary: Profit Analysis Detail + x-summary-zh: 盈亏分析明细 + x-summary-zh-hk: 盈虧分析明細 description: | - Query available account statements (daily or monthly). Returns a list of statement - dates and file keys that can be used with the download endpoint. - x-description-zh: 查询可用的账户结单(日结单或月结单),返回结单日期和文件标识列表,可用于下载接口。 + Get detailed P&L for a specific security including transaction flow and cost breakdown. + x-description-zh: | + 获取指定证券的详细盈亏分析,包含交易流水和成本分解。 + x-description-zh-hk: | + 獲取指定證券的詳細盈虧分析,包含交易流水和成本分解。 + x-subgroup: Portfolio + x-subgroup-zh: 投资组合 + x-subgroup-zh-hk: 投資組合 tags: - - Statement - parameters: - - name: statement_type + - Account + x-parameters: + - name: symbol in: query - required: false - description: 'Statement type: 1 = daily (default), 2 = monthly.' - schema: - type: integer - enum: [1, 2] - default: 1 - - name: page + type: string + required: true + description: Security symbol, e.g. `AAPL.US` + x-description-zh: 证券代码,例如 `AAPL.US` + x-description-zh-hk: 證券代碼,例如 `AAPL.US` + - name: start in: query + type: string required: false - description: Page number for pagination. - schema: - type: integer - - name: page_size + description: Start date, `YYYY-MM-DD` + x-description-zh: 开始日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 開始日期,格式 `YYYY-MM-DD` + - name: end in: query + type: string required: false - description: Number of results per page. - schema: - type: integer + description: End date, `YYYY-MM-DD` + x-description-zh: 结束日期,格式 `YYYY-MM-DD` + x-description-zh-hk: 結束日期,格式 `YYYY-MM-DD` x-codeSamples: - lang: Shell label: CLI source: | - longbridge statement list - longbridge statement list --type monthly - longbridge statement list --start-date 20260101 --limit 10 - responses: - '200': - description: Statement list - content: - application/json: - schema: - type: array - items: - type: object - properties: - date: - type: string - description: Statement date (string, e.g. "20260327"). - file_key: - type: string - description: File key used to request the download URL. - example: - - date: '20260327' - file_key: '/statement_data/data/lb/1/20260327/10000104.json' - - date: '20260324' - file_key: '/statement_data/data/lb/1/20260324/10000104.json' - - date: '20260323' - file_key: '/statement_data/data/lb/1/20260323/10000104.json' - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/statement/download: - get: - operationId: get_statement_download_url - summary: Get Statement Download URL - x-summary-zh: 获取结单下载地址 - description: | - Get a presigned download URL for a specific statement file. The URL returns a JSON - document containing the full statement content with all sections. - x-description-zh: 获取指定结单文件的预签名下载地址,返回包含结单全部板块的 JSON 文档。 - tags: - - Statement - parameters: - - name: file_key - in: query - required: true - description: File key obtained from the list statements endpoint. - schema: - type: string - x-codeSamples: + longbridge profit-analysis detail TSLA.US + longbridge profit-analysis detail AAPL.US + x-request-examples: - lang: Shell - label: CLI + label: cURL source: | - longbridge statement export --file-key abc123xyz456 --section equity_holdings - longbridge statement export --file-key abc123xyz456 --section stock_trades -o trades.csv - longbridge statement export --file-key abc123xyz456 --all -o ./report/ - responses: - '200': - description: Download URL - content: - application/json: - schema: - type: object - properties: - url: - type: string - description: Presigned URL to download the statement JSON. - example: - url: 'https://storage.example.com/statements/abc123xyz456.json?X-Amz-Signature=...' - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/quote/filings: - get: - operationId: list_filings - x-quote-command: filings - summary: Get Filings by Symbol - x-summary-zh: 获取标的监管文件 - description: | - Get the list of regulatory filings or disclosure documents for the specified symbol. - Each filing includes a title, file name, download URLs, and publication timestamp. - x-description-zh: 获取指定标的的监管文件或信息披露文件列表,每条记录包含标题、文件名、下载链接和发布时间戳。 - tags: - - News & Filings - parameters: - - name: symbol - in: query - required: true - description: 'Security symbol to query filings for (e.g. `AAPL.US`, `700.HK`).' - schema: - type: string - x-codeSamples: - - lang: Shell - label: CLI + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python source: | - longbridge filing list <SYMBOL> - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: - items: - - id: '627391979864985729' - title: 苹果 | 4 - Apple Inc. (0000320193) (Issuer) - description: '' - file_name: 4 - Apple Inc. (0000320193) (Issuer) - file_urls: - - https://www.sec.gov/Archives/edgar/data/320193/.../wk-form4_1773786674.xml - publish_at: '1773786677' - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/content/{symbol}/news: - get: - operationId: list_news - summary: Get News by Symbol - x-summary-zh: 获取标的新闻 - description: | - Get the latest news articles for the specified symbol. - x-description-zh: 获取指定标的的最新新闻资讯列表。 - tags: - - News & Filings - parameters: - - name: symbol - in: path - required: true - description: 'Security symbol to query news for (e.g. `AAPL.US`, `700.HK`).' - schema: - type: string - x-codeSamples: - - lang: Shell - label: CLI + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js source: | - longbridge news <SYMBOL> + const url = new URL("https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/portfolio/profit-analysis/detail?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: profit + type: string + required: false + description: Total profit/loss + x-description-zh: 总盈亏 + x-description-zh-hk: 總盈虧 + - name: underlying_details + type: object + required: false + description: Underlying asset P&L breakdown + x-description-zh: 正股盈亏明细 + x-description-zh-hk: 正股盈虧明細 + - name: └ holding_value + type: string + required: false + description: Current holding value + x-description-zh: 当前持仓市值 + x-description-zh-hk: 當前持倉市值 + - name: └ profit + type: string + required: false + description: Total profit/loss + x-description-zh: 总盈亏 + x-description-zh-hk: 總盈虧 + - name: └ cumulative_credited_amount + type: string + required: false + description: Cumulative credited amount + x-description-zh: 累计入账金额 + x-description-zh-hk: 累計入賬金額 + - name: └ credited_details + type: array + required: false + description: Credit transaction details + x-description-zh: 入账明细 + x-description-zh-hk: 入賬明細 + - name: └ cumulative_debited_amount + type: string + required: false + description: Cumulative debited amount + x-description-zh: 累计出账金额 + x-description-zh-hk: 累計出賬金額 + - name: └ debited_details + type: array + required: false + description: Debit transaction details + x-description-zh: 出账明细 + x-description-zh-hk: 出賬明細 + - name: └ cumulative_fee_amount + type: string + required: false + description: Cumulative fee amount + x-description-zh: 累计费用金额 + x-description-zh-hk: 累計費用金額 + - name: └ fee_details + type: array + required: false + description: Fee transaction details + x-description-zh: 费用明细 + x-description-zh-hk: 費用明細 + - name: └ short_holding_value + type: string + required: false + description: Short position value + x-description-zh: 空头持仓市值 + x-description-zh-hk: 空頭持倉市值 + - name: └ long_holding_value + type: string + required: false + description: Long position value + x-description-zh: 多头持仓市值 + x-description-zh-hk: 多頭持倉市值 + - name: └ holding_value_at_beginning + type: string + required: false + description: Holding value at period start + x-description-zh: 期初持仓市值 + x-description-zh-hk: 期初持倉市值 + - name: └ holding_value_at_ending + type: string + required: false + description: Holding value at period end + x-description-zh: 期末持仓市值 + x-description-zh-hk: 期末持倉市值 + - name: derivative_pnl_details + type: object + required: false + description: Derivatives P&L breakdown + x-description-zh: 衍生品盈亏明细 + x-description-zh-hk: 衍生品盈虧明細 + - name: └ holding_value + type: string + required: false + description: Current holding value + x-description-zh: 当前持仓市值 + x-description-zh-hk: 當前持倉市值 + - name: └ profit + type: string + required: false + description: Total profit/loss + x-description-zh: 总盈亏 + x-description-zh-hk: 總盈虧 + - name: └ cumulative_credited_amount + type: string + required: false + description: Cumulative credited amount + x-description-zh: 累计入账金额 + x-description-zh-hk: 累計入賬金額 + - name: └ credited_details + type: array + required: false + description: Credit transaction details + x-description-zh: 入账明细 + x-description-zh-hk: 入賬明細 + - name: └ cumulative_debited_amount + type: string + required: false + description: Cumulative debited amount + x-description-zh: 累计出账金额 + x-description-zh-hk: 累計出賬金額 + - name: └ debited_details + type: array + required: false + description: Debit transaction details + x-description-zh: 出账明细 + x-description-zh-hk: 出賬明細 + - name: └ cumulative_fee_amount + type: string + required: false + description: Cumulative fee amount + x-description-zh: 累计费用金额 + x-description-zh-hk: 累計費用金額 + - name: └ fee_details + type: array + required: false + description: Fee transaction details + x-description-zh: 费用明细 + x-description-zh-hk: 費用明細 + - name: └ short_holding_value + type: string + required: false + description: Short position value + x-description-zh: 空头持仓市值 + x-description-zh-hk: 空頭持倉市值 + - name: └ long_holding_value + type: string + required: false + description: Long position value + x-description-zh: 多头持仓市值 + x-description-zh-hk: 多頭持倉市值 + - name: └ holding_value_at_beginning + type: string + required: false + description: Holding value at period start + x-description-zh: 期初持仓市值 + x-description-zh-hk: 期初持倉市值 + - name: └ holding_value_at_ending + type: string + required: false + description: Holding value at period end + x-description-zh: 期末持仓市值 + x-description-zh-hk: 期末持倉市值 + - name: name + type: string + required: false + description: Security name. + x-description-zh: 标的名称。 + x-description-zh-hk: 標的名稱。 + - name: updated_at + type: string + required: false + description: Last update timestamp + x-description-zh: 最后更新时间 + x-description-zh-hk: 最後更新時間 + - name: default_tag + type: integer + required: false + description: Default display tag + x-description-zh: 默认显示标签 + x-description-zh-hk: 默認顯示標簽 + - name: currency + type: string + required: false + description: Currency + x-description-zh: 货币 + x-description-zh-hk: 貨幣 + - name: start + type: string + required: false + description: Period start + x-description-zh: 统计期开始 + x-description-zh-hk: 統計期開始 + - name: end + type: string + required: false + description: Period end + x-description-zh: 统计期结束 + x-description-zh-hk: 統計期結束 + - name: start_date + type: string + required: false + description: Start date + x-description-zh: 开始日期 + x-description-zh-hk: 開始日期 + - name: end_date + type: string + required: false + description: End date + x-description-zh: 结束日期 + x-description-zh-hk: 結束日期 + - name: updated_date + type: string + required: false + description: Last update date + x-description-zh: 最后更新日期 + x-description-zh-hk: 最後更新日期 responses: '200': description: Successful response @@ -1316,242 +39031,254 @@ paths: code: 0 message: success data: - items: - - id: '280228333' - title: 苹果拟在地图应用引入广告 - description: 苹果计划在其地图应用中引入广告,以推动服务业务增长... - url: https://longbridge.com/news/280228333 - published_at: '1774310775' - comments_count: 0 - likes_count: 0 - shares_count: 0 + name: Apple + currency: USD + profit: '100.00' + start: '1763769600' + end: '1778724973' + start_date: '2025-11-22' + end_date: '2026-05-14' + default_tag: 0 + updated_at: '1778724973' + updated_date: '2026-05-14' + underlying_details: + profit: '100.00' + holding_value: '1790.16' + holding_value_at_beginning: null + holding_value_at_ending: '1790.16' + long_holding_value: '1790.16' + short_holding_value: '0.00' + cumulative_credited_amount: '0.00' + cumulative_debited_amount: '0.00' + cumulative_fee_amount: '0.00' + credited_details: [] + debited_details: [] + fee_details: [] + derivative_pnl_details: + profit: '0.00' + holding_value: '0.00' + holding_value_at_beginning: null + holding_value_at_ending: '0.00' + long_holding_value: '0.00' + short_holding_value: '0.00' + cumulative_credited_amount: '0.00' + cumulative_debited_amount: '0.00' + cumulative_fee_amount: '0.00' + credited_details: [] + debited_details: [] + fee_details: [] default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/content/{symbol}/topics: + /v1/portfolio/profit-analysis/flows: get: - operationId: list_topics - summary: List Community Topics by Symbol - x-summary-zh: 按标的获取社区讨论 + operationId: profit_analysis_flows + summary: Profit Analysis Flows + x-summary-zh: 盈亏流水 + x-summary-zh-hk: 盈虧流水 description: | - Get the list of community discussion topics for the specified symbol. - x-description-zh: 获取指定标的的社区讨论。 + Query account cash flow history including deposits, withdrawals, dividends, and settlements. + x-description-zh: | + 查询账户资金流水历史,包含入金、出金、分红和结算等。 + x-description-zh-hk: | + 查詢賬戶資金流水歷史,包含入金、出金、股息和結算等。 + x-subgroup: Portfolio + x-subgroup-zh: 投资组合 + x-subgroup-zh-hk: 投資組合 tags: - - Community - parameters: + - Account + x-parameters: - name: symbol - in: path + in: query + type: string required: true - description: 'Security symbol to query topics for (e.g. `AAPL.US`, `700.HK`).' - schema: - type: string - x-codeSamples: - - lang: Shell - label: CLI - source: | - longbridge topics <SYMBOL> - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: - items: - - id: '39469738' - title: '' - description: 期待苹果的 AI。我相信在用户端侧,苹果能把 AI 做得很好。 - url: https://longbridge.com/topics/39469738 - published_at: '1774325592' - comments_count: 0 - likes_count: 0 - shares_count: 0 - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/content/topics/mine: - get: - operationId: list_my_topics - summary: List My Published Topics - x-summary-zh: 获取我发布的讨论 - description: | - Get the list of topics published by the current authenticated user. - Supports pagination and filtering by topic type. - x-description-zh: 获取当前认证用户已发布的讨论列表,支持分页和按讨论类型筛选。 - tags: - - Community - x-codeSamples: - - lang: Shell - label: CLI - source: | - longbridge topic mine - longbridge topic mine --type article - longbridge topic mine --type post --size 10 --page 2 - longbridge topic mine --format json - parameters: + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 - name: page in: query + type: integer required: false - description: Page number (1-based). Defaults to `1`. - schema: - type: integer - nullable: true + description: 'Page number (default: 1)' + x-description-zh: 页码(默认 1) + x-description-zh-hk: 頁碼(預設 1) - name: size in: query + type: integer required: false - description: Number of items per page, range 1–500. Defaults to `50`. - schema: - type: integer - nullable: true - - name: topic_type + description: 'Page size (default: 20)' + x-description-zh: 每页数量(默认 20) + x-description-zh-hk: 每頁數量(預設 20) + - name: derivative in: query + type: boolean required: false - description: | - Filter by topic type. One of: - - `article` — long-form article (has title) - - `post` — short post - - Omit to return all types. - schema: - type: string - nullable: true - enum: - - article - - post - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: - items: - - id: '39304657' - title: My Analysis on AAPL - description: A brief summary of my article... - body: Full markdown content here... - topic_type: article - likes_count: 12 - comments_count: 3 - views_count: 200 - shares_count: 1 - tickers: - - AAPL.US - hashtags: - - earnings - license: 1 - detail_url: https://longbridge.com/topics/39304657 - created_at: '1742000000' - updated_at: '1742000000' - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/content/topics: - post: - operationId: create_topic - summary: Create Topic - x-summary-zh: 创建社区讨论 - description: | - Create a new community topic. Two content types are supported: - - - `post` (default): Plain text only, Markdown is **not** rendered — appears as literal characters. - - `article`: This should with `title` params, suppports Markdown with headers, tables, bold, code blocks, etc. - - Only users who have opened a **Longbridge account and hold assets** are allowed. - - Symbols mentioned in the body (e.g. `TSLA.US`, `700.HK`) are automatically recognized and linked as related stocks. Use `tickers` to associate additional symbols not explicitly mentioned in the body. - - ⚠️ Do not abuse symbol linking to associate unrelated stocks. Content moderation may restrict publishing or mute the account. - - **Rate limit:** Max 3 topics per user per minute, 10 per 24 hours. Exceeding the limit returns `429`. - - > Rate limit thresholds are for reference only and may be adjusted at any time. - x-description-zh: | - 创建新的社区讨论,根据 `topic_type` 支持两种类型的发布: + description: Include derivative positions + x-description-zh: 是否包含衍生品仓位 + x-description-zh-hk: 是否包含衍生品倉位 + x-codeSamples: + - lang: Shell + label: CLI + source: | + longbridge cash-flow + longbridge cash-flow --format json + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests - - `post`:(默认),仅支持**纯文本**, 不支持 Markdown。 - - `article`: 选择这项类型,`title` 字段必填,支持 Markdown 格式包含标题、表格、加粗等格式支持。 + resp = requests.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp - 仅限 **Longbridge 开户且持有资产** 的用户才允许通过 Longbridge Developers 的 API 或 CLI 发布社区讨论和回复。 + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) - 正文中提到的标的代码(如 `TSLA.US`, `700.HK`)会被平台自动识别并关联。`tickers` 用于补充正文中未显式提及的标的。 + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows") + url.searchParams.set("symbol", "<symbol>") - ⚠️ 请勿滥用此功能关联与内容无关的标的,否则后台内容运营可能会限制发布,甚至有可能禁言。 + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; - **频率限制:** 同一用户每分钟最多创建 3 篇,24 小时内最多 10 篇,超出返回 `429`。 + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> - > 频率限制规则仅供参考,平台可能随时进行内部调整。 - tags: - - Community - x-codeSamples: - - lang: Bash - label: CLI - source: | - # Short post (plain text, Markdown not rendered) - longbridge topic create --body "Bullish on 700.HK today" - - # Short post with tickers - longbridge topic create --body "NVDA GTC highlights" --tickers NVDA.US,700.HK - - # Article (Markdown, title required) - longbridge topic create --title "My Analysis" --body "**Bullish** on 700.HK because..." --type article - - # Article from file - longbridge topic create --title "Q4 Preview" --body "$(cat analysis.md)" --type article - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - body - properties: - title: - type: string - description: Topic title. Required when `topic_type` is `article`; optional for `post`. - body: - type: string - description: | - Topic body. - - - `post`: plain text only — Markdown is not rendered. - - `article`: Markdown is supported. - topic_type: - type: string - nullable: true - enum: - - article - - post - description: | - Topic type. One of: - - `article` — long-form article with a title - - `post` — short post (default) - tickers: - type: array - nullable: true - description: 'Associated security symbols, format `{symbol}.{market}` (e.g. `["AAPL.US", "700.HK"]`). Maximum 10.' - items: - type: string - hashtags: - type: array - nullable: true - description: Associated hashtag names (e.g. `["earnings", "fed"]`). Maximum 1. - items: - type: string + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/portfolio/profit-analysis/flows?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: flows_list + type: object[] + required: true + description: Paginated list of flow items + x-description-zh: 资金流水列表(分页), + x-description-zh-hk: 資金流水列表(分頁), + - name: └ executed_date + type: string + required: true + description: Execution date (e.g. `2024-01-15`) + x-description-zh: 执行日期(如 `2024-01-15`) + x-description-zh-hk: 執行日期(如 `2024-01-15`) + - name: └ executed_timestamp + type: string + required: false + description: Execution timestamp + x-description-zh: 执行时间戳 + x-description-zh-hk: 執行時間戳 + - name: └ code + type: string + required: false + description: Security code + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ direction + type: string + required: false + description: 'Direction: `In` or `Out`' + x-description-zh: 方向:`In`(买入)或 `Out`(卖出) + x-description-zh-hk: 方向:`In`(買入)或 `Out`(賣出) + - name: └ executed_quantity + type: string + required: false + description: Executed quantity + x-description-zh: 成交数量 + x-description-zh-hk: 成交數量 + - name: └ executed_price + type: string + required: false + description: Executed price + x-description-zh: 成交价格 + x-description-zh-hk: 成交價格 + - name: └ executed_cost + type: string + required: false + description: Executed cost + x-description-zh: 成交成本 + x-description-zh-hk: 成交成本 + - name: └ describe + type: string + required: false + description: Human-readable description + x-description-zh: 描述说明 + x-description-zh-hk: 描述說明 + - name: has_more + type: boolean + required: false + description: Whether there are more pages + x-description-zh: 是否有更多页 + x-description-zh-hk: 是否有更多頁 responses: '200': description: Successful response @@ -1561,83 +39288,386 @@ paths: code: 0 message: success data: - item: - id: '39304657' - title: My View on AAPL - description: Brief plain-text summary... - body: '**Bullish** on AAPL because...' - topic_type: article - tickers: - - AAPL.US - hashtags: - - earnings - images: [] - likes_count: 0 - comments_count: 0 - views_count: 0 - shares_count: 0 - detail_url: https://longbridge.com/topics/39304657 - author: - member_id: '10086' - name: Jane Doe - avatar: https://example.com/avatar.jpg - created_at: '1742000000' - updated_at: '1742000000' - '403': - description: Forbidden — user has not opened a Longbridge account or has no assets. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '429': - description: Too Many Requests — rate limit exceeded (3/min or 10/24h per user). - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + has_more: false + flows_list: + - code: AAPL + symbol: AAPL.US + direction: In + executed_date: '2025-11-22' + executed_timestamp: '1763769600' + executed_quantity: '10' + executed_price: '180.50' + executed_cost: '1805.00' + describe: Buy AAPL.US default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/content/topics/{id}: + /v1/trade/order/today: get: - operationId: topic_detail - summary: Topic Detail - x-summary-zh: 获取讨论详情 + operationId: today_orders + summary: Today Orders + x-summary-zh: 当日订单 + x-summary-zh-hk: 當日訂單 description: | - Get the full details of a community topic by its ID. - - The response includes: - - Full body text (Markdown for `article` type, plain text for `post`) - - Author profile (member ID, display name, avatar) - - Associated tickers and hashtags - - Engagement counts (likes, replies, views, shares) - - Direct URL to the topic page + This API is used to get today order or get order by order id. x-description-zh: | - 根据 ID 获取社区讨论的完整详情。 - - 返回内容包括: - - 完整正文(`article` 类型为 Markdown,`post` 类型为纯文本) - - 作者信息(member ID、昵称、头像) - - 关联标的代码和标签 - - 互动数据(点赞、回复、浏览、分享数) - - 讨论页面直链 + 该接口用于获取当日订单和订单查询。 + x-description-zh-hk: | + 該接口用於獲取當日訂單和訂單查詢。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 tags: - - Community - parameters: - - name: id - in: path - required: true - description: 'Topic ID (e.g. `6993508780031016960`).' - schema: - type: string + - Trade + x-parameters: + - name: symbol + in: query + type: string + required: false + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: status + in: query + type: array + required: false + description: 'Order status. One of: `NotReported`, `ReplacedNotReported`, `ProtectedNotReported`, `VarietiesNotReported`, `FilledStatus`, `WaitToNew`, `NewStatus`, `WaitToReplace`, `PendingReplaceStatus`, `ReplacedStatus`, `PartialFilledStatus`, `WaitToCancel`, `PendingCancelStatus`, `RejectedStatus`, `CanceledStatus`, `ExpiredStatus`, `PartialWithdrawal`. example: `status=FilledStatus&status=NewStatus`' + x-description-zh: 订单状态。可选值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。. 例如:`status=FilledStatus&status=NewStatus` + x-description-zh-hk: 訂單狀態。可選值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。. 例如:`status=FilledStatus&status=NewStatus` + - name: side + in: query + type: string + required: false + description: Order side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: market + in: query + type: string + required: false + description: Market. **Enum Value:**, `US` - United States of America Market, `HK` - Hong Kong Market + x-description-zh: 市场。**可选值:**, `US` - 美股,`HK` - 港股 + x-description-zh-hk: 市場。**可選值:**, `US` - 美股,`HK` - 港股 + - name: order_id + in: query + type: string + required: false + description: 'Order ID, example: `701276261045858304`' + x-description-zh: 订单 ID,用于指定订单 ID 查询,例如:`701276261045858304` + x-description-zh-hk: 訂單 ID,用於指定訂單 ID 查詢,例如:`701276261045858304` + - name: is_attached + in: query + type: boolean + required: false + description: Whether `order_id` refers to an attached order, returns the attached order information if `true` + x-description-zh: order_id 是否为附加单,为 true 时返回附加单订单信息 + x-description-zh-hk: order_id 是否為附加單,為 true 時返回附加單訂單信息 x-codeSamples: - - lang: bash + - lang: Shell label: CLI source: | - longbridge topic detail 6993508780031016960 + longbridge order + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/trade/order/today' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/trade/order/today", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/trade/order/today", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/trade/order/today", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/order/today")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/trade/order/today") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/order/today"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/trade/order/today\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: orders + type: object[] + required: false + description: Order Detail + x-description-zh: 订单信息 + x-description-zh-hk: 訂單信息 + - name: └ order_id + type: string + required: false + description: Order ID + x-description-zh: 订单 ID + x-description-zh-hk: 訂單 ID + - name: └ status + type: string + required: false + description: 'Order Status. One of: `NotReported`, `ReplacedNotReported`, `ProtectedNotReported`, `VarietiesNotReported`, `FilledStatus`, `WaitToNew`, `NewStatus`, `WaitToReplace`, `PendingReplaceStatus`, `ReplacedStatus`, `PartialFilledStatus`, `WaitToCancel`, `PendingCancelStatus`, `RejectedStatus`, `CanceledStatus`, `ExpiredStatus`, `PartialWithdrawal`.' + x-description-zh: 订单状态。可选值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。 + x-description-zh-hk: 訂單狀態。可選值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。 + - name: └ stock_name + type: string + required: false + description: Stock Name + x-description-zh: 股票名称 + x-description-zh-hk: 股票名稱 + - name: └ quantity + type: string + required: false + description: Submitted Quantity + x-description-zh: 下单数量 + x-description-zh-hk: 下單數量 + - name: └ executed_quantity + type: string + required: false + description: Executed Quantity. when the order is not filled, value is 0 + x-description-zh: 成交数量。. 当订单未成交时为 0 + x-description-zh-hk: 成交數量。. 當訂單未成交時為 0 + - name: └ price + type: string + required: false + description: Submitted Price. when market condition order is not triggered, value is empty string + x-description-zh: 下单价格。. 当市价条件单未触发时为空字符串 + x-description-zh-hk: 下單價格。. 當市價條件單未觸發時為空字符串 + - name: └ executed_price + type: string + required: false + description: Executed Price. when the order is not filled, value is 0 + x-description-zh: 成交价。. 当订单未成交时为 0 + x-description-zh-hk: 成交價。. 當訂單未成交時為 0 + - name: └ submitted_at + type: string + required: false + description: Submitted Time + x-description-zh: 下单时间 + x-description-zh-hk: 下單時間 + - name: └ side + type: string + required: false + description: Order Side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: └ symbol + type: string + required: false + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: └ order_type + type: string + required: false + description: 'Order Type. One of: `LO`, `ELO`, `MO`, `AO`, `ALO`, `ODD`, `LIT`, `MIT`, `TSLPAMT`, `TSLPPCT`, `SLO`.' + x-description-zh: 订单类型。可选值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + x-description-zh-hk: 訂單類型。可選值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + - name: └ trigger_price + type: string + required: false + description: '`LIT` / `MIT` Order Trigger Price. When the order is not `LIT` / `MIT` order, value is empty string' + x-description-zh: '`LIT` / `MIT` 订单触发价格。. 当订单不是 `LIT` / `MIT` 订单为空字符串' + x-description-zh-hk: '`LIT` / `MIT` 訂單觸發價格。. 當訂單不是 `LIT` / `MIT` 訂單為空字符串' + - name: └ tag + type: string + required: false + description: Order tag. **Enum Value**, `Normal` - Normal Order, `Gtc` - Long term Order, `Grey` - Grey Order + x-description-zh: 订单标记。**可选值:**, `Normal` - 普通订单,`Gtc` - 长期单,`Grey` - 暗盘单 + x-description-zh-hk: 訂單標記。**可選值:**, `Normal` - 普通訂單,`Gtc` - 長期單,`Grey` - 暗盤單 + - name: └ time_in_force + type: string + required: false + description: Time in force Type. **Enum Value:**, `Day` - Day Order, `GTC` - Good Til Canceled Order, `GTD` - Good Til Date Order + x-description-zh: 订单有效期类型。**可选值:**, `Day` - 当日有效,`GTC` - 撤单前有效,`GTD` - 到期前有效 + x-description-zh-hk: 訂單有效期類型。**可選值:**, `Day` - 當日有效,`GTC` - 撤單前有效,`GTD` - 到期前有效 + - name: └ expire_date + type: string + required: false + description: 'Long term order expire date, format: `YYYY-MM-DD`, example: `2022-12-05`. When not a long term order, default value is empty string' + x-description-zh: 长期单过期时间,格式为 `YYYY-MM-DD`, 例如:`2022-12-05。. 不是长期单时,默认为空字符串。` + x-description-zh-hk: 長期單過期時間,格式為 `YYYY-MM-DD`, 例如:`2022-12-05。. 不是長期單時,默認為空字符串。` + - name: └ updated_at + type: string + required: false + description: Last updated time, formatted as a timestamp (second) + x-description-zh: 最近更新时间,格式为时间戳 (秒),默认为 0。 + x-description-zh-hk: 最近更新時間,格式為時間戳 (秒),默認為 0。 + - name: └ trigger_at + type: string + required: false + description: Conditional order trigger time. formatted as a timestamp (second) + x-description-zh: 条件单触发时间,格式为时间戳 (秒),默认为 0。 + x-description-zh-hk: 條件單觸發時間,格式為時間戳 (秒),默認為 0。 + - name: └ trigger_status + type: string + required: false + description: Conditional Order Trigger Status, When an order is not a conditional order or a conditional order is not triggered, the trigger status is NOT_USED. **Enum Value**, `NOT_USED`, `DEACTIVE`, `ACTIVE`, `RELEASED` + x-description-zh: 条件单触发状态,当订单不是条件单或条件单未触发时,触发状态为 NOT_USED. **可选值:**, `NOT_USED` - 未激活 `DEACTIVE` - 已失效 `ACTIVE` - 已激活 `RELEASED` - 已触发 + x-description-zh-hk: 條件單觸發狀態,當訂單不是條件單或條件單未觸發時,觸發狀態為 NOT_USED. **可選值:**, `NOT_USED` - 未激活 `DEACTIVE` - 已失效 `ACTIVE` - 已激活 `RELEASED` - 已觸發 + - name: └ outside_rth + type: string + required: false + description: Enable or disable outside regular trading hours, Default is `UnknownOutsideRth` when the order is not a US stock. **Enum Value:**, `RTH_ONLY` - Regular trading hour only, `ANY_TIME` - Any time, `OVERNIGHT` - Overnight" + x-description-zh: 是否允许盘前盘后,当订单不是美股时,默认为 UnknownOutsideRth. **可选值:**, `RTH_ONLY` - 不允许盘前盘后,`ANY_TIME` - 允许盘前盘后,`OVERNIGHT` - 夜盘" + x-description-zh-hk: 是否允許盤前盤後,當訂單不是美股時,默認為 UnknownOutsideRth. **可選值:**, `RTH_ONLY` - 不允許盤前盤後,`ANY_TIME` - 允許盤前盤後,`OVERNIGHT` - 夜盤 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 结算货币 + x-description-zh-hk: 結算貨幣 + - name: └ limit_depth_level + type: integer + required: false + description: Specifies the bid/ask depth level + x-description-zh: 指定买卖档位 + x-description-zh-hk: 指定買賣檔位 + - name: └ trigger_count + type: integer + required: false + description: Number of triggers + x-description-zh: 触发次数 + x-description-zh-hk: 觸發次數 + - name: └ trigger_qty_type + type: integer + required: false + description: Trigger quantity type. + x-description-zh: 触发数量类型。 + x-description-zh-hk: 觸發數量類型。 + - name: └ attached_orders + type: array + required: false + description: List of attached order details + x-description-zh: 附加订单详情列表 + x-description-zh-hk: 附加訂單詳情列表 + - name: └ last_done + type: string + required: false + description: Last done. when the order is not filled, value is empty string + x-description-zh: 最近成交价格。. 当订单未成交时为空字符串 + x-description-zh-hk: 最近成交價格。. 當訂單未成交時為空字符串 + - name: └ msg + type: string + required: false + description: Rejected message or remark, default value is empty string. + x-description-zh: 拒绝信息或备注,默认为空字符串。 + x-description-zh-hk: 拒絕信息或備註,默認為空字符串。 + - name: └ trailing_amount + type: string + required: false + description: '`TSLPAMT` order trailing amount. When the order is not `TSLPAMT` order, value is empty string' + x-description-zh: '`TSLPAMT` 订单跟踪金额。. 当订单不是 `TSLPAMT` 订单时为空字符串。' + x-description-zh-hk: '`TSLPAMT` 訂單跟蹤金額。. 當訂單不是 `TSLPAMT` 訂單時為空字符串。' + - name: └ trailing_percent + type: string + required: false + description: '`TSLPPCT` order trailing percent. When the order is not `TSLPPCT` order, value is empty string' + x-description-zh: '`TSLPPCT` 订单跟踪涨跌幅。. 当订单不是 `TSLPPCT` 订单时为空字符串。' + x-description-zh-hk: '`TSLPPCT` 訂單跟蹤漲跌幅。. 當訂單不是 `TSLPPCT` 訂單時為空字符串。' + - name: └ limit_offset + type: string + required: false + description: '`TSLPPCT` order limit offset amount. When the order is not `TSLPPCT` order, value is empty string' + x-description-zh: '`TSLPAMT` / `TSLPPCT` 订单指定价差。. 当订单不是 `TSLPAMT` / `TSLPPCT` 订单时为空字符串。' + x-description-zh-hk: '`TSLPAMT` / `TSLPPCT` 訂單指定價差。. 當訂單不是 `TSLPAMT` / `TSLPPCT` 訂單時為空字符串。' + - name: └ remark + type: string + required: false + description: Remark + x-description-zh: 备注 + x-description-zh-hk: 備註 + - name: └ monitor_price + type: string + required: false + description: Monitoring price + x-description-zh: 监控价格 + x-description-zh-hk: 監控價格 + - name: └ monitor_counter_id + type: string + required: false + description: Internal ID of the monitored security (conditional orders). + x-description-zh: 监控标的内部标识(条件单)。 + x-description-zh-hk: 監控標的內部標識(條件單)。 + - name: └ multi_leg + type: string + required: false + description: Multi-leg strategy information. Only returned for multi-leg option combination orders; otherwise not returned. + x-description-zh: 多腿策略信息,仅多腿期权组合订单返回,非组合订单不返回。 + x-description-zh-hk: 多腿策略信息,僅多腿期權組合訂單返回,非組合訂單不返回。 + - name: has_more + type: boolean + required: false + description: has more orders record. The maximum number of orders per query is 1000, if the number of results exceeds 1000, then has_more will be true + x-description-zh: 是否还有更多数据。. 每次查询最大订单数量为 1000,如果查询结果数量超过 1000,那么 has_more 就会为 true + x-description-zh-hk: 是否還有更多數據。. 每次查詢最大訂單數量為 1000,如果查詢結果數量超過 1000,那麼 has_more 就會為 true responses: '200': description: Successful response @@ -1647,89 +39677,447 @@ paths: code: 0 message: success data: - item: - id: '6993508780031016960' - title: My Analysis on AAPL - description: A brief plain-text summary of the topic. - body: '**Bullish** on AAPL because...' - topic_type: article - author: - member_id: '1000001' - name: Jane Doe - avatar: https://cdn.longbridge.com/avatars/1000001.jpg - tickers: - - AAPL.US - hashtags: - - earnings - images: - - url: https://cdn.longbridge.com/img/abc.jpg - sm: https://cdn.longbridge.com/img/abc_sm.jpg - lg: https://cdn.longbridge.com/img/abc_lg.jpg - likes_count: 42 - comments_count: 7 - views_count: 1500 - shares_count: 3 - detail_url: https://longbridge.com/topics/6993508780031016960 - created_at: '1742000000' - updated_at: '1742001000' + orders: + - currency: HKD + executed_price: '0.000' + executed_quantity: '0' + expire_date: '' + last_done: '' + limit_offset: '' + msg: '' + order_id: '706388312699592704' + order_type: ELO + outside_rth: UnknownOutsideRth + price: '11.900' + quantity: '200' + side: Buy + status: RejectedStatus + stock_name: Bank of East Asia Ltd/The + submitted_at: '1651644897' + symbol: 23.HK + tag: Normal + time_in_force: Day + trailing_amount: '' + trailing_percent: '' + trigger_at: '0' + trigger_price: '' + trigger_status: NOT_USED + updated_at: '1651644898' + remark: '' + limit_depth_level: 0 + monitor_price: '' + trigger_count: 1 + attached_orders: + - order_id: '706388312699592705' + attached_type_display: 2 + trigger_price: '10.500' + quantity: '200' + executed_qty: '0' + status: NewStatus + updated_at: '1651644898' + withdrawn: false + gtd: '' + time_in_force: Day + counter_id: '' + trigger_status: 0 + executed_amount: '0' + tag: 0 + submitted_at: '1651644897' + executed_price: '0.000' + force_only_rth: RTH_ONLY + reviewed: false + activate_order_type: MIT + activate_rth: RTH_ONLY + submit_price: '' + multi_leg: + strategy: '2' + strategy_name: Vertical spread + multileg_id: Spread_QQQ20260731C764/767 + code: QQQ 260731 764/767 Vertical spread + legs: + - symbol: QQQ260731C764000.US + side: Buy + position: LONG + ratio_quantity: '1' + strike_price: '764' + expire_date: '20260731' + contract_direction: C + - symbol: QQQ260731C767000.US + side: Sell + position: SHORT + ratio_quantity: '1' + strike_price: '767' + expire_date: '20260731' + contract_direction: C default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/content/topics/{topic_id}/comments: + /v1/trade/order/history: get: - operationId: list_topic_replies - summary: List Topic Replies - x-summary-zh: 获取讨论回复列表 + operationId: history_orders + summary: History Orders + x-summary-zh: 历史订单 + x-summary-zh-hk: 歷史訂單 description: | - Get the reply list for a specific topic, with pagination. - - Each reply includes: - - Author profile (member ID, display name, avatar) - - Body (plain text) - - Engagement counts (likes, nested replies) - - `reply_to_id`: `"0"` means a top-level reply; any other value is the ID of the parent reply it is nested under. + This API is used to get history order. x-description-zh: | - 获取指定讨论下的回复列表,支持分页。 - - 每条回复包含: - - 作者信息(member ID、昵称、头像) - - 正文(纯文本) - - 互动数据(点赞数、嵌套回复数) - - `reply_to_id`:`"0"` 表示顶层回复,其他值表示对指定回复的嵌套回复 + 该接口用于获取历史订单。 + x-description-zh-hk: | + 該接口用於獲取歷史訂單。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 tags: - - Community - parameters: - - name: topic_id - in: path - required: true - description: 'Topic ID (e.g. `6993508780031016960`).' - schema: - type: string - - name: page + - Trade + x-parameters: + - name: symbol in: query + type: string required: false - description: Page number (1-based). Defaults to `1`. - schema: - type: integer - nullable: true - - name: size + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: status + in: query + type: array + required: false + description: 'Order status. One of: `NotReported`, `ReplacedNotReported`, `ProtectedNotReported`, `VarietiesNotReported`, `FilledStatus`, `WaitToNew`, `NewStatus`, `WaitToReplace`, `PendingReplaceStatus`, `ReplacedStatus`, `PartialFilledStatus`, `WaitToCancel`, `PendingCancelStatus`, `RejectedStatus`, `CanceledStatus`, `ExpiredStatus`, `PartialWithdrawal`. example: `status=FilledStatus&status=NewStatus`' + x-description-zh: 订单状态。可选值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。. 例如:`status=FilledStatus&status=NewStatus` + x-description-zh-hk: 訂單狀態。可選值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。. 例如:`status=FilledStatus&status=NewStatus` + - name: side + in: query + type: string + required: false + description: Order side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: market + in: query + type: string + required: false + description: Market. **Enum Value:**, `US` - United States of America Market, `HK` - Hong Kong Market + x-description-zh: 市场。**可选值:**, `US` - 美股,`HK` - 港股 + x-description-zh-hk: 市場。**可選值:**, `US` - 美股,`HK` - 港股 + - name: start_at in: query + type: string + required: false + description: 'Start time, formatted as a timestamp (second), example: `1650410999`. If the start time is null, the default is the 90 days before of the end time or 90 days before of the current time' + x-description-zh: 开始时间,格式为时间戳 (秒),例如:`1650410999`。. 开始时间为空时,默认为结束时间或当前时间前九十天。 + x-description-zh-hk: 開始時間,格式為時間戳 (秒),例如:`1650410999`。. 開始時間為空時,默認為結束時間或當前時間前九十天。 + - name: end_at + in: query + type: string required: false - description: Number of items per page, range 1–50. Defaults to `20`. - schema: - type: integer - nullable: true - minimum: 1 - maximum: 50 + description: 'End time, formatted as a timestamp (second), example: `1650410999`. If the end time is null, the default is the current time or 90 days after of the start time' + x-description-zh: 结束时间,格式为时间戳 (秒),例如:`1650410999`。. 结束时间为空时,默认为开始时间后九十天或当前时间。 + x-description-zh-hk: 結束時間,格式為時間戳 (秒),例如:`1650410999`。. 結束時間為空時,默認為開始時間後九十天或當前時間。 x-codeSamples: - - lang: bash + - lang: Shell label: CLI source: | - longbridge topic replies 6993508780031016960 - longbridge topic replies 6993508780031016960 --page 2 --size 20 + longbridge order --history + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/trade/order/history' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/trade/order/history", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/trade/order/history", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/trade/order/history", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/order/history")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/trade/order/history") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/order/history"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/trade/order/history\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: orders + type: object[] + required: false + description: Order Detail + x-description-zh: 订单信息 + x-description-zh-hk: 訂單信息 + - name: └ order_id + type: string + required: false + description: Order ID + x-description-zh: 订单 ID + x-description-zh-hk: 訂單 ID + - name: └ status + type: string + required: false + description: 'Order Status. One of: `NotReported`, `ReplacedNotReported`, `ProtectedNotReported`, `VarietiesNotReported`, `FilledStatus`, `WaitToNew`, `NewStatus`, `WaitToReplace`, `PendingReplaceStatus`, `ReplacedStatus`, `PartialFilledStatus`, `WaitToCancel`, `PendingCancelStatus`, `RejectedStatus`, `CanceledStatus`, `ExpiredStatus`, `PartialWithdrawal`.' + x-description-zh: 订单状态。可选值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。 + x-description-zh-hk: 訂單狀態。可選值:`NotReported`、`ReplacedNotReported`、`ProtectedNotReported`、`VarietiesNotReported`、`FilledStatus`、`WaitToNew`、`NewStatus`、`WaitToReplace`、`PendingReplaceStatus`、`ReplacedStatus`、`PartialFilledStatus`、`WaitToCancel`、`PendingCancelStatus`、`RejectedStatus`、`CanceledStatus`、`ExpiredStatus`、`PartialWithdrawal`。 + - name: └ stock_name + type: string + required: false + description: Stock Name + x-description-zh: 股票名称 + x-description-zh-hk: 股票名稱 + - name: └ quantity + type: string + required: false + description: Submitted Quantity + x-description-zh: 下单数量 + x-description-zh-hk: 下單數量 + - name: └ executed_quantity + type: string + required: false + description: Executed Quantity. when the order is not filled, value is 0 + x-description-zh: 成交数量。. 当订单未成交时为 0 + x-description-zh-hk: 成交數量。. 當訂單未成交時為 0 + - name: └ price + type: string + required: false + description: Submitted Price. when market condition order is not triggered, value is empty string + x-description-zh: 下单价格。. 当市价条件单未触发时为空字符串 + x-description-zh-hk: 下單價格。. 當市價條件單未觸發時為空字符串 + - name: └ executed_price + type: string + required: false + description: Executed Price. when the order is not filled, value is 0 + x-description-zh: 成交价。. 当订单未成交时为 0 + x-description-zh-hk: 成交價。. 當訂單未成交時為 0 + - name: └ submitted_at + type: string + required: false + description: Submitted Time + x-description-zh: 下单时间 + x-description-zh-hk: 下單時間 + - name: └ side + type: string + required: false + description: Order Side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: └ symbol + type: string + required: false + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: └ order_type + type: string + required: false + description: 'Order Type. One of: `LO`, `ELO`, `MO`, `AO`, `ALO`, `ODD`, `LIT`, `MIT`, `TSLPAMT`, `TSLPPCT`, `SLO`.' + x-description-zh: 订单类型。可选值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + x-description-zh-hk: 訂單類型。可選值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + - name: └ trigger_price + type: string + required: false + description: '`LIT` / `MIT` Order Trigger Price. When the order is not `LIT` / `MIT` order, value is empty string' + x-description-zh: '`LIT` / `MIT` 订单触发价格。. 当订单不是 `LIT` / `MIT` 订单为空字符串' + x-description-zh-hk: '`LIT` / `MIT` 訂單觸發價格。. 當訂單不是 `LIT` / `MIT` 訂單為空字符串' + - name: └ tag + type: string + required: false + description: Order tag. **Enum Value**, `Normal` - Normal Order, `Gtc` - Long term Order, `Grey` - Grey Order + x-description-zh: 订单标记。**可选值:**, `Normal` - 普通订单,`Gtc` - 长期单,`Grey` - 暗盘单 + x-description-zh-hk: 訂單標記。**可選值:**, `Normal` - 普通訂單,`Gtc` - 長期單,`Grey` - 暗盤單 + - name: └ time_in_force + type: string + required: false + description: Time in force Type. **Enum Value:**, `Day` - Day Order, `GTC` - Good Til Canceled Order, `GTD` - Good Til Date Order + x-description-zh: 订单有效期类型。**可选值:**, `Day` - 当日有效,`GTC` - 撤单前有效,`GTD` - 到期前有效 + x-description-zh-hk: 訂單有效期類型。**可選值:**, `Day` - 當日有效,`GTC` - 撤單前有效,`GTD` - 到期前有效 + - name: └ expire_date + type: string + required: false + description: 'Long term order expire date, format: `YYYY-MM-DD`, example: `2022-12-05`. When not a long term order, default value is empty string' + x-description-zh: 长期单过期时间,格式为 `YYYY-MM-DD`, 例如:`2022-12-05。. 不是长期单时,默认为空字符串。` + x-description-zh-hk: 長期單過期時間,格式為 `YYYY-MM-DD`, 例如:`2022-12-05。. 不是長期單時,默認為空字符串。` + - name: └ updated_at + type: string + required: false + description: Last updated time, formatted as a timestamp (second) + x-description-zh: 最近更新时间,格式为时间戳 (秒),默认为 0。 + x-description-zh-hk: 最近更新時間,格式為時間戳 (秒),默認為 0。 + - name: └ trigger_at + type: string + required: false + description: Conditional order trigger time. formatted as a timestamp (second) + x-description-zh: 条件单触发时间,格式为时间戳 (秒),默认为 0。 + x-description-zh-hk: 條件單觸發時間,格式為時間戳 (秒),默認為 0。 + - name: └ trigger_status + type: string + required: false + description: Conditional Order Trigger Status, When an order is not a conditional order or a conditional order is not triggered, the trigger status is NOT_USED. **Enum Value**, `NOT_USED`, `DEACTIVE`, `ACTIVE`, `RELEASED` + x-description-zh: 条件单触发状态,当订单不是条件单或条件单未触发时,触发状态为 NOT_USED. **可选值:**, `NOT_USED` - 未激活 `DEACTIVE` - 已失效 `ACTIVE` - 已激活 `RELEASED` - 已触发 + x-description-zh-hk: 條件單觸發狀態,當訂單不是條件單或條件單未觸發時,觸發狀態為 NOT_USED. **可選值:**, `NOT_USED` - 未激活 `DEACTIVE` - 已失效 `ACTIVE` - 已激活 `RELEASED` - 已觸發 + - name: └ outside_rth + type: string + required: false + description: Enable or disable outside regular trading hours, Default is `UnknownOutsideRth` when the order is not a US stock. **Enum Value:**, `RTH_ONLY` - Regular trading hour only, `ANY_TIME` - Any time, `OVERNIGHT` - Overnight" + x-description-zh: 是否允许盘前盘后,当订单不是美股时,默认为 UnknownOutsideRth. **可选值:**, `RTH_ONLY` - 不允许盘前盘后,`ANY_TIME` - 允许盘前盘后,`OVERNIGHT` - 夜盘" + x-description-zh-hk: 是否允許盤前盤後,當訂單不是美股時,默認為 UnknownOutsideRth. **可選值:**, `RTH_ONLY` - 不允許盤前盤後,`ANY_TIME` - 允許盤前盤後,`OVERNIGHT` - 夜盤 + - name: └ currency + type: string + required: false + description: Currency + x-description-zh: 结算货币 + x-description-zh-hk: 結算貨幣 + - name: └ limit_depth_level + type: integer + required: false + description: Specifies the bid/ask depth level + x-description-zh: 指定买卖档位 + x-description-zh-hk: 指定買賣檔位 + - name: └ trigger_count + type: integer + required: false + description: Number of triggers + x-description-zh: 触发次数 + x-description-zh-hk: 觸發次數 + - name: └ trigger_qty_type + type: integer + required: false + description: Trigger quantity type. + x-description-zh: 触发数量类型。 + x-description-zh-hk: 觸發數量類型。 + - name: └ attached_orders + type: array + required: false + description: List of attached order details + x-description-zh: 附加订单详情列表 + x-description-zh-hk: 附加訂單詳情列表 + - name: └ last_done + type: string + required: false + description: Last done. when the order is not filled, value is empty string + x-description-zh: 最近成交价格。. 当订单未成交时为空字符串 + x-description-zh-hk: 最近成交價格。. 當訂單未成交時為空字符串 + - name: └ msg + type: string + required: false + description: Rejected message or remark, default value is empty string. + x-description-zh: 拒绝信息或备注,默认为空字符串。 + x-description-zh-hk: 拒絕信息或備註,默認為空字符串。 + - name: └ trailing_amount + type: string + required: false + description: '`TSLPAMT` order trailing amount. When the order is not `TSLPAMT` order, value is empty string' + x-description-zh: '`TSLPAMT` 订单跟踪金额。. 当订单不是 `TSLPAMT` 订单时为空字符串。' + x-description-zh-hk: '`TSLPAMT` 訂單跟蹤金額。. 當訂單不是 `TSLPAMT` 訂單時為空字符串。' + - name: └ trailing_percent + type: string + required: false + description: '`TSLPPCT` order trailing percent. When the order is not `TSLPPCT` order, value is empty string' + x-description-zh: '`TSLPPCT` 订单跟踪涨跌幅。. 当订单不是 `TSLPPCT` 订单时为空字符串。' + x-description-zh-hk: '`TSLPPCT` 訂單跟蹤漲跌幅。. 當訂單不是 `TSLPPCT` 訂單時為空字符串。' + - name: └ limit_offset + type: string + required: false + description: '`TSLPPCT` order limit offset amount. When the order is not `TSLPPCT` order, value is empty string' + x-description-zh: '`TSLPAMT` / `TSLPPCT` 订单指定价差。. 当订单不是 `TSLPAMT` / `TSLPPCT` 订单时为空字符串。' + x-description-zh-hk: '`TSLPAMT` / `TSLPPCT` 訂單指定價差。. 當訂單不是 `TSLPAMT` / `TSLPPCT` 訂單時為空字符串。' + - name: └ remark + type: string + required: false + description: Remark + x-description-zh: 备注 + x-description-zh-hk: 備註 + - name: └ monitor_price + type: string + required: false + description: Monitoring price + x-description-zh: 监控价格 + x-description-zh-hk: 監控價格 + - name: └ monitor_counter_id + type: string + required: false + description: Internal ID of the monitored security (conditional orders). + x-description-zh: 监控标的内部标识(条件单)。 + x-description-zh-hk: 監控標的內部標識(條件單)。 + - name: └ multi_leg + type: string + required: false + description: Multi-leg strategy information. Only returned for multi-leg option combination orders; otherwise not returned. + x-description-zh: 多腿策略信息,仅多腿期权组合订单返回,非组合订单不返回。 + x-description-zh-hk: 多腿策略信息,僅多腿期權組合訂單返回,非組合訂單不返回。 + - name: has_more + type: boolean + required: false + description: has more orders record. The maximum number of orders per query is 1000, if the number of results exceeds 1000, then has_more will be true + x-description-zh: 是否还有更多数据。. 每次查询最大订单数量为 1000,如果查询结果数量超过 1000,那么 has_more 就会为 true + x-description-zh-hk: 是否還有更多數據。. 每次查詢最大訂單數量為 1000,如果查詢結果數量超過 1000,那麼 has_more 就會為 true responses: '200': description: Successful response @@ -1739,134 +40127,257 @@ paths: code: 0 message: success data: - items: - - id: '7001234567890123456' - topic_id: '6993508780031016960' - body: Great analysis, fully agree! - reply_to_id: '0' - author: - member_id: '1000002' - name: John Smith - avatar: https://cdn.longbridge.com/avatars/1000002.jpg - images: [] - likes_count: 5 - comments_count: 2 - created_at: '1742001500' - - id: '7001234567890123457' - topic_id: '6993508780031016960' - body: I disagree on the valuation part. - reply_to_id: '7001234567890123456' - author: - member_id: '1000003' - name: Alice Lee - avatar: https://cdn.longbridge.com/avatars/1000003.jpg - images: [] - likes_count: 1 - comments_count: 0 - created_at: '1742001800' + orders: + - currency: HKD + executed_price: '0.000' + executed_quantity: '0' + expire_date: '' + last_done: '' + limit_offset: '' + msg: '' + order_id: '706388312699592704' + order_type: ELO + outside_rth: UnknownOutsideRth + price: '11.900' + quantity: '200' + side: Buy + status: RejectedStatus + stock_name: Bank of East Asia Ltd/The + submitted_at: '1651644897' + symbol: 23.HK + tag: Normal + time_in_force: Day + trailing_amount: '' + trailing_percent: '' + trigger_at: '0' + trigger_price: '' + trigger_status: NOT_USED + updated_at: '1651644898' + remark: '' + limit_depth_level: 0 + monitor_price: '' + trigger_count: 1 + attached_orders: + - order_id: '706388312699592705' + attached_type_display: 2 + trigger_price: '10.500' + quantity: '200' + executed_qty: '0' + status: NewStatus + updated_at: '1651644898' + withdrawn: false + gtd: '' + time_in_force: Day + counter_id: '' + trigger_status: 0 + executed_amount: '0' + tag: 0 + submitted_at: '1651644897' + executed_price: '0.000' + force_only_rth: RTH_ONLY + reviewed: false + activate_order_type: MIT + activate_rth: RTH_ONLY + submit_price: '' + multi_leg: + strategy: '2' + strategy_name: Vertical spread + multileg_id: Spread_QQQ20260731C764/767 + code: QQQ 260731 764/767 Vertical spread + legs: + - symbol: QQQ260731C764000.US + side: Buy + position: LONG + ratio_quantity: '1' + strike_price: '764' + expire_date: '20260731' + contract_direction: C + - symbol: QQQ260731C767000.US + side: Sell + position: SHORT + ratio_quantity: '1' + strike_price: '767' + expire_date: '20260731' + contract_direction: C default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - post: - operationId: create_topic_reply - summary: Create Topic Reply - x-summary-zh: 创建讨论回复 + /v1/trade/estimate/buy_limit: + get: + operationId: estimate_max_purchase + summary: Estimate Maximum Purchase Quantity + x-summary-zh: 预估最大购买数量 + x-summary-zh-hk: 預估最大購買數量 description: | - Post a reply to a community topic. Supports nesting under an existing reply. - - Plain text only — HTML and Markdown are **not** rendered. - - Only users who have opened a **Longbridge account and hold assets** are allowed. - - Symbols mentioned in the body (e.g. `TSLA.US`, `700.HK`) are automatically recognized and linked as related stocks. Use `tickers` to associate additional symbols not explicitly mentioned in the body. - - ⚠️ Do not abuse symbol linking to associate unrelated stocks. Content moderation may restrict publishing or mute the account. - - **Rate limit:** The first 3 replies per user per topic have no wait requirement. After that, each subsequent reply must wait an incrementally longer interval: - - | Reply # (after 3rd) | Required wait | - | ------------------- | ------------- | - | 4th | 3 s | - | 5th | 5 s | - | 6th | 8 s | - | 7th | 13 s | - | 8th | 21 s | - | 9th | 34 s | - | 10th+ | 55 s (cap) | - - Exceeding the rate limit returns `429`. - - > Rate limit thresholds are for reference only and may be adjusted at any time. + This API is used for estimating the maximum purchase quantity for Hong Kong and US stocks, warrants, and options. x-description-zh: | - 在指定讨论下发布回复,支持嵌套回复已有回复。 - - **正文格式:** 仅支持纯文本,不支持 HTML 或 Markdown。 - - 仅限 **Longbridge 开户且持有资产** 的用户才允许通过 Longbridge Developers 的 API 或 CLI 发布社区讨论和回复。 - - 正文中提到的标的代码(如 `TSLA.US`, `700.HK`)会被平台自动识别并关联。`tickers` 用于补充正文中未显式提及的标的。 - - ⚠️ 请勿滥用此功能关联与内容无关的标的,否则后台内容运营可能会限制发布,甚至有可能禁言。 - - **频率限制:** 同一用户在同一讨论下,前 3 条无间隔限制;第 4 条起须与上一条间隔递增: - - | 回复条数(第 3 条后)| 须等待时长 | - | -------------------- | ---------- | - | 第 4 条 | 3 秒 | - | 第 5 条 | 5 秒 | - | 第 6 条 | 8 秒 | - | 第 7 条 | 13 秒 | - | 第 8 条 | 21 秒 | - | 第 9 条 | 34 秒 | - | 第 10 条起 | 55 秒(上限)| - - 超出限制返回 `429`。 - - > 频率限制规则仅供参考,平台可能随时进行内部调整。 + 该接口用于港美股,窝轮,期权的预估最大购买数量。 + x-description-zh-hk: | + 該接口用於港美股,窩輪,期權的預估最大購買數量。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 tags: - - Community - parameters: - - name: topic_id - in: path + - Trade + x-parameters: + - name: symbol + in: query + type: string + required: true + description: 'Stock code, using ticker.region format, for example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: order_type + in: query + type: string + required: true + description: 'Order Type. One of: `LO`, `ELO`, `MO`, `AO`, `ALO`, `ODD`, `LIT`, `MIT`, `TSLPAMT`, `TSLPPCT`, `SLO`.' + x-description-zh: 订单类型。可选值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + x-description-zh-hk: 訂單類型。可選值:`LO`、`ELO`、`MO`、`AO`、`ALO`、`ODD`、`LIT`、`MIT`、`TSLPAMT`、`TSLPPCT`、`SLO`。 + - name: price + in: query + type: string + required: false + description: 'Estimated order price, for example: `388.5`' + x-description-zh: 预估下单价格,例如:`388.5` + x-description-zh-hk: 預估下單價格,例如:`388.5` + - name: side + in: query + type: string required: true - description: 'Topic ID to reply to (e.g. `6993508780031016960`).' - schema: - type: string + description: Order side. **Enum Value**, `Buy` - Buy, `Sell` - Sell (Short selling is only supported for US stocks) + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 卖出只支持美股卖空查询 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 賣出只支持美股賣空查詢 + - name: currency + in: query + type: string + required: false + description: Settlement currency + x-description-zh: 结算货币 + x-description-zh-hk: 結算貨幣 + - name: order_id + in: query + type: string + required: false + description: Order ID, required when estimating the maximum purchase quantity for a modified order + x-description-zh: 订单 ID,获取改单预估最大购买数量时必填 + x-description-zh-hk: 訂單 ID,獲取改單預估最大購買數量時必填 x-codeSamples: - lang: Shell label: CLI source: | - # Top-level reply - longbridge topic create-reply 6993508780031016960 --body "Great post!" + longbridge max-qty TSLA.US + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/trade/estimate/buy_limit?symbol=<symbol>&order_type=<order_type>&side=<side>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/trade/estimate/buy_limit", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "order_type": "<order_type>", "side": "<side>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/trade/estimate/buy_limit", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>", "order_type": "<order_type>", "side": "<side>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/trade/estimate/buy_limit") + url.searchParams.set("symbol", "<symbol>") + url.searchParams.set("order_type", "<order_type>") + url.searchParams.set("side", "<side>") - # Reply to an existing comment - longbridge topic create-reply 6993508780031016960 --body "Agreed!" --reply-to 7001234567890123456 - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - body - properties: - body: - type: string - description: | - The reply body content. + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; - Plain text only — HTML and Markdown are **not** rendered. - reply_to_id: - type: string - nullable: true - description: | - ID of the comment to reply to. + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/estimate/buy_limit?symbol=<symbol>&order_type=<order_type>&side=<side>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/trade/estimate/buy_limit") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>"), ("order_type", "<order_type>"), ("side", "<side>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> - Omit or set to `"0"` to post a top-level comment. - When set to a valid comment ID, the new comment will be treated as a reply to that comment. + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/estimate/buy_limit?symbol=<symbol>&order_type=<order_type>&side=<side>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/trade/estimate/buy_limit?symbol=<symbol>&order_type=<order_type>&side=<side>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: cash_max_qty + type: string + required: true + description: Cash available quantity, default value is empty string. + x-description-zh: 现金可买数量,默认为空字符串 + x-description-zh-hk: 現金可買數量,默認為空字符串 + - name: margin_max_qty + type: string + required: true + description: Margin available quantity, default value is empty string. + x-description-zh: 融资可买数量,默认为空字符串 + x-description-zh-hk: 融資可買數量,默認為空字符串 responses: '200': description: Successful response @@ -1876,92 +40387,193 @@ paths: code: 0 message: success data: - item: - id: '7001234567890123460' - topic_id: '6993508780031016960' - body: Great post! - reply_to_id: '0' - author: - member_id: '1000001' - name: Jane Doe - avatar: https://cdn.longbridge.com/avatars/1000001.jpg - images: [] - likes_count: 0 - comments_count: 0 - created_at: '1742002000' - '403': - description: Forbidden — the authenticated user has not opened a Longbridge account or does not hold assets. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '429': - description: Too Many Requests — rate limit exceeded for this user in this topic. Wait for the required interval before retrying. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + cash_max_qty: '100' + margin_max_qty: '100' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/trade/execution/history: + /v1/trade/execution/today: get: - operationId: list_history_executions - summary: Historical Execution - x-summary-zh: 历史成交查询 + operationId: today_executions + summary: Today Executions + x-summary-zh: 当日成交明细 + x-summary-zh-hk: 當日成交明細 description: | - Query historical execution (fill) records. Supports filtering by time range, order ID, - and symbol, with pagination. - x-description-zh: 查询历史成交(成交明细)记录,支持按时间范围、订单 ID 和标的筛选,并支持分页。 + This API is used to get today executions. + x-description-zh: | + 该接口用于获取当日订单的成交明细。 + x-description-zh-hk: | + 該接口用於獲取當日訂單的成交明細。 + x-subgroup: Execution + x-subgroup-zh: 成交 + x-subgroup-zh-hk: 成交 tags: - - Trade Execution & Order Management - parameters: - - name: start_at - in: query - required: false - description: Query range start time as a Unix timestamp (seconds). - schema: - type: integer - nullable: true - - name: end_at - in: query - required: false - description: Query range end time as a Unix timestamp (seconds). - schema: - type: integer - nullable: true - - name: order_id - in: query - required: false - description: Filter by order ID. - schema: - type: string - nullable: true + - Trade + x-parameters: - name: symbol in: query + type: string required: false - description: 'Filter by security symbol (e.g. `NVDA.US`).' - schema: - type: string - nullable: true - maxLength: 32 - minLength: 4 - pattern: '^([\w.]*\.[A-Za-z][A-Za-z])$' - - name: page + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: order_id in: query + type: string required: false - description: Page number (1-based). - schema: - type: integer - nullable: true + description: 'Order ID, example: `701276261045858304`' + x-description-zh: 订单 ID,用于指定订单 ID 查询,例如:`701276261045858304` + x-description-zh-hk: 訂單 ID,用於指定訂單 ID 查詢,例如:`701276261045858304` x-codeSamples: - lang: Shell label: CLI source: | - longbridge executions --history + longbridge order executions + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/trade/execution/today' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/trade/execution/today", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/trade/execution/today", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/trade/execution/today", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/execution/today")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/trade/execution/today") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/execution/today"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/trade/execution/today\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: trades + type: object[] + required: false + description: Execution Detail + x-description-zh: 成交明细信息 + x-description-zh-hk: 成交明細信息 + - name: └ trade_id + type: string + required: false + description: Execution ID + x-description-zh: 成交 ID + x-description-zh-hk: 成交 ID + - name: └ order_id + type: string + required: false + description: Order ID + x-description-zh: 订单 ID + x-description-zh-hk: 訂單 ID + - name: └ symbol + type: string + required: false + description: 'Stock symbol, use `ticker.region` format,example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: └ price + type: string + required: false + description: Executed price + x-description-zh: 成交价格 + x-description-zh-hk: 成交價格 + - name: └ quantity + type: string + required: false + description: Executed quantity + x-description-zh: 成交数量 + x-description-zh-hk: 成交數量 + - name: └ trade_done_at + type: string + required: false + description: Trade done time, formatted as a timestamp (second) + x-description-zh: 成交时间,格式为时间戳 (秒) + x-description-zh-hk: 成交時間,格式為時間戳 (秒) + - name: └ side + type: string + required: false + description: Trade side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: has_more + type: boolean + required: false + description: has more orders record. The maximum number of orders per query is 1000, if the number of results exceeds 1000, then has_more will be true + x-description-zh: 是否还有更多数据。. 每次查询最大订单数量为 1000,如果查询结果数量超过 1000,那么 has_more 就会为 true + x-description-zh-hk: 是否還有更多數據。. 每次查詢最大訂單數量為 1000,如果查詢結果數量超過 1000,那麼 has_more 就會為 true responses: '200': description: Successful response @@ -1972,41 +40584,205 @@ paths: message: success data: trades: - - trade_id: '218942971868942337' - order_id: '218942971868942336' - symbol: NVDA.US - price: '177.50' - quantity: '10' - trade_done_at: '1774330500' - has_more: false + - order_id: '693664675163312128' + price: '388' + quantity: '100' + side: Buy + symbol: 700.HK + trade_done_at: '1648611351' + trade_id: 693664675163312128-1648611351433741210 default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/trade/order: + /v1/trade/execution/history: get: - operationId: order_detail - summary: Order Detail - x-summary-zh: 订单详情查询 + operationId: history_executions + summary: History Executions + x-summary-zh: 历史成交明细 + x-summary-zh-hk: 歷史成交明細 description: | - Query full details of a single order by order ID, including fee breakdown and status history. - x-description-zh: 通过订单 ID 查询单笔订单的完整详情,包括费用明细和状态变更历史。 + This API is used to get history executions, including the sell and buy records, and does not support querying today's execution details. + x-description-zh: | + 该接口用于获取历史订单的成交明细,包括买入和卖出的成交记录,不支持当日成交明细查询。 + x-description-zh-hk: | + 該接口用於獲取歷史訂單的成交明細,包括買入和賣出的成交記錄,不支持當日成交明細查詢。 + x-subgroup: Execution + x-subgroup-zh: 成交 + x-subgroup-zh-hk: 成交 tags: - - Trade Execution & Order Management - parameters: - - name: order_id + - Trade + x-parameters: + - name: symbol in: query - required: true - description: Order ID to query. - schema: - type: string + type: string + required: false + description: 'Stock symbol, use `ticker.region` format, example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: start_at + in: query + type: string + required: false + description: 'Start time, formatted as a timestamp (second), example: `1650410999`. If the start time is null, the default is the 90 days before of the end time or 90 days before of the current time' + x-description-zh: 开始时间,格式为时间戳 (秒),例如:`1650410999`。. 开始时间为空时,默认为结束时间或当前时间前九十天。 + x-description-zh-hk: 開始時間,格式為時間戳 (秒),例如:`1650410999`。. 開始時間為空時,默認為結束時間或當前時間前九十天。 + - name: end_at + in: query + type: string + required: false + description: 'End time, formatted as a timestamp (second), example: `1650410999`. If the end time is null, the default is the current time or 90 days after of the start time' + x-description-zh: 结束时间,格式为时间戳 (秒),例如:`1650410999`。. 结束时间为空时,默认为开始时间后九十天或当前时间。 + x-description-zh-hk: 結束時間,格式為時間戳 (秒),例如:`1650410999`。. 結束時間為空時,默認為開始時間後九十天或當前時間。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge order <ORDER_ID> + longbridge order executions --history + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/trade/execution/history' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/trade/execution/history", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/trade/execution/history", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/trade/execution/history", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/trade/execution/history")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/trade/execution/history") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/trade/execution/history"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/trade/execution/history\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: trades + type: object[] + required: false + description: Execution Detail + x-description-zh: 成交明细信息 + x-description-zh-hk: 成交明細信息 + - name: └ trade_id + type: string + required: false + description: Execution ID + x-description-zh: 成交 ID + x-description-zh-hk: 成交 ID + - name: └ order_id + type: string + required: false + description: Order ID + x-description-zh: 订单 ID + x-description-zh-hk: 訂單 ID + - name: └ symbol + type: string + required: false + description: 'Stock symbol, use `ticker.region` format,example: `AAPL.US`' + x-description-zh: 股票代码,使用 `ticker.region` 格式,例如:`AAPL.US` + x-description-zh-hk: 股票代碼,使用 `ticker.region` 格式,例如:`AAPL.US` + - name: └ price + type: string + required: false + description: Executed price + x-description-zh: 成交价格 + x-description-zh-hk: 成交價格 + - name: └ quantity + type: string + required: false + description: Executed quantity + x-description-zh: 成交数量 + x-description-zh-hk: 成交數量 + - name: └ trade_done_at + type: string + required: false + description: Trade done time, formatted as a timestamp (second) + x-description-zh: 成交时间,格式为时间戳 (秒) + x-description-zh-hk: 成交時間,格式為時間戳 (秒) + - name: └ side + type: string + required: false + description: Trade side. **Enum Value:**, `Buy`, `Sell` + x-description-zh: 买卖方向。**可选值:**, `Buy` - 买入,`Sell` - 卖出 + x-description-zh-hk: 買賣方向。**可選值:**, `Buy` - 買入,`Sell` - 賣出 + - name: has_more + type: boolean + required: false + description: has more orders record. The maximum number of orders per query is 1000, if the number of results exceeds 1000, then has_more will be true + x-description-zh: 是否还有更多数据。. 每次查询最大订单数量为 1000,如果查询结果数量超过 1000,那么 has_more 就会为 true + x-description-zh-hk: 是否還有更多數據。. 每次查詢最大訂單數量為 1000,如果查詢結果數量超過 1000,那麼 has_more 就會為 true responses: '200': description: Successful response @@ -2016,283 +40792,674 @@ paths: code: 0 message: success data: - order_id: '1220968184320942080' - status: CanceledStatus - stock_name: 英伟达 - quantity: '10' - executed_quantity: '0' - price: '' - executed_price: '0' - submitted_at: '1774330299' - side: Buy - symbol: NVDA.US - order_type: MIT - last_done: '' - trigger_price: '177.88' - msg: '' - tag: Normal - time_in_force: Day - expire_date: '2026-03-24' - updated_at: '1774330401' - trigger_at: '0' - trailing_amount: '' - trailing_percent: '' - limit_offset: '' - trigger_status: DEACTIVE - outside_rth: RTH_ONLY - currency: USD - remark: '' - limit_depth_level: 0 - trigger_count: 0 - monitor_price: '' - free_status: None - free_amount: '' - free_currency: '' - deductions_status: NONE - deductions_amount: '' - deductions_currency: '' - platform_deducted_status: NONE - platform_deducted_amount: '' - platform_deducted_currency: '' - history: [] - charge_detail: - items: - - code: BROKER_FEES - name: 收费明细 - fees: [] - total_amount: '0.00' - currency: USD + has_more: false + trades: + - order_id: '693664675163312128' + price: '388' + quantity: '100' + side: Buy + symbol: 700.HK + trade_done_at: '1648611351' + trade_id: 693664675163312128-1648611351433741210 default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - put: - operationId: replace_order - summary: Modify Order - x-summary-zh: 修改订单 + /v1/us/orders/{order_id}: + get: + operationId: us_order_detail + summary: US Order Detail + x-summary-zh: 美股委托详情 + x-summary-zh-hk: 美股委託詳情 description: | - Modify the parameters of a pending order. Only orders in `NewStatus` or `PartialFilledStatus` - can be modified. - x-description-zh: 修改待成交订单的参数。仅 `NewStatus`(已报)或 `PartialFilledStatus`(部分成交)状态的订单可修改。 - tags: - - Trade Execution & Order Management - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - order_id - properties: - order_id: - type: string - description: ID of the order to modify (required). - quantity: - type: string - nullable: true - description: New quantity. - pattern: '^([1-9]\d*(\.\d+)?)$' - price: - type: string - nullable: true - description: New limit price. - pattern: '^(0\.\d*[1-9]\d*|[1-9]\d*(\.\d+)?)$' - trigger_price: - type: string - nullable: true - description: New trigger price (for MIT/LIT orders). - pattern: '^(0\.\d*[1-9]\d*|[1-9]\d*(\.\d+)?)$' - limit_offset: - type: string - nullable: true - description: New limit offset (for LIT orders). - pattern: '^(\d*)$|^([0-9]\d*\.?\d*[1-9])$' - trailing_amount: - type: string - nullable: true - description: New trailing amount (for TSLPAMT/TSMAMT orders). - pattern: '^(0\.\d*[1-9]\d*|[1-9]\d*(\.\d+)?)$' - trailing_percent: - type: string - nullable: true - description: New trailing percentage (for TSMPCT/TSLPPCT orders). - pattern: '^(0\.\d*[1-9]\d*|[1-9]\d*(\.\d+)?)$' - limit_depth_level: - type: integer - nullable: true - description: New limit depth level (for ELO orders). - trigger_count: - type: integer - nullable: true - description: New trigger count. - monitor_price: - type: string - nullable: true - description: New monitor price. - remark: - type: string - nullable: true - maxLength: 255 - description: Order remark (max 255 characters). + :::warning Longbridge US Accounts + This method is only available for Longbridge US data-center accounts. + ::: + + It is **not** available to accounts in other data centers (such as HK or SG), even when those accounts can trade US symbols. It is also **not** available to paper accounts (`enable_papertrading = true`): **the Longbridge US desk (US DC) does not provide paper accounts at all**, so the entire US region — every US-specific API, not just this one — is unavailable in a paper environment. + + Calling it from an unsupported account returns an error rather than an empty result — do not treat the failure as "this order does not exist". For a paper environment, use an AP account with the generic trade APIs instead. + + Get detail for a specific US order — execution history, order status, and any attached child orders. + x-description-zh: | + :::warning Longbridge US 账户 + 此方法仅适用于 Longbridge 美国数据中心账户。 + ::: + + 其他数据中心的账户(如香港、新加坡)**不支持**该方法,即便这些账户可以交易美股;模拟账户(`enable_papertrading = true`)同样**不支持**:**Longbridge US 柜台(US DC)未提供模拟账户功能**,因此整个 US region——所有美股专用接口,而不只是本接口——在模拟环境下均不可用。 + + 使用不受支持的账户调用时会返回错误,而不是空结果——请勿将该失败理解为“此委托不存在”。如需模拟环境,请改用 AP 账户与通用交易接口。 + + 获取美股指定委托的详情,包括成交历史,可选获取关联子委托。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶 + 此方法僅適用於 Longbridge 美國數據中心賬戶。 + ::: + + 其他數據中心的賬戶(如香港、新加坡)**不支持**該方法,即便這些賬戶可以交易美股;模擬賬戶(`enable_papertrading = true`)同樣**不支持**:**Longbridge US 櫃檯(US DC)未提供模擬賬戶功能**,因此整個 US region——所有美股專用接口,而不只是本接口——在模擬環境下均不可用。 + + 使用不受支持的賬戶調用時會返回錯誤,而不是空結果——請勿將該失敗理解為「此委託不存在」。如需模擬環境,請改用 AP 賬戶與通用交易接口。 + + 獲取美股指定委託的詳情,包括成交歷史,可選獲取關聯子委託。 + x-subgroup: Order + x-subgroup-zh: 订单 + x-subgroup-zh-hk: 訂單 + tags: + - Trade + x-parameters: + - name: order_id + in: path + type: string + required: true + description: Order ID + x-description-zh: 委托 ID + x-description-zh-hk: 委託 ID + x-codeSamples: + - lang: Shell + label: CLI + source: | + # View US order detail + longbridge order detail 701276261045858304 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/us/orders/<order_id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/us/orders/<order_id>", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/us/orders/<order_id>", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/us/orders/<order_id>", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/us/orders/<order_id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/us/orders/<order_id>") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/us/orders/<order_id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/us/orders/<order_id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" responses: '200': description: Successful response content: application/json: example: - code: 0 - message: success - data: {} + order: + id: '701276261045858304' + symbol: AAPL.US + action: Buy + order_type: LO + status: Filled + price: '185.00' + quantity: '10' + executed_qty: '10' + executed_price: '184.95' + executed_amount: '1849.50' + currency: USD + submitted_at: '1751866334' + done_at: '1751866400' + time_in_force: 0 + msg: '' + current_attached_order: null + current_millisecond: '1751866400000' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - delete: - operationId: cancel_order - summary: Cancel Order - x-summary-zh: 撤销订单 + /v1/gridtrading/detail: + get: + operationId: grid_detail + summary: Grid Order Detail + x-summary-zh: 网格订单详情 + x-summary-zh-hk: 網格訂單詳情 description: | - Cancel a pending order. Only orders in `NewStatus` or `PartialFilledStatus` can be cancelled. - After successful cancellation the order status changes to `CanceledStatus`. - x-description-zh: 撤销待成交订单。仅 `NewStatus`(已报)或 `PartialFilledStatus`(部分成交)状态的订单可撤销,撤销成功后订单状态变更为 `CanceledStatus`(已撤销)。 + Query the full detail of a single grid order, including its rule, paged executed sub-orders (`grid_sub_orders`), and paged lifecycle history (`grid_order_history`). + x-description-zh: | + 查询单个网格订单的完整详情,包括网格规则、分页的成交子订单(`grid_sub_orders`)以及分页的生命周期历史(`grid_order_history`)。 + x-description-zh-hk: | + 查詢單個網格訂單的完整詳情,包括網格規則、分頁的成交子訂單(`grid_sub_orders`)以及分頁的生命週期歷史(`grid_order_history`)。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 tags: - - Trade Execution & Order Management - parameters: + - Trade + x-parameters: - name: order_id in: query + type: string + required: true + description: 'Grid order ID, example: `764609681686573056`' + x-description-zh: 网格订单 ID,例如:`764609681686573056` + x-description-zh-hk: 網格訂單 ID,例如:`764609681686573056` + - name: history_id + in: query + type: string + required: false + description: Cursor for paging the lifecycle history. Pass the last `history_id` of a page to fetch older entries. + x-description-zh: 生命周期历史的分页游标,传入某页最后一条的 `history_id` 以获取更早的记录 + x-description-zh-hk: 生命週期歷史的分頁遊標,傳入某頁最後一條的 `history_id` 以獲取更早的記錄 + - name: limit + in: query + type: integer + required: false + description: Page size for `grid_sub_orders` and `grid_order_history` + x-description-zh: '`grid_sub_orders` 与 `grid_order_history` 的分页大小' + x-description-zh-hk: '`grid_sub_orders` 與 `grid_order_history` 的分頁大小' + x-codeSamples: + - lang: Shell + label: CLI + source: | + # Rule, sub-orders and history of a grid order + longbridge grid detail 764609681686573056 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/gridtrading/detail?order_id=<order_id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/gridtrading/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"order_id": "<order_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/gridtrading/detail", + headers={"Authorization": "Bearer <access_token>"}, + params={"order_id": "<order_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/gridtrading/detail") + url.searchParams.set("order_id", "<order_id>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/detail?order_id=<order_id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/gridtrading/detail") + .header("Authorization", "Bearer <access_token>") + .query(&[("order_id", "<order_id>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/detail?order_id=<order_id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/gridtrading/detail?order_id=<order_id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: order_id + type: string + required: true + description: Grid order ID + x-description-zh: 网格订单 ID + x-description-zh-hk: 網格訂單 ID + - name: symbol + type: string + required: true + description: Security symbol, `ticker.region` format + x-description-zh: 标的代码,`ticker.region` 格式 + x-description-zh-hk: 標的代碼,`ticker.region` 格式 + - name: stock_name + type: string + required: true + description: Security name + x-description-zh: 标的名称 + x-description-zh-hk: 標的名稱 + - name: status + type: string + required: true + description: 'Grid order status, example: `Performing` / `Suspended`' + x-description-zh: 网格订单状态,例如:`Performing` / `Suspended` + x-description-zh-hk: 網格訂單狀態,例如:`Performing` / `Suspended` + - name: grid_status + type: string + required: true + description: Detailed grid running status + x-description-zh: 网格详细运行状态 + x-description-zh-hk: 網格詳細運行狀態 + - name: suspend_reason + type: string + required: true + description: Reason the grid is suspended, empty when running + x-description-zh: 网格被暂停的原因,运行中时为空 + x-description-zh-hk: 網格被暫停的原因,運行中時為空 + - name: sleeping_reason + type: string + required: true + description: Reason the grid is dormant, empty when active + x-description-zh: 网格休眠的原因,活跃时为空 + x-description-zh-hk: 網格休眠的原因,活躍時為空 + - name: submitted_base_price + type: string + required: true + description: Base price the grid was anchored to at submission + x-description-zh: 提交时网格锚定的基准价 + x-description-zh-hk: 提交時網格錨定的基準價 + - name: current_base_price + type: string + required: true + description: Current base price after triggers + x-description-zh: 触发后的当前基准价 + x-description-zh-hk: 觸發後的當前基準價 + - name: upper_limit_price + type: string + required: true + description: Upper price bound + x-description-zh: 价格上界 + x-description-zh-hk: 價格上界 + - name: lower_limit_price + type: string + required: true + description: Lower price bound + x-description-zh: 价格下界 + x-description-zh-hk: 價格下界 + - name: trigger_price_type + type: int32 + required: true + description: How trigger thresholds are interpreted. **Enum Value:**, `1` - spread (absolute), `2` - percent + x-description-zh: 触发阈值的计价方式。**枚举值:**, `1` - 价差(绝对值), `2` - 百分比 + x-description-zh-hk: 觸發閾值的計價方式。**枚舉值:**, `1` - 價差(絕對值), `2` - 百分比 + - name: trigger_spread_up + type: string + required: true + description: Upward trigger spread (when `trigger_price_type` is `1`) + x-description-zh: 向上触发价差(当 `trigger_price_type` 为 `1` 时) + x-description-zh-hk: 向上觸發價差(當 `trigger_price_type` 為 `1` 時) + - name: trigger_spread_down + type: string + required: true + description: Downward trigger spread (when `trigger_price_type` is `1`) + x-description-zh: 向下触发价差(当 `trigger_price_type` 为 `1` 时) + x-description-zh-hk: 向下觸發價差(當 `trigger_price_type` 為 `1` 時) + - name: trigger_percent_up + type: string + required: true + description: Upward trigger percent (when `trigger_price_type` is `2`) + x-description-zh: 向上触发百分比(当 `trigger_price_type` 为 `2` 时) + x-description-zh-hk: 向上觸發百分比(當 `trigger_price_type` 為 `2` 時) + - name: trigger_percent_down + type: string + required: true + description: Downward trigger percent (when `trigger_price_type` is `2`) + x-description-zh: 向下触发百分比(当 `trigger_price_type` 为 `2` 时) + x-description-zh-hk: 向下觸發百分比(當 `trigger_price_type` 為 `2` 時) + - name: pullback_percent + type: string required: true - description: ID of the order to cancel. - schema: - type: string - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: {} - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/trade/estimate/buy_limit: - get: - operationId: estimate_max_buy_quantity - summary: Estimate Maximum Buy Quantity - x-summary-zh: 估算最大可买数量 - description: | - Estimate the maximum purchasable quantity for a security based on the current account - balance and margin, given a specific order type and price. - x-description-zh: 根据当前账户余额和保证金,在指定订单类型和价格条件下,估算某只证券的最大可买数量。 - tags: - - Trade Execution & Order Management - parameters: - - name: symbol - in: query + description: Pullback percent + x-description-zh: 回落百分比 + x-description-zh-hk: 回落百分比 + - name: pullback_spread + type: string required: true - description: 'Security symbol (e.g. `AAPL.US`, `700.HK`).' - schema: - type: string - maxLength: 32 - minLength: 4 - pattern: '^([\w.]*\.[A-Za-z][A-Za-z])$' - - name: order_type - in: query + description: Pullback spread + x-description-zh: 回落价差 + x-description-zh-hk: 回落價差 + - name: rebound_percent + type: string required: true - description: | - Order type. One of: - `LO` (Limit), `ELO` (Enhanced Limit), `MO` (Market), `LIT` (Limit If Touched), - `MIT` (Market If Touched), `TSLPAMT` (Trailing Stop Limit by Amount), - `TSMAMT` (Trailing Stop Market by Amount), `TSMPCT` (Trailing Stop Market by Percent), - `TSLPPCT` (Trailing Stop Limit by Percent), `AO` (Auction), `ALO` (Auction Limit), - `ODD` (Odd Lot), `SLO` (Special Limit). - schema: - type: string - enum: - - LO - - ELO - - MO - - LIT - - MIT - - TSLPAMT - - TSMAMT - - TSMPCT - - UnknownOrderType - - AO - - ALO - - ODD - - TSLPPCT - - SLO - - name: side - in: query + description: Rebound percent + x-description-zh: 反弹百分比 + x-description-zh-hk: 反彈百分比 + - name: rebound_spread + type: string required: true - description: Order side. One of `Buy`, `Sell`. - schema: - type: string - enum: - - UnknownSide - - Buy - - Sell - - name: price - in: query - required: false - description: Limit price. Required for limit order types (e.g. `LO`, `LIT`). - schema: - type: string - nullable: true - - name: currency - in: query - required: false - description: Settlement currency override (e.g. `USD`, `HKD`). - schema: - type: string - nullable: true - - name: market - in: query + description: Rebound spread + x-description-zh: 反弹价差 + x-description-zh-hk: 反彈價差 + - name: multiple_trigger + type: boolean + required: true + description: Whether a single grid level may trigger multiple times + x-description-zh: 单个网格档位是否可多次触发 + x-description-zh-hk: 單個網格檔位是否可多次觸發 + - name: time_in_force + type: int32 + required: true + description: Time in force. **Enum Value:**, `0` - Day, `1` - GTC, `6` - GTD + x-description-zh: 订单有效期。**枚举值:**, `0` - 当日有效,`1` - GTC, `6` - GTD + x-description-zh-hk: 訂單有效期。**枚舉值:**, `0` - 當日有效,`1` - GTC, `6` - GTD + - name: trigger_quantity + type: string + required: true + description: Quantity per trigger + x-description-zh: 每次触发的数量 + x-description-zh-hk: 每次觸發的數量 + - name: trigger_sell_quantity + type: string + required: true + description: Cumulative quantity triggered on the sell side + x-description-zh: 卖出方向累计触发数量 + x-description-zh-hk: 賣出方向累計觸發數量 + - name: trigger_buy_quantity + type: string + required: true + description: Cumulative quantity triggered on the buy side + x-description-zh: 买入方向累计触发数量 + x-description-zh-hk: 買入方向累計觸發數量 + - name: upper_limit_quantity + type: string + required: true + description: Quantity handled when the upper bound is reached + x-description-zh: 触及上界时处理的数量 + x-description-zh-hk: 觸及上界時處理的數量 + - name: lower_limit_quantity + type: string + required: true + description: Quantity handled when the lower bound is reached + x-description-zh: 触及下界时处理的数量 + x-description-zh-hk: 觸及下界時處理的數量 + - name: upper_limit_event + type: int32 + required: true + description: Action at the upper bound. **Enum Value:**, `1` - ignore, `2` - close position at last price + x-description-zh: 触及上界时的动作。**枚举值:**, `1` - 忽略,`2` - 按最新价平仓 + x-description-zh-hk: 觸及上界時的動作。**枚舉值:**, `1` - 忽略,`2` - 按最新價平倉 + - name: lower_limit_event + type: int32 + required: true + description: Action at the lower bound. **Enum Value:**, `1` - ignore, `2` - close position at last price + x-description-zh: 触及下界时的动作。**枚举值:**, `1` - 忽略,`2` - 按最新价平仓 + x-description-zh-hk: 觸及下界時的動作。**枚舉值:**, `1` - 忽略,`2` - 按最新價平倉 + - name: trigger_sell_depth + type: int32 + required: true + description: Sell-side order-book depth (-5 ~ 5). `0` = use `grid_order_type_up` + x-description-zh: 卖出方向盘口深度(-5 ~ 5)。`0` = 使用 `grid_order_type_up` + x-description-zh-hk: 賣出方向盤口深度(-5 ~ 5)。`0` = 使用 `grid_order_type_up` + - name: trigger_buy_depth + type: int32 + required: true + description: Buy-side order-book depth (-5 ~ 5). `0` = use `grid_order_type_down` + x-description-zh: 买入方向盘口深度(-5 ~ 5)。`0` = 使用 `grid_order_type_down` + x-description-zh-hk: 買入方向盤口深度(-5 ~ 5)。`0` = 使用 `grid_order_type_down` + - name: created_at + type: string + required: true + description: Creation time, RFC3339 format + x-description-zh: 创建时间,RFC3339 格式 + x-description-zh-hk: 創建時間,RFC3339 格式 + - name: updated_at + type: string + required: true + description: Last update time, RFC3339 format + x-description-zh: 最后更新时间,RFC3339 格式 + x-description-zh-hk: 最後更新時間,RFC3339 格式 + - name: settlement_currency + type: string + required: true + description: 'Settlement currency, example: `HKD`' + x-description-zh: 结算货币,例如:`HKD` + x-description-zh-hk: 結算貨幣,例如:`HKD` + - name: expire_time + type: string + required: true + description: Expiry time, RFC3339 format (when `time_in_force` is GTD) + x-description-zh: 过期时间,RFC3339 格式(当 `time_in_force` 为 GTD 时) + x-description-zh-hk: 過期時間,RFC3339 格式(當 `time_in_force` 為 GTD 時) + - name: gtd + type: string + required: true + description: Good-Til-Date, `YYYY-MM-DD` format + x-description-zh: Good-Til-Date,`YYYY-MM-DD` 格式 + x-description-zh-hk: Good-Til-Date,`YYYY-MM-DD` 格式 + - name: grid_sub_orders + type: object[] required: false - description: Market override. One of `US`, `HK`, `SH`, `SZ`, `SG`, `AU`, `JP`, `UK`, `DE`. - schema: - type: string - nullable: true - enum: - - UnknownMarket - - DE - - JP - - SH - - SZ - - UK - - AU - - HK - - SG - - US - - name: fractional_shares - in: query - required: false - description: Whether to allow fractional share quantities in the estimate. - schema: - type: boolean - nullable: true - - name: order_id - in: query + description: Executed sub-orders for this grid + x-description-zh: 该网格已成交的子订单 + x-description-zh-hk: 該網格已成交的子訂單 + - name: └ id + type: string + required: true + description: Sub-order ID + x-description-zh: 子订单 ID + x-description-zh-hk: 子訂單 ID + - name: └ price + type: string + required: true + description: Order price + x-description-zh: 订单价格 + x-description-zh-hk: 訂單價格 + - name: └ order_type + type: string + required: true + description: Order type + x-description-zh: 订单类型 + x-description-zh-hk: 訂單類型 + - name: └ quantity + type: string + required: true + description: Order quantity + x-description-zh: 订单数量 + x-description-zh-hk: 訂單數量 + - name: └ executed_qty + type: string + required: true + description: Executed quantity + x-description-zh: 已成交数量 + x-description-zh-hk: 已成交數量 + - name: └ action + type: int32 + required: true + description: Buy/sell direction + x-description-zh: 买卖方向 + x-description-zh-hk: 買賣方向 + - name: └ status + type: string + required: true + description: Sub-order status + x-description-zh: 子订单状态 + x-description-zh-hk: 子訂單狀態 + - name: └ submitted_at + type: string + required: true + description: Submission time, RFC3339 format + x-description-zh: 提交时间,RFC3339 格式 + x-description-zh-hk: 提交時間,RFC3339 格式 + - name: └ rth + type: int32 + required: true + description: Regular-trading-hours flag (`0` / `1` / `2`) + x-description-zh: 盘中交易时段标识(`0` / `1` / `2`) + x-description-zh-hk: 盤中交易時段標識(`0` / `1` / `2`) + - name: sub_has_more + type: boolean + required: true + description: Whether more sub-orders can be paged + x-description-zh: 是否还有更多子订单可分页 + x-description-zh-hk: 是否還有更多子訂單可分頁 + - name: grid_order_history + type: object[] required: false - description: Original order ID when estimating for an order modification scenario. - schema: - type: string - nullable: true - x-codeSamples: - - lang: Shell - label: CLI - source: | - longbridge max-qty <SYMBOL> --side buy --price <PRICE> + description: Lifecycle history entries + x-description-zh: 生命周期历史记录 + x-description-zh-hk: 生命週期歷史記錄 + - name: └ history_id + type: string + required: true + description: History entry ID, also used as the paging cursor + x-description-zh: 历史记录 ID,同时用作分页游标 + x-description-zh-hk: 歷史記錄 ID,同時用作分頁遊標 + - name: └ created_at + type: string + required: true + description: Event time, RFC3339 format + x-description-zh: 事件时间,RFC3339 格式 + x-description-zh-hk: 事件時間,RFC3339 格式 + - name: └ suspend_reason + type: string + required: true + description: Suspend reason, when applicable + x-description-zh: 暂停原因(如适用) + x-description-zh-hk: 暫停原因(如適用) + - name: └ reason + type: string + required: true + description: Human-readable description of the event + x-description-zh: 事件的可读描述 + x-description-zh-hk: 事件的可讀描述 + - name: history_has_more + type: boolean + required: true + description: Whether more history entries can be paged via `history_id` + x-description-zh: 是否可通过 `history_id` 分页获取更多历史记录 + x-description-zh-hk: 是否可通過 `history_id` 分頁獲取更多歷史記錄 + - name: support_shortsell + type: boolean + required: true + description: Whether short selling is allowed + x-description-zh: 是否允许卖空 + x-description-zh-hk: 是否允許賣空 + - name: rth + type: int32 + required: true + description: Regular-trading-hours flag (`0` / `1` / `2`) + x-description-zh: 盘中交易时段标识(`0` / `1` / `2`) + x-description-zh-hk: 盤中交易時段標識(`0` / `1` / `2`) + - name: grid_order_type_up + type: string + required: true + description: Sell-side order type when `trigger_sell_depth` is `0`. **Enum Value:**, `GMO` / `GLO` / `GTG` + x-description-zh: '`trigger_sell_depth` 为 `0` 时的卖出方向订单类型。**枚举值:**, `GMO` / `GLO` / `GTG`' + x-description-zh-hk: '`trigger_sell_depth` 為 `0` 時的賣出方向訂單類型。**枚舉值:**, `GMO` / `GLO` / `GTG`' + - name: grid_order_type_down + type: string + required: true + description: Buy-side order type when `trigger_buy_depth` is `0`. **Enum Value:**, `GMO` / `GLO` / `GTG` + x-description-zh: '`trigger_buy_depth` 为 `0` 时的买入方向订单类型。**枚举值:**, `GMO` / `GLO` / `GTG`' + x-description-zh-hk: '`trigger_buy_depth` 為 `0` 時的買入方向訂單類型。**枚舉值:**, `GMO` / `GLO` / `GTG`' responses: '200': description: Successful response @@ -2302,126 +41469,344 @@ paths: code: 0 message: success data: - cash_max_qty: '0' - margin_max_qty: '1859' + order_id: '764609681686573056' + symbol: 700.HK + stock_name: TENCENT + status: Performing + grid_status: Performing + suspend_reason: '' + sleeping_reason: '' + submitted_base_price: '300' + current_base_price: '300' + upper_limit_price: '360' + lower_limit_price: '240' + trigger_price_type: 2 + trigger_spread_up: '' + trigger_spread_down: '' + trigger_percent_up: '2' + trigger_percent_down: '2' + pullback_percent: '' + pullback_spread: '' + rebound_percent: '' + rebound_spread: '' + multiple_trigger: false + time_in_force: 1 + trigger_quantity: '100' + trigger_sell_quantity: '0' + trigger_buy_quantity: '0' + upper_limit_quantity: '200' + lower_limit_quantity: '100' + upper_limit_event: 1 + lower_limit_event: 1 + trigger_sell_depth: 0 + trigger_buy_depth: 0 + created_at: '2024-08-12T10:30:00+08:00' + updated_at: '2024-08-12T10:30:00+08:00' + settlement_currency: HKD + expire_time: '' + gtd: '' + grid_sub_orders: [] + sub_has_more: false + grid_order_history: + - history_id: '764609681686573100' + created_at: '2024-08-12T10:30:00+08:00' + status: Performing + suspend_reason: '' + reason: Grid order started + history_has_more: false + support_shortsell: false + rth: 0 + grid_order_type_up: GMO + grid_order_type_down: GMO default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/trade/order/history: + /v1/gridtrading/trigger_history_list: get: - operationId: list_history_orders - summary: Historical Order - x-summary-zh: 历史订单查询 + operationId: grid_trigger_history + summary: Grid Trigger History + x-summary-zh: 网格触发历史 + x-summary-zh-hk: 網格觸發歷史 description: | - Query historical order records with pagination. Supports filtering by time range, symbol, - market, side, and status. - x-description-zh: 分页查询历史订单记录,支持按时间范围、标的、市场、方向和状态筛选。 + Query the paged trigger history of a single grid order — the individual orders the grid has placed each time a trigger fired. + + :::warning Uses `grid_order_id`, not `order_id` + This endpoint uses `grid_order_id`, **not** `order_id` like the other grid endpoints. Using `order_id` here will not resolve the grid order. + ::: + x-description-zh: | + 查询单个网格订单的分页触发历史 —— 网格每次触发时所发出的具体订单。 + + :::warning 使用 `grid_order_id` 而非 `order_id` + 该接口使用 `grid_order_id`,而**不是**其他网格接口所用的 `order_id`。此处传入 `order_id` 将无法定位到网格订单。 + ::: + x-description-zh-hk: | + 查詢單個網格訂單的分頁觸發歷史 —— 網格每次觸發時所發出的具體訂單。 + + :::warning 使用 `grid_order_id` 而非 `order_id` + 該接口使用 `grid_order_id`,而**不是**其他網格接口所用的 `order_id`。此處傳入 `order_id` 將無法定位到網格訂單。 + ::: + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 tags: - - Trade Execution & Order Management - parameters: - - name: start_at - in: query - required: false - description: Query range start time as a Unix timestamp (seconds). - schema: - type: integer - nullable: true - - name: end_at - in: query - required: false - description: Query range end time as a Unix timestamp (seconds). - schema: - type: integer - nullable: true - - name: symbol - in: query - required: false - description: 'Filter by security symbol (e.g. `AAPL.US`).' - schema: - type: string - nullable: true - maxLength: 32 - minLength: 4 - pattern: '^([\w.]*\.[A-Za-z][A-Za-z])$' - - name: market - in: query - required: false - description: Filter by market. One of `US`, `HK`, `SH`, `SZ`, `SG`, `AU`, `JP`, `UK`, `DE`. - schema: - type: string - nullable: true - enum: - - UnknownMarket - - AU - - DE - - HK - - SH - - SZ - - US - - JP - - SG - - UK - - name: side - in: query - required: false - description: Filter by order side. One of `Buy`, `Sell`. - schema: - type: string - nullable: true - enum: - - UnknownSide - - Buy - - Sell - - name: status + - Trade + x-parameters: + - name: grid_order_id in: query - required: false - description: Filter by one or more order statuses. - schema: - type: array - nullable: true - items: - type: string - enum: - - NotReported - - VarietiesNotReported - - FilledStatus - - WaitToNew - - ReplacedStatus - - PartialFilledStatus - - CanceledStatus - - ExpiredStatus - - UnknownOrderStatus - - RejectedStatus - - PartialWithdrawal - - ReplacedNotReported - - ProtectedNotReported - - NewStatus - - WaitToReplace - - PendingReplaceStatus - - PendingCancelStatus - - WaitToCancel + type: string + required: true + description: 'Grid order ID, example: `764609681686573056`' + x-description-zh: 网格订单 ID,例如:`764609681686573056` + x-description-zh-hk: 網格訂單 ID,例如:`764609681686573056` - name: page in: query + type: integer required: false - description: Page number (1-based). - schema: - type: integer - nullable: true - - name: size + description: Page number, starting from `1` + x-description-zh: 页码,从 `1` 开始 + x-description-zh-hk: 頁碼,從 `1` 開始 + - name: limit in: query + type: integer required: false - description: Number of records per page. - schema: - type: integer - nullable: true + description: Page size + x-description-zh: 分页大小 + x-description-zh-hk: 分頁大小 x-codeSamples: - lang: Shell label: CLI source: | - longbridge orders --history + # Trigger history of a grid order + longbridge grid triggers 764609681686573056 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/gridtrading/trigger_history_list?grid_order_id=<grid_order_id>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/gridtrading/trigger_history_list", + headers={"Authorization": "Bearer <access_token>"}, + params={"grid_order_id": "<grid_order_id>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/gridtrading/trigger_history_list", + headers={"Authorization": "Bearer <access_token>"}, + params={"grid_order_id": "<grid_order_id>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/gridtrading/trigger_history_list") + url.searchParams.set("grid_order_id", "<grid_order_id>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/gridtrading/trigger_history_list?grid_order_id=<grid_order_id>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/gridtrading/trigger_history_list") + .header("Authorization", "Bearer <access_token>") + .query(&[("grid_order_id", "<grid_order_id>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/gridtrading/trigger_history_list?grid_order_id=<grid_order_id>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/gridtrading/trigger_history_list?grid_order_id=<grid_order_id>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: trigger_orders + type: object[] + required: false + description: Triggered orders + x-description-zh: 触发订单 + x-description-zh-hk: 觸發訂單 + - name: └ id + type: string + required: true + description: Trigger order ID + x-description-zh: 触发订单 ID + x-description-zh-hk: 觸發訂單 ID + - name: └ status + type: string + required: true + description: Order status + x-description-zh: 订单状态 + x-description-zh-hk: 訂單狀態 + - name: └ name + type: string + required: true + description: Security name + x-description-zh: 股票名称 + x-description-zh-hk: 股票名稱 + - name: └ symbol + type: string + required: true + description: Security symbol, `ticker.region` format + x-description-zh: 股票代码,`ticker.region` 格式 + x-description-zh-hk: 股票代碼,`ticker.region` 格式 + - name: └ price + type: string + required: true + description: Order price + x-description-zh: 订单价格 + x-description-zh-hk: 訂單價格 + - name: └ quantity + type: string + required: true + description: Order quantity + x-description-zh: 订单数量 + x-description-zh-hk: 訂單數量 + - name: └ executed_price + type: string + required: true + description: Executed price + x-description-zh: 成交价格 + x-description-zh-hk: 成交價格 + - name: └ executed_qty + type: string + required: true + description: Executed quantity + x-description-zh: 已成交数量 + x-description-zh-hk: 已成交數量 + - name: └ submitted_at + type: string + required: true + description: Submission time, RFC3339 format + x-description-zh: 提交时间,RFC3339 格式 + x-description-zh-hk: 提交時間,RFC3339 格式 + - name: └ action + type: int32 + required: true + description: Buy/sell direction + x-description-zh: 买卖方向 + x-description-zh-hk: 買賣方向 + - name: └ order_type + type: string + required: true + description: Order type + x-description-zh: 订单类型 + x-description-zh-hk: 訂單類型 + - name: └ trigger_price + type: string + required: true + description: Price that triggered this order + x-description-zh: 触发该订单的价格 + x-description-zh-hk: 觸發該訂單的價格 + - name: └ msg + type: string + required: true + description: Additional message + x-description-zh: 附加消息 + x-description-zh-hk: 附加消息 + - name: └ currency + type: string + required: true + description: Order currency + x-description-zh: 订单货币 + x-description-zh-hk: 訂單貨幣 + - name: └ last_done + type: string + required: true + description: Last traded price at trigger time + x-description-zh: 触发时的最新成交价 + x-description-zh-hk: 觸發時的最新成交價 + - name: └ updated_at + type: string + required: true + description: Last update time, RFC3339 format + x-description-zh: 最后更新时间,RFC3339 格式 + x-description-zh-hk: 最後更新時間,RFC3339 格式 + - name: └ time_in_force + type: int32 + required: true + description: Time in force. **Enum Value:**, `0` - Day, `1` - GTC, `6` - GTD + x-description-zh: 订单有效期。**可选值:**, `0` - 当日有效,`1` - GTC, `6` - GTD + x-description-zh-hk: 訂單有效期。**可選值:**, `0` - 當日有效,`1` - GTC, `6` - GTD + - name: └ gtd + type: string + required: true + description: Good-Til-Date, `YYYY-MM-DD` format + x-description-zh: 到期日,`YYYY-MM-DD` 格式 + x-description-zh-hk: 到期日,`YYYY-MM-DD` 格式 + - name: └ trigger_at + type: string + required: true + description: Trigger time, RFC3339 format + x-description-zh: 触发时间,RFC3339 格式 + x-description-zh-hk: 觸發時間,RFC3339 格式 + - name: └ trigger_status + type: int32 + required: true + description: Trigger status + x-description-zh: 触发状态 + x-description-zh-hk: 觸發狀態 + - name: has_more + type: boolean + required: false + description: Whether more pages are available + x-description-zh: 是否还有更多分页 + x-description-zh-hk: 是否還有更多分頁 responses: '200': description: Successful response @@ -2431,36 +41816,27 @@ paths: code: 0 message: success data: - orders: - - order_id: '1220968184320942080' - status: FilledStatus - stock_name: 苹果 - quantity: '10' - executed_quantity: '10' - price: '200.00' - executed_price: '199.85' - submitted_at: '1770000000' - side: Buy - symbol: AAPL.US - order_type: LO - last_done: '199.85' - trigger_price: '' + trigger_orders: + - id: '764610000000000001' + status: Filled + name: TENCENT + symbol: 700.HK + price: '294' + quantity: '100' + executed_price: '294' + executed_qty: '100' + submitted_at: '2024-08-12T10:35:00+08:00' + action: 1 + order_type: GMO + trigger_price: '294' msg: '' - tag: Normal - time_in_force: Day - expire_date: '2026-02-01' - updated_at: '1770000600' - trigger_at: '0' - trailing_amount: '' - trailing_percent: '' - limit_offset: '' - trigger_status: DEACTIVE - outside_rth: RTH_ONLY - currency: USD - remark: '' - limit_depth_level: 0 - trigger_count: 0 - monitor_price: '' + currency: HKD + last_done: '294' + updated_at: '2024-08-12T10:35:05+08:00' + time_in_force: 1 + gtd: '' + trigger_at: '2024-08-12T10:35:00+08:00' + trigger_status: 1 has_more: false default: description: Unexpected error @@ -2468,39 +41844,209 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /v1/trade/execution/today: + /v1/orders/info: get: - operationId: list_today_executions - summary: Today's Execution - x-summary-zh: 当日成交查询 + operationId: grid_symbol_info + summary: Grid Symbol Info + x-summary-zh: 网格标的信息 + x-summary-zh-hk: 網格標的信息 description: | - Query today's execution (fill) records. Supports filtering by order ID and symbol. - x-description-zh: 查询当日成交(成交明细)记录,支持按订单 ID 和标的筛选。 + Fetch the grid-trading information for a security: lot size, last done price, price steps, and authorization status. Use it to build a valid grid rule before calling Submit Grid Order. The `channel_info.strategy_granted` field tells you whether the strategy risk-disclosure consent has already been recorded — if it is `false`, submit the Strategy Questionnaire first. + x-description-zh: | + 获取标的的网格交易信息:每手股数、最新成交价、价格步长以及授权状态。在调用提交网格订单之前,用它来构建合法的网格规则。`channel_info.strategy_granted` 字段告诉你是否已记录策略风险披露同意——若为 `false`,请先提交策略问卷。 + x-description-zh-hk: | + 獲取標的的網格交易信息:每手股數、最新成交價、價格步長以及授權狀態。在調用提交網格訂單之前,用它來構建合法的網格規則。`channel_info.strategy_granted` 欄位告訴你是否已記錄策略風險披露同意——若為 `false`,請先提交策略問卷。 + x-subgroup: Grid Trading + x-subgroup-zh: 网格交易 + x-subgroup-zh-hk: 網格交易 tags: - - Trade Execution & Order Management - parameters: - - name: order_id - in: query - required: false - description: Filter by order ID. - schema: - type: string - nullable: true + - Trade + x-parameters: - name: symbol in: query - required: false - description: 'Filter by security symbol (e.g. `NVDA.US`).' - schema: - type: string - nullable: true - maxLength: 32 - minLength: 4 - pattern: '^([\w.]*\.[A-Za-z][A-Za-z])$' + type: string + required: true + description: Security symbol in `ticker.region` format, e.g. `700.HK`. + x-description-zh: 标的代码,`ticker.region` 格式,如 `700.HK`。 + x-description-zh-hk: 標的代碼,`ticker.region` 格式,如 `700.HK`。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge executions + # Show the grid-trading info for 700.HK + longbridge grid info 700.HK + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/orders/info?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/orders/info", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/orders/info", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/orders/info") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/orders/info?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/orders/info") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/orders/info?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/orders/info?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: last_done + type: string + required: true + description: Latest traded price + x-description-zh: 最新成交价 + x-description-zh-hk: 最新成交價 + - name: lot_size + type: string + required: true + description: Board lot size + x-description-zh: 每手股数 + x-description-zh-hk: 每手股數 + - name: buy_lot_size + type: string + required: true + description: Minimum lot size for a buy order + x-description-zh: 买入最小手数 + x-description-zh-hk: 買入最小手數 + - name: sell_lot_size + type: string + required: true + description: Minimum lot size for a sell order + x-description-zh: 卖出最小手数 + x-description-zh-hk: 賣出最小手數 + - name: bid_sizes + type: object[] + required: true + description: Price-step tiers + x-description-zh: 价格步长档位 + x-description-zh-hk: 價格步長檔位 + - name: └ str_proceed + type: string + required: true + description: Start price of the tier + x-description-zh: 档位起始价格 + x-description-zh-hk: 檔位起始價格 + - name: └ end_proceed + type: string + required: true + description: End price of the tier + x-description-zh: 档位结束价格 + x-description-zh-hk: 檔位結束價格 + - name: └ bid_size + type: string + required: true + description: Minimum price step within the tier + x-description-zh: 档位内的最小价格步长 + x-description-zh-hk: 檔位內的最小價格步長 + - name: channel_info + type: object + required: true + description: Trading-channel info and authorization. `strategy_granted` indicates whether the one-time strategy risk-disclosure consent has been recorded + x-description-zh: 交易通道信息与授权状态。`strategy_granted` 表示是否已记录一次性策略风险披露同意 + x-description-zh-hk: 交易通道信息與授權狀態。`strategy_granted` 表示是否已記錄一次性策略風險披露同意 + - name: └ strategy_granted + type: boolean + required: true + description: Whether the one-time strategy risk-disclosure consent has been recorded + x-description-zh: 是否已记录一次性策略风险披露同意 + x-description-zh-hk: 是否已記錄一次性策略風險披露同意 + - name: └ support_rth + type: boolean + required: true + description: Whether the security supports regular-trading-hours grids + x-description-zh: 标的是否支持盘中交易时段网格 + x-description-zh-hk: 標的是否支持盤中交易時段網格 + - name: └ currency + type: string + required: true + description: Trading currency + x-description-zh: 交易货币 + x-description-zh-hk: 交易貨幣 + - name: └ settlement_currency + type: string[] + required: true + description: Supported settlement currencies + x-description-zh: 支持的结算货币 + x-description-zh-hk: 支持的結算貨幣 responses: '200': description: Successful response @@ -2510,209 +42056,303 @@ paths: code: 0 message: success data: - trades: - - trade_id: '218942971868942337' - order_id: '218942971868942336' - symbol: NVDA.US - price: '177.50' - quantity: '10' - trade_done_at: '1774330500' + name: TENCENT + last_done: '300.000' + lot_size: '100' + buy_lot_size: '100' + sell_lot_size: '100' + bid_sizes: + - str_proceed: '0.010' + end_proceed: '500.000' + bid_size: '0.200' + channel_info: + strategy_granted: true + support_rth: false + currency: HKD + settlement_currency: + - HKD default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/trade/order/today: + /v1/dailycoins/query: get: - operationId: list_today_orders - summary: Today's Order - x-summary-zh: 当日订单查询 + operationId: dca_list + summary: List DCA Plans + x-summary-zh: 获取定投列表 + x-summary-zh-hk: 獲取定投列表 description: | - Query today's orders with support for multi-condition filtering. - x-description-zh: 查询当日订单,支持多条件组合筛选。 + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Get all recurring investment (DCA) plans for the current user. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 获取当前用户的所有定投(DCA)计划。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 獲取當前用戶的所有定投(DCA)計劃。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 tags: - - Trade Execution & Order Management - parameters: - - name: symbol - in: query - required: false - description: 'Filter by security symbol (e.g. `NVDA.US`).' - schema: - type: string - nullable: true - maxLength: 32 - minLength: 4 - pattern: '^([\w.]*\.[A-Za-z][A-Za-z])$' - - name: market - in: query - required: false - description: Filter by market. One of `US`, `HK`, `SH`, `SZ`, `SG`, `AU`, `JP`, `UK`, `DE`. - schema: - type: string - nullable: true - enum: - - UnknownMarket - - DE - - SH - - US - - AU - - HK - - JP - - SZ - - SG - - UK - - name: side - in: query - required: false - description: Filter by order side. One of `Buy`, `Sell`. - schema: - type: string - nullable: true - enum: - - UnknownSide - - Buy - - Sell + - Account + x-parameters: - name: status in: query + type: string required: false - description: Filter by one or more order statuses. - schema: - type: array - nullable: true - items: - type: string - enum: - - UnknownOrderStatus - - VarietiesNotReported - - WaitToCancel - - NotReported - - ReplacedNotReported - - NewStatus - - PendingReplaceStatus - - RejectedStatus - - CanceledStatus - - PartialWithdrawal - - FilledStatus - - WaitToReplace - - PartialFilledStatus - - PendingCancelStatus - - ExpiredStatus - - ProtectedNotReported - - WaitToNew - - ReplacedStatus - - name: order_id - in: query - required: false - description: Filter by order ID. - schema: - type: string - nullable: true - - name: page - in: query - required: false - description: Page number (1-based). - schema: - type: integer - nullable: true - - name: size + description: 'Filter by plan status: `Active`, `Suspended`, `Finished`.' + x-description-zh: 按计划状态筛选:`Active`、`Suspended`、`Finished`。 + x-description-zh-hk: 按計劃狀態篩選:`Active`、`Suspended`、`Finished`。 + - name: symbol in: query + type: string required: false - description: Number of records per page. - schema: - type: integer - nullable: true + description: Filter by security symbol, e.g. `AAPL.US`. + x-description-zh: 按标的代码筛选,如 `AAPL.US`。 + x-description-zh-hk: 按標的代碼篩選,如 `AAPL.US`。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge orders - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: - orders: - - order_id: '1220968184320942080' - status: CanceledStatus - stock_name: 英伟达 - quantity: '10' - executed_quantity: '0' - price: '' - executed_price: '0' - submitted_at: '1774330299' - side: Buy - symbol: NVDA.US - order_type: MIT - last_done: '' - trigger_price: '177.88' - msg: '' - tag: Normal - time_in_force: Day - expire_date: '2026-03-24' - updated_at: '1774330401' - trigger_at: '0' - trailing_amount: '' - trailing_percent: '' - limit_offset: '' - trigger_status: DEACTIVE - outside_rth: RTH_ONLY - currency: USD - remark: '' - limit_depth_level: 0 - trigger_count: 0 - monitor_price: '' - has_more: false - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/gridtrading/submit: - post: - operationId: submit_grid_order - summary: Submit Grid Order - x-summary-zh: 提交网格订单 - description: | - Submit a grid strategy order. The grid places buy orders as the price falls and sell - orders as it rises, within the `[lower_limit_price, upper_limit_price]` band anchored to - `submitted_base_price`. Before using grid trading you must record the strategy - risk-disclosure consent once (see `POST /v1/record/questionnaire`). - x-description-zh: | - 提交网格策略订单。网格在 `[lower_limit_price, upper_limit_price]` 区间内、以 - `submitted_base_price` 为基准,价格下跌时挂买单、上涨时挂卖单。使用网格交易前需先记录一次策略风险揭示确认(见 - `POST /v1/record/questionnaire`)。 - tags: - - Grid Trading - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - symbol - - settlement_currency - - grid_trading_rule - properties: - symbol: - type: string - description: 'Security symbol in `ticker.region` format (e.g. `700.HK`).' - settlement_currency: - type: string - description: Settlement currency (e.g. `HKD`, `USD`). - grid_trading_rule: - $ref: '#/components/schemas/GridTradeRule' - x-codeSamples: + longbridge dca + longbridge dca --status Active + x-request-examples: - lang: Shell - label: CLI + label: cURL source: | - longbridge grid submit 700.HK --currency HKD --base-price 300 --upper-price 360 --lower-price 240 --trigger-type percent --trigger-up 2 --trigger-down 2 --quantity 100 --upper-quantity 200 --lower-quantity 100 --order-type GMO --tif gtc + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/dailycoins/query' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/dailycoins/query", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/dailycoins/query", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/query", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/query")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/dailycoins/query") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/query"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/dailycoins/query\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: plans + type: object[] + required: true + description: List of DCA plans + x-description-zh: 定投计划列表, + x-description-zh-hk: 定投計劃列表, + - name: └ plan_id + type: string + required: true + description: DCA plan ID + x-description-zh: 定投计划 ID + x-description-zh-hk: 定投計劃 ID + - name: └ symbol + type: string + required: true + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ stock_name + type: string + required: false + description: Security name + x-description-zh: 标的名称 + x-description-zh-hk: 標的名稱 + - name: └ market + type: string + required: false + description: Market + x-description-zh: 市场 + x-description-zh-hk: 市場 + - name: └ status + type: string + required: false + description: 'Plan status: `Active`, `Suspended`, `Finished`' + x-description-zh: 计划状态:`Active`(进行中)、`Suspended`(已暂停)、`Finished`(已结束) + x-description-zh-hk: 計劃狀態:`Active`(進行中)、`Suspended`(已暫停)、`Finished`(已結束) + - name: └ per_invest_amount + type: string + required: false + description: Amount per investment + x-description-zh: 每次投入金额 + x-description-zh-hk: 每次投入金額 + - name: └ invest_frequency + type: string + required: false + description: 'Frequency: `Daily`, `Weekly`, `Fortnightly`, `Monthly`' + x-description-zh: 投资频率:`Daily`、`Weekly`、`Fortnightly`、`Monthly` + x-description-zh-hk: 投資頻率:`Daily`、`Weekly`、`Fortnightly`、`Monthly` + - name: └ invest_day_of_week + type: string + required: false + description: Day of week for weekly plans + x-description-zh: 每周扣款日 + x-description-zh-hk: 每週扣款日 + - name: └ invest_day_of_month + type: string + required: false + description: Day of month for monthly plans + x-description-zh: 每月扣款日 + x-description-zh-hk: 每月扣款日 + - name: └ next_trd_date + type: string + required: false + description: Next trade date + x-description-zh: 下次交易日 + x-description-zh-hk: 下次交易日 + - name: └ cum_amount + type: string + required: false + description: Cumulative invested amount + x-description-zh: 累计投入金额 + x-description-zh-hk: 累計投入金額 + - name: └ cum_profit + type: string + required: false + description: Cumulative profit/loss + x-description-zh: 累计盈亏 + x-description-zh-hk: 累計盈虧 + - name: └ average_cost + type: string + required: false + description: Average cost per share + x-description-zh: 平均持仓成本 + x-description-zh-hk: 平均持倉成本 + - name: └ allow_margin_finance + type: boolean + required: false + description: Whether margin financing is allowed + x-description-zh: 是否允许融资 + x-description-zh-hk: 是否允許融資 + - name: └ alter_hours + type: string + required: false + description: Reminder hours before trade + x-description-zh: 提前提醒小时数 + x-description-zh-hk: 提前提醒小時數 + - name: └ display_account + type: string + required: false + description: Account display name + x-description-zh: 账户显示名称 + x-description-zh-hk: 賬戶顯示名稱 + - name: └ account_channel + type: string + required: false + description: Account channel + x-description-zh: 账户渠道 + x-description-zh-hk: 賬戶渠道 + - name: └ aaid + type: string + required: false + description: Account asset ID + x-description-zh: 账户资产 ID + x-description-zh-hk: 賬戶資產 ID + - name: └ member_id + type: string + required: false + description: Member ID + x-description-zh: 用户 ID + x-description-zh-hk: 用戶 ID + - name: └ issue_number + type: string + required: false + description: Execution count + x-description-zh: 已执行次数 + x-description-zh-hk: 已執行次數 + - name: └ created_at + type: string + required: false + description: Creation timestamp + x-description-zh: 创建时间 + x-description-zh-hk: 創建時間 + - name: └ updated_at + type: string + required: false + description: Last update timestamp + x-description-zh: 最后更新时间 + x-description-zh-hk: 最後更新時間 responses: '200': description: Successful response @@ -2722,124 +42362,259 @@ paths: code: 0 message: success data: - order_id: '764609681686573056' + plans: + - plan_id: '1239402174908207104' + symbol: AAPL.US + stock_name: Apple Inc. + market: US + status: Active + per_invest_amount: '100' + invest_frequency: Monthly + invest_day_of_month: '15' + invest_day_of_week: '' + next_trd_date: '1778853600' + cum_amount: '0' + cum_profit: '0' + average_cost: '0' + allow_margin_finance: false + alter_hours: '6' + display_account: LBPT10065023 + member_id: '3162' + aaid: '20975338' + account_channel: lb_papertrading + issue_number: 0 + created_at: '1778725628' + updated_at: '1778725628' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/gridtrading/replace: - post: - operationId: replace_grid_order - summary: Modify Grid Order - x-summary-zh: 修改网格订单 + /v1/dailycoins/query-records: + get: + operationId: dca_history + summary: DCA Trade History + x-summary-zh: 定投交易历史 + x-summary-zh-hk: 定投交易歷史 description: | - Modify the rule of an existing grid order. Provide the `order_id` and a full - `grid_trading_rule` with the new parameters. - x-description-zh: 修改已存在网格订单的规则。需提供 `order_id` 以及包含新参数的完整 `grid_trading_rule`。 + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Get the execution history for a specific DCA plan including trade dates, amounts, and prices. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 获取指定定投的执行历史,包含交易日期、金额和价格。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 獲取指定定投的執行歷史,包含交易日期、金額和價格。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 tags: - - Grid Trading - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - order_id - - grid_trading_rule - properties: - order_id: - type: string - description: ID of the grid order to modify. - grid_trading_rule: - $ref: '#/components/schemas/GridTradeRule' + - Account + x-parameters: + - name: plan_id + in: query + type: string + required: true + description: DCA plan ID. + x-description-zh: 定投计划 ID。 + x-description-zh-hk: 定投計劃 ID。 + - name: page + in: query + type: integer + required: true + description: Page number, starting from `1`. + x-description-zh: 页码,从 `1` 开始。 + x-description-zh-hk: 頁碼,從 `1` 開始。 + - name: size + in: query + type: integer + required: true + description: Page size (records per page). + x-description-zh: 每页记录数。 + x-description-zh-hk: 每頁記錄數。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid replace 764609681686573056 --base-price 305 --upper-price 360 --lower-price 240 --trigger-type percent --trigger-up 2 --trigger-down 2 --quantity 100 --upper-quantity 200 --lower-quantity 100 --order-type GMO --tif gtc - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: {} - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/gridtrading/list: - get: - operationId: list_grid_orders - summary: List Grid Orders - x-summary-zh: 网格订单列表 - description: | - Query grid orders with pagination and optional filtering by market, symbol, and status. - x-description-zh: 分页查询网格订单,支持按市场、标的和状态筛选。 - tags: - - Grid Trading - parameters: - - name: page - in: query + longbridge dca history 1225781523156889600 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/dailycoins/query-records?plan_id=<plan_id>&page=<page>&size=<size>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/dailycoins/query-records", + headers={"Authorization": "Bearer <access_token>"}, + params={"plan_id": "<plan_id>", "page": "<page>", "size": "<size>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/dailycoins/query-records", + headers={"Authorization": "Bearer <access_token>"}, + params={"plan_id": "<plan_id>", "page": "<page>", "size": "<size>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/dailycoins/query-records") + url.searchParams.set("plan_id", "<plan_id>") + url.searchParams.set("page", "<page>") + url.searchParams.set("size", "<size>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/query-records?plan_id=<plan_id>&page=<page>&size=<size>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/dailycoins/query-records") + .header("Authorization", "Bearer <access_token>") + .query(&[("plan_id", "<plan_id>"), ("page", "<page>"), ("size", "<size>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/query-records?plan_id=<plan_id>&page=<page>&size=<size>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/dailycoins/query-records?plan_id=<plan_id>&page=<page>&size=<size>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: records + type: object[] + required: true + description: List of execution records + x-description-zh: 执行记录列表, + x-description-zh-hk: 執行紀錄列表, + - name: └ symbol + type: string + required: true + description: Security symbol + x-description-zh: 证券代码 + x-description-zh-hk: 證券代碼 + - name: └ order_id + type: string required: false - description: Page number (starts at 1). - schema: - type: integer - nullable: true - - name: limit - in: query + description: Associated order ID + x-description-zh: 关联订单 ID + x-description-zh-hk: 關聯訂單 ID + - name: └ status + type: string required: false - description: Page size. - schema: - type: integer - nullable: true - - name: market - in: query + description: Execution status + x-description-zh: 执行状态 + x-description-zh-hk: 執行狀態 + - name: └ action + type: string + required: false + description: Action type (e.g. `buy`) + x-description-zh: 操作类型 + x-description-zh-hk: 操作類型 + - name: └ order_type + type: string + required: false + description: Order type (e.g. `market`) + x-description-zh: 订单类型 + x-description-zh-hk: 訂單類型 + - name: └ executed_qty + type: string + required: false + description: Executed quantity + x-description-zh: 成交数量 + x-description-zh-hk: 成交數量 + - name: └ executed_price + type: string required: false - description: Filter by market. One of `US`, `HK`, `CN`, `SG`. - schema: - type: string - nullable: true - - name: symbol - in: query + description: Executed price + x-description-zh: 成交价格 + x-description-zh-hk: 成交價格 + - name: └ executed_amount + type: string required: false - description: 'Filter by security symbol (e.g. `700.HK`).' - schema: - type: string - nullable: true - - name: status - in: query + description: Executed cost amount + x-description-zh: 成交金额 + x-description-zh-hk: 成交金額 + - name: └ rejected_reason + type: string required: false - description: 'Comma-joined status filter (e.g. `Performing,Suspended`).' - schema: - type: string - nullable: true - - name: sort_by - in: query + description: Rejection reason if failed + x-description-zh: 拒绝原因(如有) + x-description-zh-hk: 拒絕原因(如有) + - name: └ created_at + type: string required: false - description: Sort field. - schema: - type: string - nullable: true - - name: sort_order - in: query + description: Creation Unix timestamp + x-description-zh: 执行时间 + x-description-zh-hk: 執行時間 + - name: has_more + type: boolean required: false - description: Sort order. - schema: - type: string - nullable: true - x-codeSamples: - - lang: Shell - label: CLI - source: | - longbridge grid --symbol 700.HK --status Performing + description: Whether more records exist + x-description-zh: 是否有更多记录 + x-description-zh-hk: 是否有更多紀錄 responses: '200': description: Successful response @@ -2849,70 +42624,196 @@ paths: code: 0 message: success data: - grid_order: - - order_id: '764609681686573056' - symbol: 700.HK - stock_name: 腾讯控股 - market: HK - status: Performing - grid_status: Running - submitted_base_price: '300' - current_base_price: '300' - upper_limit_price: '360' - lower_limit_price: '240' - trigger_price_type: 2 - trigger_percent_up: '2' - trigger_percent_down: '2' - trigger_quantity: '100' - upper_limit_quantity: '200' - lower_limit_quantity: '100' - upper_limit_event: 1 - lower_limit_event: 1 - multiple_trigger: false - trigger_times: 0 - settlement_currency: HKD - time_in_force: 1 - gtd: '' - created_at: '2026-08-12T09:30:00+08:00' - rth: 0 - support_shortsell: false - grid_order_type_up: GMO - grid_order_type_down: GMO has_more: false + records: + - symbol: AAPL.US + order_id: '123456' + status: Filled + action: Buy + order_type: Market + executed_qty: '1' + executed_price: '180.50' + executed_amount: '180.50' + created_at: '1763769600' + rejected_reason: '' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - post: - operationId: list_grid_orders_by_ids - summary: Query Grid Orders by IDs - x-summary-zh: 按 ID 查询网格订单 + /v1/dailycoins/statistic: + get: + operationId: dca_stats + summary: DCA Statistics + x-summary-zh: 定投统计 + x-summary-zh-hk: 定投統計 description: | - Query specific grid orders by their IDs (request body). Returns the matching grid orders. - x-description-zh: 通过订单 ID(请求体)查询指定的网格订单,返回匹配的网格订单列表。 - tags: - - Grid Trading - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - order_ids - properties: - order_ids: - type: array - description: Grid order IDs to query. - items: - type: string + :::warning Not for Longbridge US Accounts + This method requires an AP data-center account (HK / SG). US data-center accounts will receive a region restriction error. AP accounts can call this method with any supported symbol, including US stocks. + ::: + + Get DCA statistics summary including total invested amount and profit/loss. + x-description-zh: | + :::warning Longbridge US 账户不支持 + 此方法需要 AP 数据中心账户(香港/新加坡)。美股数据中心账户将收到区域限制错误。AP 账户可操作任意标的,包括美股。 + ::: + + 获取定投统计汇总信息,包括总投入金额和盈亏情况。 + x-description-zh-hk: | + :::warning Longbridge US 賬戶不支援 + 此方法需要 AP 數據中心賬戶(香港/新加坡)。美股數據中心賬戶將收到區域限制錯誤。AP 賬戶可操作任意標的,包括美股。 + ::: + + 獲取定投統計匯總信息,包括總投入金額和盈虧情況。 + x-subgroup: DCA + x-subgroup-zh: 定投 + x-subgroup-zh-hk: 定投 + tags: + - Account + x-parameters: + - name: symbol + in: query + type: string + required: false + description: Filter statistics by security symbol. + x-description-zh: 按标的代码筛选统计。 + x-description-zh-hk: 按標的代碼篩選統計。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid --ids 764609681686573056 764609681686573057 + longbridge dca stats + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/dailycoins/statistic' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/dailycoins/statistic", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/dailycoins/statistic", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/dailycoins/statistic", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/dailycoins/statistic")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/dailycoins/statistic") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/dailycoins/statistic"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/dailycoins/statistic\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: active_count + type: string + required: false + description: Number of active plans + x-description-zh: 活跃计划数量 + x-description-zh-hk: 活躍計劃數量 + - name: finished_count + type: string + required: false + description: Number of finished plans + x-description-zh: 已完成计划数量 + x-description-zh-hk: 已完成計劃數量 + - name: suspended_count + type: string + required: false + description: Number of suspended plans + x-description-zh: 已暂停计划数量 + x-description-zh-hk: 已暫停計劃數量 + - name: rest_days + type: string + required: false + description: Days until next investment + x-description-zh: 距下次扣款天数 + x-description-zh-hk: 距下次扣款天數 + - name: total_amount + type: string + required: false + description: Total invested amount + x-description-zh: 总投入金额 + x-description-zh-hk: 總投入金額 + - name: total_profit + type: string + required: false + description: Total profit/loss + x-description-zh: 总盈亏 + x-description-zh-hk: 總盈虧 + - name: nearest_plans + type: object[] + required: false + description: Nearest upcoming DCA plans (same structure as DcaPlan) + x-description-zh: 最近即将执行的定投计划(结构与 DcaPlan 一致) + x-description-zh-hk: 最近即將執行的定投計劃(結構與 DcaPlan 一致) responses: '200': description: Successful response @@ -2922,67 +42823,190 @@ paths: code: 0 message: success data: - grid_order: - - order_id: '764609681686573056' - symbol: 700.HK - stock_name: 腾讯控股 - market: HK - status: Performing - grid_status: Running - submitted_base_price: '300' - upper_limit_price: '360' - lower_limit_price: '240' - trigger_price_type: 2 - trigger_percent_up: '2' - trigger_percent_down: '2' - trigger_quantity: '100' - settlement_currency: HKD - time_in_force: 1 - grid_order_type_up: GMO - grid_order_type_down: GMO + active_count: '2' + finished_count: '1' + suspended_count: '0' + rest_days: '3' + total_amount: '5400' + total_profit: '120.50' + nearest_plans: + - plan_id: '1239402174908207104' + symbol: AAPL.US + stock_name: Apple Inc. + market: US + status: Active + per_invest_amount: '100' + invest_frequency: Monthly + invest_day_of_month: '15' + next_trd_date: '1778853600' + cum_amount: '0' + cum_profit: '0' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/gridtrading/detail: + /v1/quote/get_security_list: get: - operationId: grid_order_detail - summary: Grid Order Detail - x-summary-zh: 网格订单详情 + operationId: list_securities + x-quote-command: security-list + summary: Query Tradable Securities List + x-summary-zh: 查询可交易证券列表 + x-summary-zh-hk: 查詢可交易證券列表 description: | - Query the full detail of a single grid order, including its rule, triggered sub-orders, - and lifecycle history (both paged via `history_id` / `limit`). - x-description-zh: 查询单个网格订单的完整详情,包括规则、已触发的子订单以及生命周期历史(均可通过 `history_id` / `limit` 分页)。 + Query the list of tradable securities filtered by market and category. Primarily used to + retrieve securities eligible for extended-hours (pre-market / after-hours) trading sessions. + Both `market` and `category` are required parameters. + x-description-zh: 按市场和类别筛选可交易证券列表,主要用于获取符合盘前/盘后延长交易时段条件的证券。`market` 和 `category` 均为必填参数。 + x-description-zh-hk: 按市場和類別篩選可交易證券列表,主要用於獲取符合盤前/盤後延長交易時段條件的證券。`market` 和 `category` 均爲必填參數。 tags: - - Grid Trading - parameters: - - name: order_id + - Quote + x-parameters: + - name: market in: query + type: string required: true - description: Grid order ID to query. - schema: - type: string - - name: history_id + description: Market code. One of `US`, `HK`. + x-description-zh: 市场代码,`US` 或 `HK`。 + x-description-zh-hk: 市場代碼,`US` 或 `HK`。 + - name: category in: query + type: string + required: true + description: Security category filter for the target trading session (e.g. `overnight` for US overnight-tradable securities). + x-description-zh: 目标交易时段的证券类别筛选(如 `overnight` 表示美股可夜盘交易证券)。 + x-description-zh-hk: 目標交易時段的證券類別篩選(如 `overnight` 表示美股可夜盤交易證券)。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/get_security_list?market=<market>&category=<category>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/get_security_list", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>", "category": "<category>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/get_security_list", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>", "category": "<category>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/get_security_list") + url.searchParams.set("market", "<market>") + url.searchParams.set("category", "<category>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/get_security_list?market=<market>&category=<category>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/get_security_list") + .header("Authorization", "Bearer <access_token>") + .query(&[("market", "<market>"), ("category", "<category>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/get_security_list?market=<market>&category=<category>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/get_security_list?market=<market>&category=<category>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: true + description: Matched securities. + x-description-zh: 匹配的证券列表。 + x-description-zh-hk: 匹配的證券列表。 + - name: └ symbol + type: string + required: true + description: Security symbol, e.g. `AAPL.US`. + x-description-zh: 证券代码,如 `AAPL.US`。 + x-description-zh-hk: 證券代碼,如 `AAPL.US`。 + - name: └ name_cn + type: string required: false - description: History cursor for paging through the lifecycle history. - schema: - type: string - nullable: true - - name: limit - in: query + description: Simplified Chinese name. + x-description-zh: 简体中文名称。 + x-description-zh-hk: 簡體中文名稱。 + - name: └ name_hk + type: string required: false - description: Page size. - schema: - type: integer - nullable: true + description: Traditional Chinese name. + x-description-zh: 繁体中文名称。 + x-description-zh-hk: 繁體中文名稱。 + - name: └ name_en + type: string + required: false + description: English name. + x-description-zh: 英文名称。 + x-description-zh-hk: 英文名稱。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid detail 764609681686573056 + longbridge security-list responses: '200': description: Successful response @@ -2992,153 +43016,193 @@ paths: code: 0 message: success data: - order_id: '764609681686573056' - symbol: 700.HK - stock_name: 腾讯控股 - status: Performing - grid_status: Running - suspend_reason: '' - sleeping_reason: '' - submitted_base_price: '300' - current_base_price: '300' - upper_limit_price: '360' - lower_limit_price: '240' - trigger_price_type: 2 - trigger_percent_up: '2' - trigger_percent_down: '2' - multiple_trigger: false - time_in_force: 1 - trigger_quantity: '100' - upper_limit_quantity: '200' - lower_limit_quantity: '100' - upper_limit_event: 1 - lower_limit_event: 1 - trigger_sell_depth: 0 - trigger_buy_depth: 0 - created_at: '2026-08-12T09:30:00+08:00' - updated_at: '2026-08-12T09:30:00+08:00' - settlement_currency: HKD - gtd: '' - grid_sub_orders: [] - sub_has_more: false - grid_order_history: - - history_id: '1' - created_at: '2026-08-12T09:30:00+08:00' - status: Performing - suspend_reason: '' - reason: '' - history_has_more: false - support_shortsell: false - rth: 0 - grid_order_type_up: GMO - grid_order_type_down: GMO + list: + - symbol: AAPL.US + name_cn: 苹果 + name_hk: 蘋果 + name_en: Apple Inc. default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/gridtrading/trigger_history_list: + x-subgroup: Stocks + x-subgroup-zh: 个股行情 + x-subgroup-zh-hk: 個股 + /v1/quote/history_market_temperature: get: - operationId: grid_trigger_history - summary: Grid Trigger History - x-summary-zh: 网格触发历史 + operationId: list_market_temperature + x-quote-command: market-temp + summary: Get Historical Market Temperature + x-summary-zh: 获取历史市场温度 + x-summary-zh-hk: 獲取歷史市場溫度 description: | - Query the trigger history of a grid order — the individual orders that the grid has - fired. The required parameter is `grid_order_id` (not `order_id`). - x-description-zh: 查询某个网格订单的触发历史,即网格已触发生成的各笔订单。必填参数为 `grid_order_id`(而非 `order_id`)。 + Get the historical market temperature time series for the specified market within a date range. + Each data point contains the daily temperature, valuation, and sentiment scores (all scored 0–100). + x-description-zh: 获取指定市场在日期范围内的历史市场温度时间序列,每个数据点包含当日情绪温度、估值和情绪分项评分(均为 0–100 分制)。 + x-description-zh-hk: 獲取指定市場在日期範圍內的歷史市場溫度時間序列,每個數據點包含當日情緒溫度、估值和情緒分項評分(均爲 0–100 分制)。 tags: - - Grid Trading - parameters: - - name: grid_order_id + - Market + x-parameters: + - name: market in: query + type: string required: true - description: Grid order ID whose trigger history to query. - schema: - type: string - - name: page + description: Market code. One of `HK`, `US`, `CN`, `SG`. + x-description-zh: 市场代码,`HK`、`US`、`CN`、`SG` 之一。 + x-description-zh-hk: 市場代碼,`HK`、`US`、`CN`、`SG` 之一。 + - name: start_date in: query - required: false - description: Page number. - schema: - type: integer - nullable: true - - name: limit + type: string + required: true + description: Start date in `YYYYMMDD` format (e.g. `20250101`). + x-description-zh: 起始日期,`YYYYMMDD` 格式(如 `20250101`)。 + x-description-zh-hk: 起始日期,`YYYYMMDD` 格式(如 `20250101`)。 + - name: end_date in: query - required: false - description: Page size. - schema: - type: integer - nullable: true - x-codeSamples: + type: string + required: true + description: End date in `YYYYMMDD` format (e.g. `20250110`). + x-description-zh: 结束日期,`YYYYMMDD` 格式(如 `20250110`)。 + x-description-zh-hk: 結束日期,`YYYYMMDD` 格式(如 `20250110`)。 + x-request-examples: - lang: Shell - label: CLI + label: cURL source: | - longbridge grid triggers 764609681686573056 - responses: - '200': - description: Successful response - content: - application/json: - example: - code: 0 - message: success - data: - trigger_orders: - - id: '764700000000000000' - status: FilledStatus - name: 腾讯控股 - symbol: 700.HK - price: '294' - quantity: '100' - executed_price: '294' - executed_qty: '100' - submitted_at: '2026-08-12T10:15:00+08:00' - action: 1 - order_type: MO - trigger_price: '294' - msg: '' - currency: HKD - last_done: '294' - updated_at: '2026-08-12T10:15:03+08:00' - time_in_force: 1 - gtd: '' - trigger_at: '2026-08-12T10:15:00+08:00' - trigger_status: 0 - has_more: false - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - /v1/gridtrading/cancel: - post: - operationId: cancel_grid_order - summary: Cancel Grid Order - x-summary-zh: 取消网格订单 - description: | - Cancel a grid order. The grid stops and no further triggers are placed. - x-description-zh: 取消网格订单。网格停止,不再触发新的挂单。 - tags: - - Grid Trading - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - order_id - properties: - order_id: - type: string - description: ID of the grid order to cancel. + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/history_market_temperature?market=<market>&start_date=<start_date>&end_date=<end_date>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/history_market_temperature", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>", "start_date": "<start_date>", "end_date": "<end_date>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/history_market_temperature", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>", "start_date": "<start_date>", "end_date": "<end_date>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/history_market_temperature") + url.searchParams.set("market", "<market>") + url.searchParams.set("start_date", "<start_date>") + url.searchParams.set("end_date", "<end_date>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/history_market_temperature?market=<market>&start_date=<start_date>&end_date=<end_date>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/history_market_temperature") + .header("Authorization", "Bearer <access_token>") + .query(&[("market", "<market>"), ("start_date", "<start_date>"), ("end_date", "<end_date>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/history_market_temperature?market=<market>&start_date=<start_date>&end_date=<end_date>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/history_market_temperature?market=<market>&start_date=<start_date>&end_date=<end_date>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Market-temperature time series. + x-description-zh: 市场温度时间序列。 + x-description-zh-hk: 市場溫度時間序列。 + - name: └ timestamp + type: string + required: false + description: Unix timestamp of the data point. + x-description-zh: 数据点的 Unix 时间戳。 + x-description-zh-hk: 數據點的 Unix 時間戳。 + - name: └ temperature + type: integer + required: false + description: Market temperature (0–100). + x-description-zh: 市场温度(0–100)。 + x-description-zh-hk: 市場溫度(0–100)。 + - name: └ valuation + type: integer + required: false + description: Valuation score. + x-description-zh: 估值分数。 + x-description-zh-hk: 估值分數。 + - name: └ sentiment + type: integer + required: false + description: Sentiment score. + x-description-zh: 情绪分数。 + x-description-zh-hk: 情緒分數。 + - name: type + type: string + required: false + description: Granularity of the series, e.g. `day`. + x-description-zh: 序列粒度,如 `day`。 + x-description-zh-hk: 序列粒度,如 `day`。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid cancel 764609681686573056 + longbridge market-temp [MARKET] --history --start YYYY-MM-DD --end YYYY-MM-DD responses: '200': description: Successful response @@ -3147,40 +43211,177 @@ paths: example: code: 0 message: success - data: {} + data: + list: + - timestamp: '1735794000' + temperature: 58 + valuation: 54 + sentiment: 61 + - timestamp: '1735880400' + temperature: 59 + valuation: 56 + sentiment: 63 + type: day default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/gridtrading/suspend: - post: - operationId: suspend_grid_order - summary: Suspend Grid Order - x-summary-zh: 暂停网格订单 + x-subgroup: Market Status + x-subgroup-zh: 市场状态 + x-subgroup-zh-hk: 市場狀態 + /v1/quote/market_temperature: + get: + operationId: market_temperature + x-quote-command: market-temp + summary: Get Current Market Temperature + x-summary-zh: 获取当前市场情绪 + x-summary-zh-hk: 獲取當前市場情緒 description: | - Suspend a running grid order. The grid stops triggering but is kept and can be restarted. - x-description-zh: 暂停运行中的网格订单。网格停止触发,但会保留并可重新启动。 + Get the current sentiment temperature snapshot for the specified market. + Scores range from 0–100; a higher value indicates a more bullish market. + x-description-zh: 获取指定市场的当前情绪温度快照。评分范围 0–100,数值越高表示市场越乐观。 + x-description-zh-hk: 獲取指定市場的當前情緒溫度快照。評分範圍 0–100,數值越高表示市場越樂觀。 tags: - - Grid Trading - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - order_id - properties: - order_id: - type: string - description: ID of the grid order to suspend. + - Market + x-parameters: + - name: market + in: query + type: string + required: true + description: Market code. One of `HK`, `US`, `CN`, `SG`. + x-description-zh: 市场代码,`HK`、`US`、`CN`、`SG` 之一。 + x-description-zh-hk: 市場代碼,`HK`、`US`、`CN`、`SG` 之一。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/market_temperature?market=<market>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/market_temperature", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/market_temperature", + headers={"Authorization": "Bearer <access_token>"}, + params={"market": "<market>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/market_temperature") + url.searchParams.set("market", "<market>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/market_temperature?market=<market>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/market_temperature") + .header("Authorization", "Bearer <access_token>") + .query(&[("market", "<market>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/market_temperature?market=<market>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/market_temperature?market=<market>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: temperature + type: integer + required: false + description: Current market temperature (0–100). + x-description-zh: 当前市场温度(0–100)。 + x-description-zh-hk: 當前市場溫度(0–100)。 + - name: description + type: string + required: false + description: Text description of the current temperature. + x-description-zh: 当前温度的文字描述。 + x-description-zh-hk: 當前溫度的文字描述。 + - name: valuation + type: integer + required: false + description: Valuation score. + x-description-zh: 估值分数。 + x-description-zh-hk: 估值分數。 + - name: sentiment + type: integer + required: false + description: Sentiment score. + x-description-zh: 情绪分数。 + x-description-zh-hk: 情緒分數。 + - name: updated_at + type: string + required: false + description: Update time (Unix timestamp). + x-description-zh: 更新时间(Unix 时间戳)。 + x-description-zh-hk: 更新時間(Unix 時間戳)。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid suspend 764609681686573056 + longbridge market-temp [MARKET] responses: '200': description: Successful response @@ -3189,130 +43390,514 @@ paths: example: code: 0 message: success - data: {} + data: + temperature: 70 + description: 温度温暖并快速上升中 + valuation: 59 + sentiment: 82 + updated_at: '1774317902' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/gridtrading/restart: - post: - operationId: restart_grid_order - summary: Restart Grid Order - x-summary-zh: 重启网格订单 + x-subgroup: Market Status + x-subgroup-zh: 市场状态 + x-subgroup-zh-hk: 市場狀態 + /v1/statement/list: + get: + operationId: list_statements + summary: List Statements + x-summary-zh: 查询结单列表 + x-summary-zh-hk: 查詢結單列表 description: | - Restart a suspended grid order. The grid resumes triggering. - x-description-zh: 重新启动已暂停的网格订单。网格恢复触发。 + Query available account statements (daily or monthly). Returns a list of statement + dates and file keys that can be used with the download endpoint. + x-description-zh: 查询可用的账户结单(日结单或月结单),返回结单日期和文件标识列表,可用于下载接口。 + x-description-zh-hk: 查詢可用的賬戶結單(日結單或月結單),返回結單日期和文件標識列表,可用於下載接口。 tags: - - Grid Trading - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - order_id - properties: - order_id: - type: string - description: ID of the grid order to restart. + - Account + x-parameters: + - name: statement_type + in: query + type: integer + required: false + description: 'Statement type: `1` = daily (default), `2` = monthly.' + x-description-zh: 对账单类型:`1` 日结单(默认),`2` 月结单。 + x-description-zh-hk: 對賬單類型:`1` 日結單(預設),`2` 月結單。 + - name: page + in: query + type: integer + required: false + description: Page number for pagination. + x-description-zh: 分页页码。 + x-description-zh-hk: 分頁頁碼。 + - name: page_size + in: query + type: integer + required: false + description: Number of results per page. + x-description-zh: 每页结果数量。 + x-description-zh-hk: 每頁結果數量。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/statement/list' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/statement/list", + headers={"Authorization": "Bearer <access_token>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/statement/list", + headers={"Authorization": "Bearer <access_token>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const resp = await fetch("https://openapi.longbridge.com/v1/statement/list", { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/statement/list")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/statement/list") + .header("Authorization", "Bearer <access_token>") + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/statement/list"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/statement/list\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: list + type: object[] + required: false + description: Daily holding history records + x-description-zh: 每日持仓历史记录, + x-description-zh-hk: 每日持倉歷史紀錄, + - name: └ dt + type: integer + required: false + description: Date. + x-description-zh: 日期。 + x-description-zh-hk: 日期。 + - name: └ file_key + type: string + required: false + description: File key — pass to Get Statement Download URL to obtain a download link. + x-description-zh: 文件 key——传给「获取对账单下载地址」以获取下载链接。 + x-description-zh-hk: 文件 key——傳給「獲取對賬單下載地址」以獲取下載連結。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid restart 764609681686573056 + longbridge statement list + longbridge statement list --type monthly + longbridge statement list --start-date 20260101 --limit 10 responses: '200': - description: Successful response + description: Statement list content: application/json: + schema: + type: array + items: + type: object + properties: + date: + type: string + description: Statement date (string, e.g. "20260327"). + file_key: + type: string + description: File key used to request the download URL. example: - code: 0 - message: success - data: {} + - date: '20260327' + file_key: /statement_data/data/lb/1/20260327/10000104.json + - date: '20260324' + file_key: /statement_data/data/lb/1/20260324/10000104.json + - date: '20260323' + file_key: /statement_data/data/lb/1/20260323/10000104.json default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/record/questionnaire: - post: - operationId: submit_strategy_questionnaire - summary: Submit Strategy Questionnaire - x-summary-zh: 提交策略问卷 - description: | - Record the user's consent to the strategy risk disclosure. This is required once before - using grid trading. The consent body is `{ "type": "strategy", "items": { "agree": "true" } }`. - x-description-zh: | - 记录用户对策略风险揭示的确认。使用网格交易前需执行一次。请求体为 - `{ "type": "strategy", "items": { "agree": "true" } }`。 - tags: - - Grid Trading - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - type - - items - properties: - type: - type: string - description: Questionnaire type. Use `strategy` for grid trading. - items: - type: object - description: 'Consent items. For grid trading: `{ "agree": "true" }`.' - additionalProperties: - type: string + x-subgroup: Portfolio + x-subgroup-zh: 投资组合 + x-subgroup-zh-hk: 投資組合 + /v1/statement/download: + get: + operationId: get_statement_download_url + summary: Get Statement Download URL + x-summary-zh: 获取结单下载地址 + x-summary-zh-hk: 獲取結單下載地址 + description: | + Get a presigned download URL for a specific statement file. The URL returns a JSON + document containing the full statement content with all sections. + x-description-zh: 获取指定结单文件的预签名下载地址,返回包含结单全部板块的 JSON 文档。 + x-description-zh-hk: 獲取指定結單文件的預簽名下載地址,返回包含結單全部板塊的 JSON 文檔。 + tags: + - Account + x-parameters: + - name: file_key + in: query + type: string + required: true + description: File key obtained from the List Statements endpoint. + x-description-zh: 从「查询对账单列表」获取的文件 key。 + x-description-zh-hk: 從「查詢對賬單列表」獲取的文件 key。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/statement/download?file_key=<file_key>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/statement/download", + headers={"Authorization": "Bearer <access_token>"}, + params={"file_key": "<file_key>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/statement/download", + headers={"Authorization": "Bearer <access_token>"}, + params={"file_key": "<file_key>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/statement/download") + url.searchParams.set("file_key", "<file_key>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/statement/download?file_key=<file_key>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/statement/download") + .header("Authorization", "Bearer <access_token>") + .query(&[("file_key", "<file_key>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/statement/download?file_key=<file_key>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/statement/download?file_key=<file_key>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: url + type: string + required: true + description: Time-limited download URL for the statement file. + x-description-zh: 对账单文件的限时下载地址。 + x-description-zh-hk: 對賬單文件的限時下載地址。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid questionnaire + longbridge statement export --file-key abc123xyz456 --section equity_holdings + longbridge statement export --file-key abc123xyz456 --section stock_trades -o trades.csv + longbridge statement export --file-key abc123xyz456 --all -o ./report/ responses: '200': - description: Successful response + description: Download URL content: application/json: + schema: + type: object + properties: + url: + type: string + description: Presigned URL to download the statement JSON. example: - code: 0 - message: success - data: {} + url: https://storage.example.com/statements/abc123xyz456.json?X-Amz-Signature=... default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' - /v1/orders/info: + x-subgroup: Portfolio + x-subgroup-zh: 投资组合 + x-subgroup-zh-hk: 投資組合 + /v1/quote/filings: get: - operationId: grid_symbol_info - summary: Grid Symbol Info - x-summary-zh: 网格标的信息 + operationId: list_filings + x-quote-command: filings + summary: Get Filings by Symbol + x-summary-zh: 获取标的监管文件 + x-summary-zh-hk: 獲取標的監管文件 description: | - Query the security info used to build a grid order — name, latest price, board lot sizes, - price-step (bid-size) rules, and channel / authorization info (whether the strategy consent - was granted, RTH support, and supported currencies). - x-description-zh: 查询用于构建网格订单的证券信息——名称、最新价、每手股数、价位(报价步长)规则,以及渠道/授权信息(是否已完成策略确认、是否支持盘前盘后、支持的币种)。 + Get the list of regulatory filings or disclosure documents for the specified symbol. + Each filing includes a title, file name, download URLs, and publication timestamp. + x-description-zh: 获取指定标的的监管文件或信息披露文件列表,每条记录包含标题、文件名、下载链接和发布时间戳。 + x-description-zh-hk: 獲取指定標的的監管文件或信息披露文件列表,每條記錄包含標題、文件名、下載鏈接和發佈時間戳。 tags: - - Grid Trading - parameters: - - name: counter_id + - Quote + x-parameters: + - name: symbol in: query + type: string required: true - description: 'Security symbol in `ticker.region` format (e.g. `700.HK`).' - schema: - type: string + description: Security symbol to query filings for (e.g. `AAPL.US`, `700.HK`). + x-description-zh: 要查询公告的证券代码(如 `AAPL.US`、`700.HK`)。 + x-description-zh-hk: 要查詢公告的證券代碼(如 `AAPL.US`、`700.HK`)。 + x-request-examples: + - lang: Shell + label: cURL + source: | + curl --request GET \ + --url 'https://openapi.longbridge.com/v1/quote/filings?symbol=<symbol>' \ + --header 'Authorization: Bearer <access_token>' + - lang: Python + label: Python + source: | + import requests + + resp = requests.get( + "https://openapi.longbridge.com/v1/quote/filings", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) + print(resp.json()) + - lang: Python + label: Python (async) + source: | + import asyncio + import aiohttp + + async def main(): + async with aiohttp.ClientSession() as session: + async with session.get( + "https://openapi.longbridge.com/v1/quote/filings", + headers={"Authorization": "Bearer <access_token>"}, + params={"symbol": "<symbol>"}, + ) as resp: + print(await resp.json()) + + asyncio.run(main()) + - lang: JavaScript + label: Node.js + source: | + const url = new URL("https://openapi.longbridge.com/v1/quote/filings") + url.searchParams.set("symbol", "<symbol>") + + const resp = await fetch(url, { + method: "GET", + headers: { + "Authorization": "Bearer <access_token>", + }, + }) + console.log(await resp.json()) + - lang: Java + label: Java + source: | + import java.net.URI; + import java.net.http.*; + + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder() + .uri(URI.create("https://openapi.longbridge.com/v1/quote/filings?symbol=<symbol>")) + .header("Authorization", "Bearer <access_token>") + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + var resp = client.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println(resp.body()); + - lang: Rust + label: Rust + source: | + let client = reqwest::Client::new(); + let resp = client + .request(reqwest::Method::GET, "https://openapi.longbridge.com/v1/quote/filings") + .header("Authorization", "Bearer <access_token>") + .query(&[("symbol", "<symbol>")]) + .send() + .await? + .text() + .await?; + println!("{resp}"); + - lang: C++ + label: C++ + source: | + #include <curl/curl.h> + + int main() { + CURL *curl = curl_easy_init(); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Authorization: Bearer <access_token>"); + curl_easy_setopt(curl, CURLOPT_URL, "https://openapi.longbridge.com/v1/quote/filings?symbol=<symbol>"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return 0; + } + - lang: Go + label: Go + source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, _ := http.NewRequest(\"GET\", \"https://openapi.longbridge.com/v1/quote/filings?symbol=<symbol>\", nil)\n\treq.Header.Set(\"Authorization\", \"Bearer <access_token>\")\n\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n\tbody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(body))\n}\n" + x-response-properties: + - name: items + type: object[] + required: false + description: Filings for the security. + x-description-zh: 该证券的公告列表。 + x-description-zh-hk: 該證券的公告列表。 + - name: └ id + type: string + required: false + description: Filing ID. + x-description-zh: 公告 ID。 + x-description-zh-hk: 公告 ID。 + - name: └ title + type: string + required: false + description: Filing title. + x-description-zh: 公告标题。 + x-description-zh-hk: 公告標題。 + - name: └ file_name + type: string + required: false + description: File name. + x-description-zh: 文件名。 + x-description-zh-hk: 文件名。 + - name: └ file_urls + type: array + required: false + description: Source document URLs. + x-description-zh: 原始文件地址列表。 + x-description-zh-hk: 原始文件地址列表。 + - name: └ publish_at + type: string + required: false + description: Publish time (Unix timestamp). + x-description-zh: 发布时间(Unix 时间戳)。 + x-description-zh-hk: 發布時間(Unix 時間戳)。 + - name: └ description + type: string + required: false + description: Filing description. + x-description-zh: 公告描述。 + x-description-zh-hk: 公告描述。 x-codeSamples: - lang: Shell label: CLI source: | - longbridge grid info 700.HK + longbridge filing list <SYMBOL> responses: '200': description: Successful response @@ -3322,28 +43907,23 @@ paths: code: 0 message: success data: - name: 腾讯控股 - last_done: '300' - lot_size: '100' - buy_lot_size: '100' - sell_lot_size: '100' - bid_sizes: - - str_proceed: '0' - end_proceed: '500' - bid_size: '0.2' - channel_info: - strategy_granted: true - support_rth: true - currency: HKD - settlement_currency: - - HKD - - USD + items: + - id: '627391979864985729' + title: 苹果 | 4 - Apple Inc. (0000320193) (Issuer) + description: '' + file_name: 4 - Apple Inc. (0000320193) (Issuer) + file_urls: + - https://www.sec.gov/Archives/edgar/data/320193/.../wk-form4_1773786674.xml + publish_at: '1773786677' default: description: Unexpected error content: application/json: schema: $ref: '#/components/schemas/Error' + x-subgroup: Analytics + x-subgroup-zh: 数据分析 + x-subgroup-zh-hk: 數據分析 components: securitySchemes: oauth2: diff --git a/packages/api-reference/package.json b/packages/api-reference/package.json index 890719b7d..86443be4b 100644 --- a/packages/api-reference/package.json +++ b/packages/api-reference/package.json @@ -7,13 +7,18 @@ "types": "./src/index.ts", "exports": { ".": "./src/index.ts", + "./markdown": "./src/openapi-markdown.ts", "./src/api-reference.css": "./src/api-reference.css" }, "dependencies": { + "@longbridge/openapi-ui": "workspace:*", + "@longbridge/openapi-tryit": "workspace:*", "js-yaml": "^4", - "markdown-it": "^14" + "markdown-it": "^14", + "markdown-it-container": "^4.0.0" }, "devDependencies": { - "@types/markdown-it": "^14" + "@types/markdown-it": "^14", + "@types/markdown-it-container": "^4.0.1" } } diff --git a/packages/api-reference/src/ApiReference.tsx b/packages/api-reference/src/ApiReference.tsx index 1682892ef..f7b58c2b5 100644 --- a/packages/api-reference/src/ApiReference.tsx +++ b/packages/api-reference/src/ApiReference.tsx @@ -9,24 +9,94 @@ import { t } from '@longbridge/openapi-utils' import type { Locale } from '@longbridge/openapi-utils' import { parseSpec, - splitDescriptionAndCode, localizeDocLinks, - formatPath, epId, buildCurl, buildResponseExample, + endpointResponseExamples, + pickLocale, + PAGE_ICONS, type EndpointItem, type PageItem, type CodeBlock, type Section, + type XParameter, + type TagGroup, + type SubGroup, + type WsGroupData, + type WsCommandItem, } from './openapi-loader' -import { CodePanel } from './CodeSample' +import { DOCS_ORDER, LEAF_FALLBACK } from './docs-order' +import { CodePanel, CodeTabs, CodeDropdown, highlightCode } from './CodeSample' import { QuotePermission } from './QuotePermission' +import { EnvProvider } from './EnvContext' +import { EndpointUrlBar, CopyPageMenu } from './EndpointUrlBar' +import { RequestPanel } from './RequestPanel' +import { ResponsePanel } from './ResponsePanel' +import { AuthTable, AuthModeSelect } from './AuthTable' +import type { ApiResponse } from '@longbridge/openapi-tryit' +import { CliCommand } from '@longbridge/openapi-ui' import MarkdownIt from 'markdown-it' +import container from 'markdown-it-container' // ── markdown-it setup ───────────────────────────────────────────────────────── -const _md = new MarkdownIt({ html: false, linkify: true, typographer: false }) +const _md = new MarkdownIt({ + html: false, + linkify: true, + typographer: false, + // Syntax-highlight fenced code blocks in x-page markdown with the same + // highlighter the CodeTabs/CodePanel use, so page code matches endpoint code. + highlight: (str, lang) => highlightCode(str, (lang || '').toLowerCase()), +}) + +// `:::type Title` admonitions → docs-style callout boxes (same DOM/CSS as the +// docs remark-callout output: `.callout.callout-<type>` + `.callout-title`). +const CALLOUT_TYPES = ['tip', 'warning', 'danger', 'info', 'note', 'caution', 'success'] +for (const type of CALLOUT_TYPES) { + // markdown-it-container ships types for a different @types/markdown-it build, + // so its plugin signature doesn't unify with our MarkdownIt instance — cast. + _md.use(container as unknown as Parameters<(typeof _md)['use']>[0], type, { + render(tokens: any[], idx: number) { + const token = tokens[idx] + if (token.nesting === 1) { + const raw = token.info.trim().slice(type.length).trim() + const title = raw || type.charAt(0).toUpperCase() + type.slice(1) + return `<div class="callout callout-${type}" role="note" data-lbus-component="callout-${type}">\n<p class="callout-title">${_md.utils.escapeHtml(title)}</p>\n` + } + return '</div>\n' + }, + }) +} + +// Slug for heading anchors — keeps unicode letters/digits (so Chinese headings +// get stable ids), strips inline markdown marks. Shared by the heading-id rule +// and the sidebar section extractor so their ids match. +export function slugify(s: string): string { + return ( + s + .toLowerCase() + .trim() + .replace(/[`*_~]/g, '') + .replace(/[^\p{L}\p{N}]+/gu, '-') + .replace(/^-+|-+$/g, '') || 'section' + ) +} + +// Give every heading a stable `id` so sidebar section links can scroll to it. +_md.core.ruler.push('heading_ids', (state) => { + const seen: Record<string, number> = {} + const tokens = state.tokens + for (let i = 0; i < tokens.length; i++) { + if (tokens[i].type !== 'heading_open') continue + const inline = tokens[i + 1] + const text = inline && inline.content ? inline.content : '' + let slug = slugify(text) + if (seen[slug]) slug = `${slug}-${seen[slug]++}` + else seen[slug] = 1 + tokens[i].attrSet('id', slug) + } +}) // Patch link_open to add target="_blank" for external links const _defLinkOpen = _md.renderer.rules.link_open @@ -63,9 +133,685 @@ export interface ApiReferenceProps { // ── Build sections for an endpoint ─────────────────────────────────────────── +const PARAM_TITLE: Record<Locale, string> = { en: 'Parameters', 'zh-CN': '参数', 'zh-HK': '參數' } +const PARAM_NOTE: Record<Locale, string> = { + en: 'SDK method parameters.', + 'zh-CN': 'SDK 方法参数。', + 'zh-HK': 'SDK 方法參數。', +} + +// Docs-model section labels (trilingual). +const L = { + request: { en: 'Request', 'zh-CN': '请求', 'zh-HK': '請求' }, + pathParams: { en: 'Path parameters', 'zh-CN': '路径参数', 'zh-HK': '路徑參數' }, + queryParams: { en: 'Query parameters', 'zh-CN': '查询参数', 'zh-HK': '查詢參數' }, + requestBody: { en: 'Request body', 'zh-CN': '请求体', 'zh-HK': '請求體' }, + response: { en: 'Response', 'zh-CN': '响应', 'zh-HK': '響應' }, + responseProps: { en: 'Response properties', 'zh-CN': '响应字段', 'zh-HK': '響應欄位' }, + responseJson: { en: 'Response JSON example', 'zh-CN': '响应 JSON 示例', 'zh-HK': '響應 JSON 示例' }, + errorCode: { en: 'Error code', 'zh-CN': '错误码', 'zh-HK': '錯誤碼' }, + name: { en: 'Name', 'zh-CN': '名称', 'zh-HK': '名稱' }, + type: { en: 'Type', 'zh-CN': '类型', 'zh-HK': '類型' }, + required: { en: 'Required', 'zh-CN': '必填', 'zh-HK': '必填' }, + description: { en: 'Description', 'zh-CN': '说明', 'zh-HK': '說明' }, + errorCodeBody: { + en: 'See the ', + 'zh-CN': '参见', + 'zh-HK': '參見', + }, + errorCodeLink: { en: 'Error Codes', 'zh-CN': '错误码文档', 'zh-HK': '錯誤碼文檔' }, + apiKeyNoteBody: { + en: 'Shown with OAuth (Bearer). For API-Key auth, sign the request — see ', + 'zh-CN': '示例使用 OAuth(Bearer)。如用 API Key 鉴权,请对请求签名 —— 见', + 'zh-HK': '示例使用 OAuth(Bearer)。如用 API Key 鑑權,請對請求簽名 —— 見', + }, + authorization: { en: 'Authorization', 'zh-CN': '鉴权', 'zh-HK': '鑑權' }, + authorizationDesc: { + en: 'Access token issued for the account, sent as `Authorization: Bearer <access_token>`.', + 'zh-CN': '账户签发的 access token,通过 `Authorization: Bearer <access_token>` 发送。', + 'zh-HK': '帳戶簽發的 access token,通過 `Authorization: Bearer <access_token>` 發送。', + }, + tryIt: { en: 'Try it', 'zh-CN': 'Try it', 'zh-HK': 'Try it' }, + close: { en: 'Close', 'zh-CN': '关闭', 'zh-HK': '關閉' }, + menu: { en: 'Menu', 'zh-CN': '菜单', 'zh-HK': '選單' }, +} as const + +type RowVM = { name: string; type: string; required: boolean; description: string } +function rowsFrom(xs: XParameter[] | undefined, locale: Locale): RowVM[] { + return (xs ?? []).map((p) => ({ + name: p.name, + type: p.type ?? 'string', + required: !!p.required, + description: pickLocale(p.description, p['x-description-zh'], p['x-description-zh-hk'], locale), + })) +} + +// Plain table — rendered inside `article.docs-content`, so it inherits the +// exact docs table styling. +function ParamTable({ rows, locale }: { rows: RowVM[]; locale: Locale }) { + if (!rows.length) return null + return ( + <table className="api-fields"> + <thead> + <tr> + <th>{L.name[locale]}</th> + <th>{L.type[locale]}</th> + <th>{L.required[locale]}</th> + <th>{L.description[locale]}</th> + </tr> + </thead> + <tbody> + {rows.map((row, i) => ( + <tr key={`${row.name}-${i}`}> + <td> + <code>{row.name}</code> + </td> + <td>{row.type}</td> + <td>{row.required ? t(locale, 'api.param.required') : t(locale, 'api.param.optional')}</td> + <td>{row.description}</td> + </tr> + ))} + </tbody> + </table> + ) +} + +// Breadcrumb — replicates src/components/shell/Breadcrumb.tsx so the DOM/styling +// matches docs (`.docs-content [data-lbus-component="breadcrumb"]`). +function DocsBreadcrumb({ items, locale }: { items: { text: string; href?: string }[]; locale: Locale }) { + const homeHref = locale === 'en' ? '/' : `/${locale}/` + const all = [{ text: t(locale, 'breadcrumb.home'), href: homeHref }, ...items] + return ( + <nav aria-label="Breadcrumb" data-lbus-component="breadcrumb"> + <ol + className="flex flex-wrap items-center gap-2 p-0 m-0 list-none text-sm text-[color:var(--lb-fg-2)]" + role="list"> + {all.map((item, index) => { + const isLast = index === all.length - 1 + return ( + <li key={`${item.text}-${index}`} className="inline-flex items-center gap-2"> + {item.href && !isLast ? ( + <a href={item.href} className="text-inherit no-underline hover:text-[color:var(--lbus-c-text)]"> + {item.text} + </a> + ) : ( + <span + aria-current={isLast ? 'page' : undefined} + className={isLast ? 'font-semibold text-[color:var(--lbus-c-text)]' : undefined}> + {item.text} + </span> + )} + {!isLast && ( + <span aria-hidden="true" className="text-[color:var(--lb-fg-3)]"> + / + </span> + )} + </li> + ) + })} + </ol> + </nav> + ) +} + +// Slim doc footer — replicates src/components/shell/DocFooter.astro (styled by +// `.docs-doc-footer` in docs.css). Rendered inside `.docs-inner` like docs. +function DocFooterRow({ locale }: { locale: Locale }) { + const lp = (p: string) => (locale === 'en' ? p : `/${locale}${p}`) + const sgBase = locale === 'en' ? 'https://longbridge.com/sg' : 'https://longbridge.com/sg/zh-CN' + const left = [ + { label: 'Longbridge', href: 'https://longbridge.com', ext: true }, + { label: t(locale, 'footer.download'), href: 'https://longbridge.com/download', ext: true }, + { label: t(locale, 'footer.terms'), href: `${sgBase}/support/topics/us-trade/user-agreement`, ext: true }, + { label: t(locale, 'footer.privacy'), href: `${sgBase}/support/topics/Other/privacy-policy`, ext: true }, + ] + const right = [ + { label: 'SDK', href: lp('/sdk') }, + { label: 'MCP', href: lp('/docs/mcp') }, + { + label: 'ChatGPT App', + href: 'https://chatgpt.com/apps/longbridge/asdk_app_6a2baf2fad748191812393c3e00308ef', + ext: true, + }, + { label: 'Claude Connector', href: 'https://claude.ai/directory/connectors/longbridge', ext: true }, + { label: 'CLI', href: lp('/docs/cli') }, + { label: 'LLM', href: lp('/docs/llm') }, + { label: t(locale, 'footer.assets'), href: lp('/docs/assets') }, + { label: 'Navi', href: 'https://navi-lang.org', ext: true }, + { label: t(locale, 'footer.feedback'), href: 'https://github.com/longbridge/developers/issues', ext: true }, + ] + const ext = (e?: boolean) => (e ? { target: '_blank', rel: 'noreferrer' } : {}) + return ( + <footer className="docs-doc-footer" data-lbus-component="docs-footer"> + <div className="docs-doc-footer__group"> + {left.map((l) => ( + <a key={l.label} href={l.href} {...ext(l.ext)}> + {l.label} + </a> + ))} + </div> + <div className="docs-doc-footer__group"> + {right.map((l) => ( + <a key={l.label} href={l.href} {...ext(l.ext)}> + {l.label} + </a> + ))} + <a + className="docs-doc-footer__github" + href="https://github.com/longbridge" + target="_blank" + rel="noreferrer" + aria-label="GitHub"> + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 24 24" + fill="currentColor" + width="16" + height="16" + aria-hidden="true"> + <path d="M12.001 2C6.47598 2 2.00098 6.475 2.00098 12C2.00098 16.425 4.86348 20.1625 8.83848 21.4875C9.33848 21.575 9.52598 21.275 9.52598 21.0125C9.52598 20.775 9.51348 19.9875 9.51348 19.15C7.00098 19.6125 6.35098 18.5375 6.15098 17.975C6.03848 17.6875 5.55098 16.8 5.12598 16.5625C4.77598 16.375 4.27598 15.9125 5.11348 15.9C5.90098 15.8875 6.46348 16.625 6.65098 16.925C7.55098 18.4375 8.98848 18.0125 9.56348 17.75C9.65098 17.1 9.91348 16.6625 10.201 16.4125C7.97598 16.1625 5.65098 15.3 5.65098 11.475C5.65098 10.3875 6.03848 9.4875 6.67598 8.7875C6.57598 8.5375 6.22598 7.5125 6.77598 6.1375C6.77598 6.1375 7.61348 5.875 9.52598 7.1625C10.326 6.9375 11.176 6.825 12.026 6.825C12.876 6.825 13.726 6.9375 14.526 7.1625C16.4385 5.8625 17.276 6.1375 17.276 6.1375C17.826 7.5125 17.476 8.5375 17.376 8.7875C18.0135 9.4875 18.401 10.375 18.401 11.475C18.401 15.3125 16.0635 16.1625 13.8385 16.4125C14.201 16.725 14.5135 17.325 14.5135 18.2625C14.5135 19.6 14.501 20.675 14.501 21.0125C14.501 21.275 14.6885 21.5875 15.1885 21.4875C19.259 20.1133 21.9999 16.2963 22.001 12C22.001 6.475 17.526 2 12.001 2Z" /> + </svg> + </a> + </div> + </footer> + ) +} + +// Drop the leading CRUD/read verb from an endpoint name — the HTTP method badge +// already conveys the action, so "Query Signals" → "Signals", "Get Signal +// Detail" → "Signal Detail", "更新定投" → "定投". Falls back to the original if +// stripping would leave nothing. +const VERB_EN = + /^(Query|List|Get|Fetch|Retrieve|Return|Show|Create|Add|New|Update|Modify|Set|Replace|Delete|Remove|Cancel|Submit|Estimate|Calculate|Calc|Check|Search|Pin|Unpin|Enable|Disable|Suspend|Restart|Toggle|Download|Export|Register|Bind|Subscribe|Unsubscribe|Apply)\s+/i +const VERB_ZH = + /^(查询|获取|列出|返回|显示|创建|新建|新增|更新|修改|设置|替换|删除|移除|取消|提交|估算|计算|校验|检查|搜索|置顶|启用|禁用|暂停|重启|切换|下载|导出|注册|绑定|订阅|退订|应用)/ +// Move a leading market qualifier to a parenthetical suffix, so the topic reads +// first: "US Crypto Overview" → "Crypto Overview (US)", "美股加密货币概览" → +// "加密货币概览(美股)". +const MARKET_EN = /^(US|HK|SG|CN|A-Share)\s+(.+)$/ +const MARKET_ZH = /^(美股|港股|A股|新加坡)(.+)$/ +function marketToSuffix(name: string): string { + const mEn = MARKET_EN.exec(name) + if (mEn) return `${mEn[2]} (${mEn[1]})` + const mZh = MARKET_ZH.exec(name) + if (mZh) return `${mZh[2]}(${mZh[1]})` + return name +} +// Drop a "Push · " / "推送 · " prefix (the WS badge already marks it as push) and +// a redundant "Warrant" / "权证" prefix (already under the Warrants group). +function stripRedundantPrefix(name: string): string { + return name + .replace(/^(Push|推送|推播)\s*[·:]\s*/, '') + .replace(/^(Warrant|权证|權證|轮证|輪證)\s*/, '') +} +function stripLeadingVerb(name: string): string { + const n = stripRedundantPrefix(name) + const re = /^[㐀-鿿]/.test(n) ? VERB_ZH : VERB_EN + const stripped = n.replace(re, '').trim() + return marketToSuffix(stripped.length > 0 ? stripped : n) +} +// WS command names keep their leading verb — Subscribe / Unsubscribe / 订阅 / 取消 +// are the meaning, not noise — so only strip the push/warrant prefix + market. +function wsLeafName(name: string): string { + return marketToSuffix(stripRedundantPrefix(name)) +} + +// Icons for the top-level groups, matching the CLI docs category icons (lucide), +// so the API Reference sidebar reads like the CLI sidebar. +const ICO = (paths: string) => + `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${paths}</svg>` +const GROUP_ICONS: Record<string, string> = { + Quote: ICO('<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>'), + Fundamental: ICO('<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>'), + Market: ICO('<path d="M3 3v18h18"/><path d="m19 9-5 5-4-4-3 3"/>'), + Screener: ICO('<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>'), + Trade: ICO('<polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/>'), + Account: ICO('<rect x="2" y="7" width="20" height="14" rx="2" ry="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/>'), + 'AI Agent': ICO('<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>'), + 'News & Contents': ICO('<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8"/><path d="M15 18h-5"/><path d="M10 6h8v4h-8V6Z"/>'), +} + +// Subgroup order from the docs guide subfolder positions +// (docs/{lang}/docs/<group>/<sub>/_category_.json). WS protocol groups map onto +// their docs subfolder (Subscription → "Subscribe"); anything unlisted sorts last. +const DOCS_SUB_ORDER: Record<string, number> = { + Subscribe: 2, + Stocks: 3, + Options: 4, + Warrants: 5, + Analytics: 7, + Watchlist: 9, + Fundamentals: 1, + 'Market Data': 2, + 'Market Status': 4, + 'Financial Calendar': 6, + Order: 3, + 'Grid Trading': 3.5, + Execution: 4, + Assets: 5, + Portfolio: 1, + Alerts: 2, + DCA: 3, + News: 1, + Topics: 2, + Sharelist: 3, + Workspace: 0, + Conversation: 1, +} +const subRank = (name: string) => + name in DOCS_SUB_ORDER ? DOCS_SUB_ORDER[name] : Number.MAX_SAFE_INTEGER + +// Sidebar item class strings — identical to the docs Sidebar (SidebarItem.tsx) +// so they share Tailwind output and render pixel-identically. +const NAV_LEAF = + 'flex items-center w-full text-left bg-transparent border-0 cursor-pointer rounded-lg py-1 px-2 text-[14px] leading-6 no-underline' +const NAV_LEAF_ACTIVE = + 'bg-[color-mix(in_oklab,var(--lb-brand)_10%,transparent)] text-[color:var(--lb-brand)] font-medium' +const NAV_LEAF_IDLE = 'text-[color:var(--lb-fg-2)] hover:text-[color:var(--lb-brand)]' + +function Caret({ open }: { open: boolean }) { + return ( + <span + className="ml-auto inline-flex items-center justify-center shrink-0 text-[color:var(--lb-fg-3)]" + aria-hidden="true"> + <svg + width="16" + height="16" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + className={`transition-transform duration-200 ${open ? 'rotate-90' : ''}`}> + <polyline points="9 18 15 12 9 6" /> + </svg> + </span> + ) +} + +/** Render a flat list of endpoint leaves (shared by groups and subgroups). */ +function EndpointLeaves({ + endpoints, + activeOp, + onSelect, + locale, +}: { + endpoints: EndpointItem[] + activeOp: string | null + onSelect: (id: string) => void + locale: Locale +}) { + return ( + <> + {endpoints.map((ep) => { + const id = epId(ep) + const active = activeOp === id + const summary = stripLeadingVerb( + pickLocale( + ep.operation.summary, + ep.operation['x-summary-zh'], + ep.operation['x-summary-zh-hk'], + locale + ) + ) + return ( + <li key={id} className="list-none"> + <button + type="button" + onClick={() => onSelect(id)} + aria-current={active ? 'page' : undefined} + className={`${NAV_LEAF} ${active ? NAV_LEAF_ACTIVE : NAV_LEAF_IDLE}`}> + <span className={`nav-method method-${ep.method.toLowerCase()}`}>{ep.method}</span> + <span className="flex-1 min-w-0 truncate">{summary}</span> + </button> + </li> + ) + })} + </> + ) +} + +/** A docs subsection: a nested collapsible level between group and endpoints. */ +function ApiSidebarSubGroup({ + sub, + activeOp, + activeWs, + onSelect, + onWs, + locale, + forceOpen, +}: { + sub: SubGroup + activeOp: string | null + activeWs: string | null + onSelect: (id: string) => void + onWs: (id: string) => void + locale: Locale + forceOpen: boolean +}) { + const hasActive = + sub.endpoints.some((ep) => epId(ep) === activeOp) || + !!sub.wsCommands?.some((c) => c.id === activeWs) + const [open, setOpen] = useState(false) + const isOpen = forceOpen || open || hasActive + const label = pickLocale(sub.name, sub.nameZh, sub.nameZhHk, locale) + return ( + <li data-lbus-component="sidebar-subgroup" className="list-none"> + <button + type="button" + onClick={() => setOpen((v) => !v)} + aria-expanded={isOpen} + className="group flex items-center w-full bg-transparent border-0 cursor-pointer text-left rounded-lg pl-3 pr-2 py-1 text-[13px] leading-6"> + <span className="flex-1 min-w-0 truncate font-semibold text-[color:var(--lb-fg-2)] group-hover:text-[color:var(--lb-brand)]"> + {label} + </span> + <Caret open={isOpen} /> + </button> + {isOpen && ( + <ul className="list-none py-0 m-0 pl-2 flex flex-col gap-[2px]" role="list"> + {[ + ...sub.endpoints.map((ep) => { + const id = epId(ep) + const active = activeOp === id + return { + ord: DOCS_ORDER[id] ?? LEAF_FALLBACK, + node: ( + <li key={`e:${id}`} className="list-none"> + <button + type="button" + onClick={() => onSelect(id)} + aria-current={active ? 'page' : undefined} + className={`${NAV_LEAF} ${active ? NAV_LEAF_ACTIVE : NAV_LEAF_IDLE}`}> + <span className={`nav-method method-${ep.method.toLowerCase()}`}>{ep.method}</span> + <span className="flex-1 min-w-0 truncate"> + {stripLeadingVerb( + pickLocale(ep.operation.summary, ep.operation['x-summary-zh'], ep.operation['x-summary-zh-hk'], locale) + )} + </span> + </button> + </li> + ), + } + }), + ...(sub.wsCommands ?? []).map((c) => { + const active = activeWs === c.id + return { + ord: DOCS_ORDER[c.id] ?? LEAF_FALLBACK, + node: ( + <li key={`w:${c.id}`} className="list-none"> + <button + type="button" + onClick={() => onWs(c.id)} + aria-current={active ? 'page' : undefined} + className={`${NAV_LEAF} ${active ? NAV_LEAF_ACTIVE : NAV_LEAF_IDLE}`}> + <span className="nav-method method-ws">WS</span> + <span className="flex-1 min-w-0 truncate"> + {wsLeafName(pickLocale(c.name, c.nameZh, c.nameZhHk, locale))} + </span> + </button> + </li> + ), + } + }), + ] + .sort((a, b) => a.ord - b.ord) + .map((x) => x.node)} + </ul> + )} + </li> + ) +} + +function ApiSidebarGroup({ + group, + wsGroups, + activeOp, + activeWs, + onSelect, + onWs, + locale, + forceOpen, +}: { + group: TagGroup + wsGroups: WsGroupData[] + activeOp: string | null + activeWs: string | null + onSelect: (id: string) => void + onWs: (id: string) => void + locale: Locale + forceOpen: boolean +}) { + const hasActive = + group.endpoints.some((ep) => epId(ep) === activeOp) || + group.subgroups.some( + (sg) => + sg.endpoints.some((ep) => epId(ep) === activeOp) || + !!sg.wsCommands?.some((c) => c.id === activeWs) + ) + const [open, setOpen] = useState(true) + const isOpen = forceOpen || open || hasActive + const label = pickLocale(group.name, group.nameZh, group.nameZhHk, locale) + return ( + <li data-lbus-component="sidebar-group" className="list-none"> + <button + type="button" + onClick={() => setOpen((v) => !v)} + aria-expanded={isOpen} + className="group flex items-center gap-2 w-full bg-transparent border-0 cursor-pointer text-left rounded-lg px-2 py-1 text-[14px] leading-6"> + <span + className="nav-ico inline-flex items-center justify-center shrink-0 w-4 text-[color:var(--lb-fg-3)]" + aria-hidden="true" + dangerouslySetInnerHTML={GROUP_ICONS[group.name] ? { __html: GROUP_ICONS[group.name] } : undefined} + /> + <span className="flex-1 min-w-0 truncate font-bold text-[color:var(--lb-fg-1)] group-hover:text-[color:var(--lb-brand)]"> + {label} + </span> + <Caret open={isOpen} /> + </button> + {isOpen && ( + <ul className="list-none py-0 m-0 flex flex-col gap-[2px]" role="list"> + <EndpointLeaves endpoints={group.endpoints} activeOp={activeOp} onSelect={onSelect} locale={locale} /> + {[ + ...group.subgroups.map((sg) => ({ + rank: subRank(sg.name), + node: ( + <ApiSidebarSubGroup + key={`s:${sg.name}`} + sub={sg} + activeOp={activeOp} + activeWs={activeWs} + onSelect={onSelect} + onWs={onWs} + locale={locale} + forceOpen={forceOpen} + /> + ), + })), + ...wsGroups.map((wg) => ({ + rank: subRank(wg.name), + node: <WsSidebarGroup key={`w:${wg.name}`} group={wg} activeWs={activeWs} onSelect={onWs} locale={locale} forceOpen={forceOpen} />, + })), + ] + .sort((a, b) => a.rank - b.rank) + .map((x) => x.node)} + </ul> + )} + </li> + ) +} + +// WebSocket quote functions — a sidebar group alongside the HTTP endpoint +// groups. Each command opens its own detail view (/docs/api/<id>). +function WsSidebarGroup({ + group, + activeWs, + onSelect, + locale, + forceOpen = false, +}: { + group: WsGroupData + activeWs: string | null + onSelect: (id: string) => void + locale: Locale + forceOpen?: boolean +}) { + const hasActive = group.commands.some((c) => c.id === activeWs) + const [open, setOpen] = useState(false) + const isOpen = forceOpen || open || hasActive + const label = pickLocale(group.name, group.nameZh, group.nameZhHk, locale) + return ( + <li data-lbus-component="sidebar-subgroup" className="list-none"> + <button + type="button" + onClick={() => setOpen((v) => !v)} + aria-expanded={isOpen} + className="group flex items-center w-full bg-transparent border-0 cursor-pointer text-left rounded-lg pl-3 pr-2 py-1 text-[13px] leading-6"> + <span className="flex-1 min-w-0 truncate font-semibold text-[color:var(--lb-fg-2)] group-hover:text-[color:var(--lb-brand)]"> + {label} + </span> + <Caret open={isOpen} /> + </button> + {isOpen && ( + <ul className="list-none py-0 m-0 pl-2 flex flex-col gap-[2px]" role="list"> + {group.commands.map((c) => { + const active = activeWs === c.id + return ( + <li key={c.id} className="list-none"> + <button + type="button" + onClick={() => onSelect(c.id)} + aria-current={active ? 'page' : undefined} + className={`${NAV_LEAF} ${active ? NAV_LEAF_ACTIVE : NAV_LEAF_IDLE}`}> + <span className="nav-method method-ws">WS</span> + <span className="flex-1 min-w-0 truncate">{pickLocale(c.name, c.nameZh, c.nameZhHk, locale)}</span> + </button> + </li> + ) + })} + </ul> + )} + </li> + ) +} + +const L_WS = { + request: { en: 'Request', 'zh-CN': '请求', 'zh-HK': '請求' }, + push: { en: 'Push', 'zh-CN': '推送', 'zh-HK': '推送' }, + callExample: { en: 'Call example', 'zh-CN': '调用示例', 'zh-HK': '調用示例' }, + example: { en: 'Example', 'zh-CN': '示例', 'zh-HK': '示例' }, + responseExample: { en: 'Response example', 'zh-CN': '响应示例', 'zh-HK': '響應示例' }, + pushExample: { en: 'Push example', 'zh-CN': '推送示例', 'zh-HK': '推送示例' }, + reqParams: { en: 'Request parameters', 'zh-CN': '请求参数', 'zh-HK': '請求參數' }, + respFields: { en: 'Response fields', 'zh-CN': '响应字段', 'zh-HK': '響應欄位' }, + pushFields: { en: 'Push fields', 'zh-CN': '推送字段', 'zh-HK': '推送欄位' }, +} as const + +function wsBlocks(cmd: WsCommandItem): CodeBlock[] { + return cmd.requestExamples.map((s) => ({ lang: s.lang.toLowerCase(), code: s.source, label: s.label })) +} + +/** WebSocket command — center column (mirrors the endpoint detail: title, a + * method/URL-style bar, then description). Call example + response live in the + * right rail (WsRail), exactly like an HTTP endpoint. */ +function WsDetail({ cmd, tag, locale, localePrefix, onOpenRail }: { cmd: WsCommandItem; tag: string; locale: Locale; localePrefix: string; onOpenRail?: () => void }) { + const title = pickLocale(cmd.name, cmd.nameZh, cmd.nameZhHk, locale) + const desc = pickLocale(cmd.description, cmd.descriptionZh, cmd.descriptionZhHk, locale) + return ( + <> + {tag && <p className="ep-tag">{tag}</p>} + <div className="ep-titlebar"> + <h1 className="ep-title">{title}</h1> + <CopyPageMenu operationId={cmd.id} localePrefix={localePrefix} locale={locale} /> + </div> + <div className="ep-urlbar" data-lbus-component="ws-bar"> + <span className="ep-method-badge method-ws">WS</span> + <code className="ep-urlbar-url"> + {(cmd.direction === 'push' ? L_WS.push : L_WS.request)[locale]} + {cmd.cmd != null ? ` · cmd ${cmd.cmd}` : ''} + </code> + {onOpenRail && ( + <button type="button" className="ep-urlbar-tryit" onClick={onOpenRail}> + {L_WS.example[locale]} + </button> + )} + </div> + {desc && <div className="prose vp-doc" dangerouslySetInnerHTML={{ __html: renderMd(desc, localePrefix) }} />} + {cmd.quoteCommand && ( + <section className="api-section"> + <QuotePermission command={cmd.quoteCommand} locale={locale} /> + </section> + )} + {cmd.fields && cmd.fields.length > 0 && ( + <section className="api-section"> + <h2>{L_WS.reqParams[locale]}</h2> + <ParamTable rows={rowsFrom(cmd.fields, locale)} locale={locale} /> + </section> + )} + {cmd.responseFields && cmd.responseFields.length > 0 && ( + <section className="api-section"> + <h2>{(cmd.direction === 'push' ? L_WS.pushFields : L_WS.respFields)[locale]}</h2> + <ParamTable rows={rowsFrom(cmd.responseFields, locale)} locale={locale} /> + </section> + )} + {cmd.responseExample && ( + <section className="api-section"> + <h3>{L.responseJson[locale]}</h3> + <CodeTabs + blocks={[{ label: 'JSON', lang: 'json', code: cmd.responseExample.trim() }]} + labelCopy={t(locale, 'api.copy')} + labelCopied={t(locale, 'api.copied')} + /> + </section> + )} + </> + ) +} + +/** WebSocket command — right rail: call example (SDK, language dropdown) + a + * response/push JSON card. Mirrors the endpoint rail (RequestPanel/ResponsePanel). */ +function WsRail({ cmd, locale, labelCopy, labelCopied, open, onClose }: { cmd: WsCommandItem; locale: Locale; labelCopy: string; labelCopied: string; open: boolean; onClose: () => void }) { + const blocks = wsBlocks(cmd) + return ( + <> + <div + className={`api-rail-scrim${open ? ' open' : ''}`} + onClick={onClose} + aria-hidden="true" + /> + <aside className={`api-rail${open ? ' open' : ''}`} data-lbus-component="ws-rail"> + <button + type="button" + className="api-rail-close" + aria-label={L.close[locale]} + onClick={onClose}> + ✕ + </button> + {blocks.length > 0 && ( + <section className="api-rail-card"> + <div className="api-rail-head"> + <span className="api-rail-title">{L_WS.callExample[locale]}</span> + </div> + <CodeDropdown blocks={blocks} labelCopy={labelCopy} labelCopied={labelCopied} /> + </section> + )} + {cmd.responseExample && ( + <section className="api-rail-card"> + <div className="api-rail-head"> + <span className="api-rail-title">{(cmd.direction === 'push' ? L_WS.pushExample : L_WS.responseExample)[locale]}</span> + </div> + <pre className="code-pre ws-response"> + <code dangerouslySetInnerHTML={{ __html: highlightCode(cmd.responseExample.trim(), 'json') }} /> + </pre> + </section> + )} + </aside> + </> + ) +} + function buildSections(ep: EndpointItem, locale: Locale): Section[] { const sections: Section[] = [] - const isZh = locale !== 'en' // Auth section — always shown const authSection: Section = { @@ -83,6 +829,24 @@ function buildSections(ep: EndpointItem, locale: Locale): Section[] { } sections.push(authSection) + // Preferred: a single flat "Parameters" table (docs `## Parameters`). + const xp = ep.operation['x-parameters'] + if (xp?.length) { + sections.push({ + key: 'parameters', + title: PARAM_TITLE[locale] ?? 'Parameters', + note: PARAM_NOTE[locale], + params: xp.map((p) => ({ + name: p.name, + type: p.type ?? 'string', + location: '', + required: !!p.required, + description: pickLocale(p.description, p['x-description-zh'], p['x-description-zh-hk'], locale), + })), + }) + return sections + } + // Path params const pathParams = (ep.operation.parameters ?? []).filter((p) => p.in === 'path') if (pathParams.length) { @@ -94,7 +858,7 @@ function buildSections(ep: EndpointItem, locale: Locale): Section[] { type: p.schema?.type ?? 'string', location: 'path', required: p.required ?? false, - description: (isZh ? p['x-description-zh'] : p.description) ?? p.description ?? '', + description: pickLocale(p.description, p['x-description-zh'], p['x-description-zh-hk'], locale), })), }) } @@ -110,7 +874,7 @@ function buildSections(ep: EndpointItem, locale: Locale): Section[] { type: p.schema?.type ?? 'string', location: 'query', required: p.required ?? false, - description: (isZh ? p['x-description-zh'] : p.description) ?? p.description ?? '', + description: pickLocale(p.description, p['x-description-zh'], p['x-description-zh-hk'], locale), })), }) } @@ -125,7 +889,7 @@ function buildSections(ep: EndpointItem, locale: Locale): Section[] { type: v.type ?? 'object', location: 'body', required: required.includes(name), - description: (isZh ? v['x-description-zh'] : v.description) ?? v.description ?? '', + description: pickLocale(v.description, v['x-description-zh'], v['x-description-zh-hk'], locale), })) sections.push({ key: 'body', @@ -145,7 +909,7 @@ function buildSections(ep: EndpointItem, locale: Locale): Section[] { type: v.type ?? 'object', location: 'response', required: false, - description: (isZh ? v['x-description-zh'] : v.description) ?? v.description ?? '', + description: pickLocale(v.description, v['x-description-zh'], v['x-description-zh-hk'], locale), })) sections.push({ key: 'response', @@ -162,17 +926,9 @@ function buildSections(ep: EndpointItem, locale: Locale): Section[] { function buildCodeBlocks(ep: EndpointItem, serverUrl: string, locale: Locale): CodeBlock[] { const blocks: CodeBlock[] = [] - // Code samples from x-codeSamples - if (ep.operation['x-codeSamples']?.length) { - for (const sample of ep.operation['x-codeSamples']) { - blocks.push({ - lang: sample.lang.toLowerCase(), - code: sample.source, - label: sample.label || sample.lang, - }) - } - } else { - // Auto-generated curl fallback + // Request example = the real HTTP call to the path (curl), not SDK code. + // WebSocket operations have no HTTP request line. + if (ep.method !== 'WEBSOCKET') { blocks.push({ lang: 'bash', code: buildCurl(ep, serverUrl), @@ -193,84 +949,200 @@ function buildCodeBlocks(ep: EndpointItem, serverUrl: string, locale: Locale): C return blocks } +/** Extract the CLI sample (rendered as its own block, docs-style). */ +function cliSample(ep: EndpointItem | null): string { + if (!ep) return '' + const s = ep.operation['x-codeSamples']?.find((x) => x.label === 'CLI' || x.lang.toLowerCase() === 'shell') + return s?.source ?? '' +} + // ── Main component ──────────────────────────────────────────────────────────── export function ApiReference({ rawYaml, locale }: ApiReferenceProps) { const localePrefix = LOCALE_PREFIX[locale] ?? '' // Parse spec once - const { groups, pages, serverUrl } = useMemo(() => parseSpec(rawYaml), [rawYaml]) + const { groups, pages, wsGroups, serverUrl } = useMemo(() => parseSpec(rawYaml), [rawYaml]) + + // Every WS command id (merged into subgroups + standalone groups) so path-based + // routing (/docs/api/<id>) can tell a WS command from a REST operationId. + const wsIdSet = useMemo(() => { + const s = new Set<string>() + for (const g of groups) for (const sg of g.subgroups) for (const c of sg.wsCommands ?? []) s.add(c.id) + for (const g of wsGroups) for (const c of g.commands) s.add(c.id) + return s + }, [groups, wsGroups]) // ── URL state ───────────────────────────────────────────────────────────── - const getQuery = () => { - if (typeof window === 'undefined') return { op: null, page: null } + // Canonical URLs are path-based: `/docs/api/<operationId>` (locale-prefixed). + // The legacy `?op=` / `?page=` query form is still honored for old links. + const apiBase = `${localePrefix}/docs/api` + const getRoute = () => { + if (typeof window === 'undefined') return { op: null, page: null, ws: null } + const path = window.location.pathname.replace(/\/+$/, '') + if (path.startsWith(apiBase + '/')) { + const seg = path.slice(apiBase.length + 1) + if (seg && !seg.includes('/')) { + const id = decodeURIComponent(seg) + // A path segment is a WS command when it matches a known ws id, else an + // endpoint operationId. + return wsIdSet.has(id) + ? { op: null, page: null, ws: id } + : { op: id, page: null, ws: null } + } + } const p = new URLSearchParams(window.location.search) - return { op: p.get('op'), page: p.get('page') } + return { op: p.get('op'), page: p.get('page'), ws: p.get('ws') } } + // With no explicit route, /docs/api lands on the Overview page (no separate + // "API Reference" intro screen). + const resolvePage = (q: { op: string | null; page: string | null; ws: string | null }) => + q.page ?? (!q.op && !q.ws ? 'overview' : null) - const [activeOp, setActiveOp] = useState<string | null>(() => getQuery().op) - const [activePage, setActivePage] = useState<string | null>(() => getQuery().page) + const [activeOp, setActiveOp] = useState<string | null>(() => getRoute().op) + const [activePage, setActivePage] = useState<string | null>(() => resolvePage(getRoute())) + const [activeWs, setActiveWs] = useState<string | null>(() => getRoute().ws) // Listen for popstate useEffect(() => { function onPop() { - const q = getQuery() + const q = getRoute() setActiveOp(q.op) - setActivePage(q.page) + setActivePage(resolvePage(q)) + setActiveWs(q.ws) } window.addEventListener('popstate', onPop) return () => window.removeEventListener('popstate', onPop) + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) - // Navigate to endpoint - const selectEndpoint = useCallback((id: string) => { - const url = new URL(window.location.href) - url.searchParams.set('op', id) - url.searchParams.delete('page') - window.history.pushState({}, '', url.toString()) - setActiveOp(id) - setActivePage(null) - }, []) + // Navigate to endpoint → /docs/api/<id> + // Scroll the content back to the top on navigation, like a fresh page load. + const scrollTop = () => { + if (typeof window !== 'undefined') window.scrollTo({ top: 0 }) + } - // Navigate to page - const selectPage = useCallback((id: string) => { - const url = new URL(window.location.href) - url.searchParams.set('page', id) - url.searchParams.delete('op') - window.history.pushState({}, '', url.toString()) - setActivePage(id) - setActiveOp(null) - }, []) + // Narrow-screen (<lg) only: the left nav is an off-canvas drawer. + const [navOpen, setNavOpen] = useState(false) + + const selectEndpoint = useCallback( + (id: string) => { + window.history.pushState({}, '', `${apiBase}/${id}`) + setActiveOp(id) + setActivePage(null) + setActiveWs(null) + setNavOpen(false) + scrollTop() + }, + [apiBase] + ) + + // Navigate to page → /docs/api?page=<id> (pages stay on the query form) + const selectPage = useCallback( + (id: string) => { + window.history.pushState({}, '', `${apiBase}?page=${id}`) + setActivePage(id) + setActiveOp(null) + setActiveWs(null) + setNavOpen(false) + scrollTop() + }, + [apiBase] + ) + + // Navigate to a WebSocket command → /docs/api/<id> (path-based, like endpoints; + // getRoute resolves the segment to a WS command via wsIdSet). + const selectWs = useCallback( + (id: string) => { + window.history.pushState({}, '', `${apiBase}/${id}`) + setActiveWs(id) + setActiveOp(null) + setActivePage(null) + setNavOpen(false) + scrollTop() + }, + [apiBase] + ) // ── Search ──────────────────────────────────────────────────────────────── - const [query, setQuery] = useState('') - const searchInputRef = useRef<HTMLInputElement>(null) + // The nav filter is driven by the global header search; no in-sidebar box. + const [query] = useState('') + const rootRef = useRef<HTMLDivElement>(null) + + // Add a copy button to each x-page markdown code block (rendered as raw HTML, + // so enhanced imperatively rather than via a React component). + const copyLabel = t(locale, 'api.copy') + const copiedLabel = t(locale, 'api.copied') + useEffect(() => { + const root = rootRef.current + if (!root) return + // Icon-only copy button, matching the reference's other code blocks. + const COPY_SVG = + '<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>' + const CHECK_SVG = + '<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>' + const pres = root.querySelectorAll<HTMLElement>('.api-page-md pre') + const cleanups: Array<() => void> = [] + pres.forEach((pre) => { + if (pre.querySelector('.page-copy-btn')) return + pre.style.position = 'relative' + const btn = document.createElement('button') + btn.type = 'button' + btn.className = 'page-copy-btn' + btn.innerHTML = COPY_SVG + btn.setAttribute('aria-label', copyLabel) + btn.title = copyLabel + const onClick = () => { + const code = pre.querySelector('code')?.textContent ?? pre.textContent ?? '' + navigator.clipboard.writeText(code).then(() => { + btn.innerHTML = CHECK_SVG + btn.setAttribute('aria-label', copiedLabel) + btn.title = copiedLabel + window.setTimeout(() => { + btn.innerHTML = COPY_SVG + btn.setAttribute('aria-label', copyLabel) + btn.title = copyLabel + }, 1500) + }) + } + btn.addEventListener('click', onClick) + pre.appendChild(btn) + cleanups.push(() => btn.remove()) + }) + return () => cleanups.forEach((c) => c()) + }, [activePage, locale, copyLabel, copiedLabel]) const filteredGroups = useMemo(() => { if (!query.trim()) return groups const q = query.toLowerCase() + const matchEp = (ep: EndpointItem) => { + const op = ep.operation + const summary = (op.summary ?? '') + ' ' + (op['x-summary-zh'] ?? '') + ' ' + (op['x-summary-zh-hk'] ?? '') + return ( + ep.path.toLowerCase().includes(q) || + summary.toLowerCase().includes(q) || + ep.method.toLowerCase().includes(q) || + (op.tags ?? []).some((tg) => tg.toLowerCase().includes(q)) + ) + } return groups .map((g) => ({ ...g, - endpoints: g.endpoints.filter((ep) => { - const op = ep.operation - const summary = (op.summary ?? '') + ' ' + (op['x-summary-zh'] ?? '') - return ( - ep.path.toLowerCase().includes(q) || - summary.toLowerCase().includes(q) || - ep.method.toLowerCase().includes(q) || - (op.tags ?? []).some((tg) => tg.toLowerCase().includes(q)) - ) - }), + endpoints: g.endpoints.filter(matchEp), + subgroups: g.subgroups + .map((sg) => ({ ...sg, endpoints: sg.endpoints.filter(matchEp) })) + .filter((sg) => sg.endpoints.length > 0), })) - .filter((g) => g.endpoints.length > 0) + .filter((g) => g.endpoints.length > 0 || g.subgroups.length > 0) }, [groups, query]) // ── Find active endpoint / page ─────────────────────────────────────────── const activeEndpoint = useMemo<EndpointItem | null>(() => { if (!activeOp) return null for (const g of groups) { - const found = g.endpoints.find((ep) => epId(ep) === activeOp) + const found = + g.endpoints.find((ep) => epId(ep) === activeOp) ?? + g.subgroups.flatMap((sg) => sg.endpoints).find((ep) => epId(ep) === activeOp) if (found) return found } return null @@ -282,240 +1154,544 @@ export function ApiReference({ rawYaml, locale }: ApiReferenceProps) { }, [pages, activePage]) // ── Derive data for active endpoint ────────────────────────────────────── - const isZh = locale !== 'en' - const epSections = useMemo<Section[]>( () => (activeEndpoint ? buildSections(activeEndpoint, locale) : []), - [activeEndpoint, locale], + [activeEndpoint, locale] + ) + + // Docs-model data (new endpoints authored with x-request-examples) + const isDocsModel = !!activeEndpoint?.operation['x-request-examples'] + const xparams = activeEndpoint?.operation['x-parameters'] + const epPathParams = useMemo( + () => + rowsFrom( + (xparams ?? []).filter((p) => p.in === 'path'), + locale + ), + [xparams, locale] + ) + const epQueryParams = useMemo( + () => + rowsFrom( + (xparams ?? []).filter((p) => p.in === 'query'), + locale + ), + [xparams, locale] + ) + const epBodyParams = useMemo( + () => + rowsFrom( + (xparams ?? []).filter((p) => p.in === 'body'), + locale + ), + [xparams, locale] + ) + const hasParams = epPathParams.length + epQueryParams.length + epBodyParams.length > 0 + const epRespProps = useMemo( + () => rowsFrom(activeEndpoint?.operation['x-response-properties'], locale), + [activeEndpoint, locale] + ) + // Fallback response JSON example, shown when an endpoint documents no response + // fields so every endpoint still has a Response section. + const epRespExample = useMemo( + () => (activeEndpoint ? buildResponseExample(activeEndpoint) : null), + [activeEndpoint] + ) + + // Authored OAuth (Bearer) request samples for the right rail's OAuth mode. + const epReqExamples = useMemo<CodeBlock[]>( + () => + (activeEndpoint?.operation['x-request-examples'] ?? []).map((s) => ({ + lang: s.lang.toLowerCase(), + code: s.source, + label: s.label, + })), + [activeEndpoint] ) const epCodeBlocks = useMemo<CodeBlock[]>( () => (activeEndpoint ? buildCodeBlocks(activeEndpoint, serverUrl, locale) : []), - [activeEndpoint, serverUrl, locale], + [activeEndpoint, serverUrl, locale] ) const epProse = useMemo<string>(() => { if (!activeEndpoint) return '' - const raw = isZh - ? (activeEndpoint.operation['x-description-zh'] ?? activeEndpoint.operation.description ?? '') - : (activeEndpoint.operation.description ?? '') - const { prose } = splitDescriptionAndCode(raw) - return prose ? renderMd(prose, localePrefix) : '' - }, [activeEndpoint, isZh, localePrefix]) - - const epPathSegs = useMemo( - () => (activeEndpoint ? formatPath(activeEndpoint.path) : []), - [activeEndpoint], - ) + const op = activeEndpoint.operation + const raw = pickLocale(op.description, op['x-description-zh'], op['x-description-zh-hk'], locale) + // Render the full description (prose + embedded code blocks such as + // protobuf) so WebSocket / quote message schemas show inline, matching the + // docs page style. + return raw ? renderMd(raw, localePrefix) : '' + }, [activeEndpoint, locale, localePrefix]) + + const epCli = useMemo(() => cliSample(activeEndpoint), [activeEndpoint]) const epTag = useMemo<string>(() => { if (!activeEndpoint) return '' const tag = activeEndpoint.operation.tags?.[0] ?? '' - // find zh name from groups + // find localized name from groups const grp = groups.find((g) => g.name === tag) - return isZh ? (grp?.nameZh ?? tag) : tag - }, [activeEndpoint, groups, isZh]) + return pickLocale(tag, grp?.nameZh, grp?.nameZhHk, locale) + }, [activeEndpoint, groups, locale]) // ── Page content ────────────────────────────────────────────────────────── - const pageHtml = useMemo<string>(() => { - if (!activePg) return '' - const raw = isZh ? (activePg.contentZh ?? activePg.content) : activePg.content - return raw ? renderMd(raw, localePrefix) : '' - }, [activePg, isZh, localePrefix]) - - // ── Copy path ───────────────────────────────────────────────────────────── - const [pathCopied, setPathCopied] = useState(false) - function copyPath() { - if (!activeEndpoint) return - navigator.clipboard.writeText(activeEndpoint.path).then(() => { - setPathCopied(true) - setTimeout(() => setPathCopied(false), 1800) - }) - } + // Page markdown, split at the [[SIGNING_TABS]] marker so a CodeTabs component + // can be injected in the middle (Authentication page signing implementations). + const pageParts = useMemo(() => { + if (!activePg) return { before: '', after: '' } + const raw = pickLocale(activePg.content, activePg.contentZh, activePg.contentZhHk, locale) + const [before, after = ''] = raw.split('[[SIGNING_TABS]]') + return { + before: before ? renderMd(before, localePrefix) : '', + after: after ? renderMd(after, localePrefix) : '', + } + }, [activePg, locale, localePrefix]) + + // The active WebSocket command (detail view) + its group, if any. + const activeWsCmd = useMemo<WsCommandItem | null>(() => { + if (!activeWs) return null + for (const g of wsGroups) { + const c = g.commands.find((x) => x.id === activeWs) + if (c) return c + } + // WS commands merged into topical subgroups live on the tag groups. + for (const g of groups) { + for (const s of g.subgroups) { + const c = s.wsCommands?.find((x) => x.id === activeWs) + if (c) return c + } + } + return null + }, [activeWs, wsGroups, groups]) + const activeWsGroup = useMemo<WsGroupData | null>(() => { + if (!activeWs) return null + const g0 = wsGroups.find((g) => g.commands.some((c) => c.id === activeWs)) + if (g0) return g0 + for (const g of groups) { + for (const s of g.subgroups) { + if (s.wsCommands?.some((c) => c.id === activeWs)) + return { name: s.name, nameZh: s.nameZh, nameZhHk: s.nameZhHk, tag: g.name, commands: s.wsCommands } + } + } + return null + }, [activeWs, wsGroups, groups]) + // Top-level group label for the WS eyebrow/breadcrumb (mirrors epTag for REST). + const wsTag = useMemo<string>(() => { + if (!activeWsGroup) return '' + const tag = activeWsGroup.tag ?? activeWsGroup.name + const grp = groups.find((g) => g.name === tag) + return pickLocale(tag, grp?.nameZh, grp?.nameZhHk, locale) + }, [activeWsGroup, groups, locale]) + + // Live TryIt response for the right-rail Response panel; cleared per endpoint. + const [liveResp, setLiveResp] = useState<ApiResponse | null>(null) + // Narrow-screen only: the request rail becomes an on-demand drawer. + const [railOpen, setRailOpen] = useState(false) + useEffect(() => { + setLiveResp(null) + setRailOpen(false) + }, [activeOp, activeWs]) + useEffect(() => { + if (!railOpen) return + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') setRailOpen(false) + } + document.addEventListener('keydown', onKey) + return () => document.removeEventListener('keydown', onKey) + }, [railOpen]) + useEffect(() => { + if (!navOpen) return + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') setNavOpen(false) + } + document.addEventListener('keydown', onKey) + return () => document.removeEventListener('keydown', onKey) + }, [navOpen]) + // Narrow-screen reveal bar: slides down once scrolled (mirrors docs LocalNav). + const [navRevealed, setNavRevealed] = useState(false) + useEffect(() => { + const onScroll = () => setNavRevealed(window.scrollY > 100) + onScroll() + window.addEventListener('scroll', onScroll, { passive: true }) + return () => window.removeEventListener('scroll', onScroll) + }, []) // ── Render ──────────────────────────────────────────────────────────────── - const showIntro = !activeOp && !activePage - const showPage = !!activePg - const showEndpoint = !!activeEndpoint + const showWs = !!activeWsCmd + const showPage = !!activePg && !showWs + const showEndpoint = !!activeEndpoint && !showWs + + // Breadcrumb trail (Home is prepended by DocsBreadcrumb). + const crumbs: { text: string; href?: string }[] = + showWs && activeWsCmd && activeWsGroup + ? [ + ...(wsTag ? [{ text: wsTag }] : []), + { text: pickLocale(activeWsCmd.name, activeWsCmd.nameZh, activeWsCmd.nameZhHk, locale) }, + ] + : showEndpoint && activeEndpoint + ? [ + ...(epTag ? [{ text: epTag }] : []), + { + text: stripLeadingVerb( + pickLocale( + activeEndpoint.operation.summary, + activeEndpoint.operation['x-summary-zh'], + activeEndpoint.operation['x-summary-zh-hk'], + locale + ) + ), + }, + ] + : activePg + ? [{ text: pickLocale(activePg.title, activePg.titleZh, activePg.titleZhHk, locale) }] + : [] return ( - <div data-lbus-component="api-reference" className="api-reference-page"> - {/* ── Sidebar ── */} - <aside data-lbus-component="api-sidebar" className="api-sidebar"> - <div className="sidebar-search"> - <input - ref={searchInputRef} - className="search-input" - type="text" - placeholder={t(locale, 'api.search')} - value={query} - onChange={(e) => setQuery(e.target.value)} - /> + <EnvProvider> + <div ref={rootRef} data-lbus-component="api-reference" className="docs-layout"> + {/* Narrow-screen nav — same mechanism as docs/cli pages: a reveal bar that + slides down on scroll with a "菜单" toggle, a slide-in sidebar drawer and + a backdrop. (Re-created here rather than importing the app shell, which + this package can't depend on; markup/behaviour mirror LocalNav/Sidebar/ + Backdrop.) */} + <div + className="lg:hidden fixed left-0 right-0 top-[60px] z-20 transition-transform duration-200 will-change-transform" + style={{ transform: navRevealed ? 'translateY(0)' : 'translateY(-100%)', pointerEvents: navRevealed ? 'auto' : 'none' }} + data-lbus-component="local-nav"> + <div className="flex items-center h-12 px-4 bg-[var(--lb-bg-1)] border-b border-[color:var(--lb-stroke)]"> + <button + type="button" + className="inline-flex items-center gap-2 bg-transparent border-0 cursor-pointer text-[12px] font-medium text-[color:var(--lb-fg-3)] hover:text-[color:var(--lb-fg-1)]" + aria-label={L.menu[locale]} + aria-expanded={navOpen} + onClick={() => setNavOpen(true)}> + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"> + <line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /> + </svg> + {L.menu[locale]} + </button> </div> - <div className="sidebar-scroll"> - {/* Static pages */} - {pages.map((pg) => ( - <button - key={pg.id} - type="button" - className={`nav-item${activePage === pg.id ? ' is-active' : ''}`} - onClick={() => selectPage(pg.id)} - > - {isZh ? (pg.titleZh ?? pg.title) : pg.title} - </button> - ))} - {/* Tag groups */} - {filteredGroups.map((g) => ( - <div key={g.name} className="tag-group"> - <p className="tag-label">{isZh ? (g.nameZh ?? g.name) : g.name}</p> - {g.endpoints.map((ep) => { - const id = epId(ep) - const summary = isZh - ? (ep.operation['x-summary-zh'] ?? ep.operation.summary ?? '') - : (ep.operation.summary ?? '') + </div> + {navOpen && ( + <div + className="fixed inset-0 bg-black/40 z-20 lg:hidden" + data-lbus-component="backdrop" + aria-hidden="true" + onClick={() => setNavOpen(false)} + /> + )} + {/* ── Sidebar (docs sidebar DOM) ── */} + <aside + data-lbus-component="sidebar" + className={`fixed inset-y-0 left-0 z-40 w-64 overflow-y-auto border-r border-[color:var(--lb-stroke)] bg-[var(--lbus-c-bg)] px-6 py-6 transition-transform duration-200 ${navOpen ? 'translate-x-0' : '-translate-x-full'} lg:sticky lg:top-[60px] lg:z-auto lg:inset-y-auto lg:h-[calc(100vh-60px)] lg:translate-x-0`} + aria-label="API navigation"> + <nav aria-label="API navigation"> + {/* Static pages — a bare (header-less) group, like docs Overview/Getting Started */} + {pages.length > 0 && ( + <ul className="list-none p-0 m-0 flex flex-col gap-[2px]" role="list"> + {pages.map((pg) => { + const active = activePage === pg.id + const icon = pg.icon ? PAGE_ICONS[pg.icon] : undefined return ( - <button - key={id} - type="button" - className={`nav-item${activeOp === id ? ' is-active' : ''}`} - onClick={() => selectEndpoint(id)} - > - <span className={`nav-method method-${ep.method.toLowerCase()}`}> - {ep.method} - </span> - <span className="nav-label">{summary}</span> - </button> + <li key={pg.id} className="list-none"> + <button + type="button" + onClick={() => selectPage(pg.id)} + aria-current={active ? 'page' : undefined} + className={`${NAV_LEAF} gap-2 ${active ? NAV_LEAF_ACTIVE : NAV_LEAF_IDLE}`}> + <span + className="nav-ico inline-flex items-center justify-center shrink-0 w-4 text-[color:var(--lb-fg-3)]" + aria-hidden="true" + dangerouslySetInnerHTML={icon ? { __html: icon } : undefined} + /> + <span className="flex-1 min-w-0 truncate"> + {pickLocale(pg.title, pg.titleZh, pg.titleZhHk, locale)} + </span> + </button> + </li> ) })} + </ul> + )} + {/* Tag groups — each a collapsible section separated by a divider. WS + command groups are merged into the tag they belong to (x-tag). */} + {filteredGroups.map((g) => ( + <div key={g.name} className="border-t border-[color:var(--app-card-stroke)] mt-[10px] pt-[10px]"> + <ul className="list-none p-0 m-0 flex flex-col gap-[2px]" role="list"> + <ApiSidebarGroup + group={g} + wsGroups={wsGroups.filter((w) => w.tag === g.name)} + activeOp={activeOp} + activeWs={activeWs} + onSelect={selectEndpoint} + onWs={selectWs} + locale={locale} + forceOpen={!!query.trim()} + /> + </ul> </div> ))} - </div> + {/* WS groups whose tag has no matching HTTP group (fallback) */} + {wsGroups.filter((w) => !filteredGroups.some((g) => g.name === w.tag)).length > 0 && ( + <div className="border-t border-[color:var(--app-card-stroke)] mt-[10px] pt-[10px]"> + <ul className="list-none p-0 m-0 flex flex-col gap-[2px]" role="list"> + {wsGroups + .filter((w) => !filteredGroups.some((g) => g.name === w.tag)) + .map((wg) => ( + <WsSidebarGroup key={wg.name} group={wg} activeWs={activeWs} onSelect={selectWs} locale={locale} forceOpen={!!query.trim()} /> + ))} + </ul> + </div> + )} + </nav> </aside> - {/* ── Intro (nothing selected) ── */} - {showIntro && ( - <div data-lbus-component="api-intro" className="api-intro"> - <div className="intro-content"> - <h2 className="intro-title">{t(locale, 'api.intro.title')}</h2> - <p className="intro-desc">{t(locale, 'api.intro.desc')}</p> - <div className="intro-cards"> - <div className="intro-card"> - <strong className="intro-card-title">{t(locale, 'api.intro.httpTitle')}</strong> - <p className="intro-card-desc">{t(locale, 'api.intro.httpDesc')}</p> - </div> - <div className="intro-card"> - <strong className="intro-card-title">{t(locale, 'api.intro.wsTitle')}</strong> - <p className="intro-card-desc">{t(locale, 'api.intro.wsDesc')}</p> - </div> - </div> - <p className="intro-hint">{t(locale, 'api.intro.hint')}</p> - </div> - </div> - )} + <div className="docs-body"> + <div className="docs-inner"> + <div className={`docs-main${(showEndpoint && isDocsModel) || showWs ? ' has-rail' : ''}`}> + <article className="docs-content"> + <DocsBreadcrumb items={crumbs} locale={locale} /> + {/* ── WebSocket command detail (center) ── */} + {showWs && activeWsCmd && ( + <WsDetail cmd={activeWsCmd} tag={wsTag} locale={locale} localePrefix={localePrefix} onOpenRail={() => setRailOpen(true)} /> + )} + {/* ── Page content ── */} + {showPage && activePg && ( + <> + {/* Inject the page title as H1 only when the body has none (mirrors DocsLayout). */} + {!/<h1[ >]/.test(pageParts.before) && ( + <h1 className="ep-title"> + {pickLocale(activePg.title, activePg.titleZh, activePg.titleZhHk, locale)} + </h1> + )} + <div className="api-page-md" dangerouslySetInnerHTML={{ __html: pageParts.before }} /> + {activePg.codeTabs?.length ? ( + <div className="api-page-codetabs"> + <CodeTabs + blocks={activePg.codeTabs.map((s) => ({ + lang: s.lang.toLowerCase(), + code: s.source, + label: s.label, + }))} + labelCopy={t(locale, 'api.copy')} + labelCopied={t(locale, 'api.copied')} + /> + </div> + ) : null} + {pageParts.after && ( + <div className="api-page-md" dangerouslySetInnerHTML={{ __html: pageParts.after }} /> + )} + </> + )} - {/* ── Page content ── */} - {showPage && ( - <div data-lbus-component="api-main-page" className="api-main"> - <div - className="api-content api-page-content vp-doc prose" - dangerouslySetInnerHTML={{ __html: pageHtml }} - /> - </div> - )} + {/* ── Endpoint detail ── */} + {showEndpoint && activeEndpoint && ( + <> + {epTag && <p className="ep-tag">{epTag}</p>} + <div className="ep-titlebar"> + <h1 className="ep-title"> + {stripLeadingVerb( + pickLocale( + activeEndpoint.operation.summary, + activeEndpoint.operation['x-summary-zh'], + activeEndpoint.operation['x-summary-zh-hk'], + locale + ) + )} + </h1> + <CopyPageMenu + operationId={activeEndpoint.operation.operationId} + localePrefix={localePrefix} + locale={locale} + /> + </div> - {/* ── Endpoint detail ── */} - {showEndpoint && activeEndpoint && ( - <div data-lbus-component="api-main-endpoint" className="api-main api-main--split"> - {/* Left column: metadata + params */} - <div className="api-content"> - {epTag && <p className="ep-tag">{epTag}</p>} - <h1 className="ep-title"> - {isZh - ? (activeEndpoint.operation['x-summary-zh'] ?? activeEndpoint.operation.summary ?? '') - : (activeEndpoint.operation.summary ?? '')} - </h1> - - {/* Path + method badge */} - <div className="ep-path"> - <span className={`ep-method-badge method-${activeEndpoint.method.toLowerCase()}`}> - {activeEndpoint.method} - </span> - <span className="ep-path-text"> - {epPathSegs.map((seg, i) => ( - <span key={i} className={seg.isParam ? 'path-param' : 'path-static'}> - {seg.text} - </span> - ))} - </span> - <button - type="button" - className="path-copy-btn" - title={t(locale, 'api.pathCopy')} - onClick={copyPath} - > - {pathCopied ? '✓' : t(locale, 'api.pathCopy')} - </button> - </div> + {/* URL bar: method + full URL + copy URL + (narrow-only) Try it */} + <EndpointUrlBar + method={activeEndpoint.method} + path={activeEndpoint.path} + locale={locale} + onTryIt={isDocsModel ? () => setRailOpen(true) : undefined} + tryItLabel={L.tryIt[locale]} + /> + + {/* Authorization — prerequisite for calling the endpoint, kept near the top */} + <section className="api-section"> + <div className="api-auth-head"> + <h2>{L.authorization[locale]}</h2> + <AuthModeSelect locale={locale} /> + </div> + <AuthTable locale={locale} /> + </section> - {/* Quote permission badge */} - {activeEndpoint.operation['x-quote-command'] && ( - <QuotePermission - command={activeEndpoint.operation['x-quote-command']} + {/* Permission (quote permission) — endpoint-specific, stays near the top */} + {(activeEndpoint.operation['x-quote-command'] || + activeEndpoint.operation['x-quote-level'] || + activeEndpoint.operation['x-quote-market']) && ( + <section className="api-section"> + <QuotePermission + command={activeEndpoint.operation['x-quote-command']} + level={activeEndpoint.operation['x-quote-level']} + market={activeEndpoint.operation['x-quote-market']} + locale={locale} + /> + </section> + )} + + {/* Prose description */} + {epProse && <div className="prose vp-doc" dangerouslySetInnerHTML={{ __html: epProse }} />} + + {/* CLI — reuse the docs CliCommand card for pixel parity */} + {epCli && <CliCommand code={epCli} locale={locale} />} + + {isDocsModel ? ( + <> + {/* ── Request ── */} + <h2 id="request">{L.request[locale]}</h2> + + {hasParams ? ( + <div id="parameters"> + {epPathParams.length > 0 && ( + <section className="api-section"> + <h3>{L.pathParams[locale]}</h3> + <ParamTable rows={epPathParams} locale={locale} /> + </section> + )} + {epQueryParams.length > 0 && ( + <section className="api-section"> + <h3>{L.queryParams[locale]}</h3> + <ParamTable rows={epQueryParams} locale={locale} /> + </section> + )} + {epBodyParams.length > 0 && ( + <section className="api-section"> + <h3>{L.requestBody[locale]}</h3> + <ParamTable rows={epBodyParams} locale={locale} /> + </section> + )} + </div> + ) : ( + <p className="param-fallback">{t(locale, 'api.fallback')}</p> + )} + + {/* Request Example + Response JSON now render in the right rail. */} + + {/* ── Response ── field table (when documented) + JSON example */} + {(epRespProps.length > 0 || epRespExample) && ( + <> + <h2 id="response">{L.response[locale]}</h2> + {epRespProps.length > 0 && ( + <section id="response-properties" className="api-section"> + <h3>{L.responseProps[locale]}</h3> + <ParamTable rows={epRespProps} locale={locale} /> + </section> + )} + {epRespExample && ( + <section className="api-section api-section--code"> + <h3>{L.responseJson[locale]}</h3> + <CodeTabs + blocks={[{ label: 'JSON', lang: 'json', code: epRespExample }]} + labelCopy={t(locale, 'api.copy')} + labelCopied={t(locale, 'api.copied')} + /> + </section> + )} + </> + )} + + {/* ── Error Code ── */} + <h2 id="error-code">{L.errorCode[locale]}</h2> + <p className="error-code-note"> + {L.errorCodeBody[locale]} + <a href={`${localePrefix}/docs/error-codes`}>{L.errorCodeLink[locale]}</a> + {locale === 'en' ? ' page for the full list of error codes.' : '。'} + </p> + </> + ) : ( + <> + {/* Legacy scalar rendering (un-migrated ops) */} + {epSections.map((section) => ( + <section key={section.key} className="api-section"> + <h2>{section.title}</h2> + {section.note && <p className="section-note">{section.note}</p>} + {section.params.length === 0 ? ( + <p className="param-fallback">{t(locale, 'api.fallback')}</p> + ) : ( + <ParamTable rows={section.params} locale={locale} /> + )} + </section> + ))} + {epCodeBlocks.length > 0 && ( + <section className="api-section api-section--code"> + <CodePanel + blocks={epCodeBlocks} + labelCopy={t(locale, 'api.copy')} + labelCopied={t(locale, 'api.copied')} + /> + </section> + )} + </> + )} + + </> + )} + </article> + + {/* Right rail: WebSocket call example + response/push */} + {showWs && activeWsCmd && ( + <WsRail + cmd={activeWsCmd} locale={locale} + labelCopy={t(locale, 'api.copy')} + labelCopied={t(locale, 'api.copied')} + open={railOpen} + onClose={() => setRailOpen(false)} /> )} - {/* Prose description */} - {epProse && ( - <div - className="prose vp-doc" - dangerouslySetInnerHTML={{ __html: epProse }} - /> + {/* Right rail: Request + Response panels (TryIt debugger). Beside the + content on wide screens; a right-side drawer on narrow ones. */} + {showEndpoint && isDocsModel && activeEndpoint && ( + <> + <div + className={`api-rail-scrim${railOpen ? ' open' : ''}`} + onClick={() => setRailOpen(false)} + aria-hidden="true" + /> + <aside + className={`api-rail${railOpen ? ' open' : ''}`} + data-lbus-component="api-rail"> + <button + type="button" + className="api-rail-close" + aria-label={L.close[locale]} + onClick={() => setRailOpen(false)}> + ✕ + </button> + <RequestPanel + method={activeEndpoint.method} + path={activeEndpoint.path} + xparams={activeEndpoint.operation['x-parameters'] ?? []} + oauthBlocks={epReqExamples} + locale={locale} + onResponse={setLiveResp} + labelCopy={t(locale, 'api.copy')} + labelCopied={t(locale, 'api.copied')} + /> + <ResponsePanel + examples={endpointResponseExamples(activeEndpoint)} + live={liveResp} + locale={locale} + /> + </aside> + </> )} - - {/* Param sections */} - {epSections.map((section) => ( - <section key={section.key} className="api-section"> - <h4 className="section-title">{section.title}</h4> - <div className="param-list"> - {section.params.length === 0 ? ( - <p className="param-fallback">{t(locale, 'api.fallback')}</p> - ) : ( - section.params.map((row) => ( - <div key={row.name} className="param-row"> - <div className="param-meta"> - <code className="param-name">{row.name}</code> - <span className="param-type">{row.type}</span> - <span - className={`param-required ${row.required ? 'is-required' : 'is-optional'}`} - > - {row.required - ? t(locale, 'api.param.required') - : t(locale, 'api.param.optional')} - </span> - </div> - {row.description && ( - <p className="param-desc">{row.description}</p> - )} - </div> - )) - )} - </div> - </section> - ))} </div> - - {/* Right column: code samples */} - {epCodeBlocks.length > 0 && ( - <CodePanel - blocks={epCodeBlocks} - labelCopy={t(locale, 'api.copy')} - labelCopied={t(locale, 'api.copied')} - /> - )} + <DocFooterRow locale={locale} /> </div> - )} + </div> </div> + </EnvProvider> ) } diff --git a/packages/api-reference/src/AuthTable.tsx b/packages/api-reference/src/AuthTable.tsx new file mode 100644 index 000000000..921e335ac --- /dev/null +++ b/packages/api-reference/src/AuthTable.tsx @@ -0,0 +1,154 @@ +/** + * AuthTable — the Authorization section's header table. Reads the selected auth + * mode from EnvContext and lists the required headers accordingly: the full HMAC + * signing set (Signed) or the single Bearer header (OAuth). Rendered as a child + * of ApiReference so it lives inside <EnvProvider>. + */ +import type { Locale } from '@longbridge/openapi-utils' +import { useEnv, type AuthMode } from './EnvContext' +import { Dropdown } from './Dropdown' + +const L = { + name: { en: 'Name', 'zh-CN': '名称', 'zh-HK': '名稱' }, + type: { en: 'Type', 'zh-CN': '类型', 'zh-HK': '類型' }, + required: { en: 'Required', 'zh-CN': '必填', 'zh-HK': '必填' }, + description: { en: 'Description', 'zh-CN': '说明', 'zh-HK': '說明' }, + authToken: { + en: 'The account Access Token from the [OpenAPI dashboard](https://open.longbridge.com/dashboard/tokens) — passed as the raw token (no `Bearer` prefix) and included in the signature calculation.', + 'zh-CN': '[OpenAPI 后台](https://open.longbridge.com/dashboard/tokens) 上的账户 Access Token,作为原始 token 传入(不带 `Bearer` 前缀),并参与签名计算。', + 'zh-HK': '[OpenAPI 後台](https://open.longbridge.com/dashboard/tokens) 上的賬戶 Access Token,作為原始 token 傳入(不帶 `Bearer` 前綴),並參與簽名計算。', + }, + apiKey: { + en: 'Your App Key.', + 'zh-CN': '你的 App Key。', + 'zh-HK': '你的 App Key。', + }, + timestamp: { + en: 'Request timestamp in **seconds** (Unix epoch).', + 'zh-CN': '请求时间戳,单位**秒**(Unix 时间)。', + 'zh-HK': '請求時間戳,單位**秒**(Unix 時間)。', + }, + signature: { + en: 'HMAC-SHA256 signature. Format: `HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature=<sig>`.', + 'zh-CN': 'HMAC-SHA256 签名。格式:`HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature=<sig>`。', + 'zh-HK': 'HMAC-SHA256 簽名。格式:`HMAC-SHA256 SignedHeaders=authorization;x-api-key;x-timestamp, Signature=<sig>`。', + }, + bearer: { + en: 'The access token obtained after completing the OAuth 2.0 authorization flow (the OAuth token endpoint), sent as `Authorization: Bearer <access_token>`. No signature required.', + 'zh-CN': '完成 OAuth 2.0 授权流程(OAuth token 端点)后获取的 access token,通过 `Authorization: Bearer <access_token>` 发送,无需签名。', + 'zh-HK': '完成 OAuth 2.0 授權流程(OAuth token 端點)後獲取的 access token,通過 `Authorization: Bearer <access_token>` 發送,無需簽名。', + }, + signNote: { + en: 'Signed mode: every request is signed with your App Key/Secret. Header set:', + 'zh-CN': '签名方式:每个请求用 App Key / Secret 签名。请求头如下:', + 'zh-HK': '簽名方式:每個請求用 App Key / Secret 簽名。請求頭如下:', + }, + oauthNote: { + en: 'OAuth mode: obtain an access token via the OAuth 2.0 flow, then pass it directly as a Bearer credential (no signing).', + 'zh-CN': 'OAuth 方式:先通过 OAuth 2.0 流程获取 access token,再用 Bearer 方式直接携带(无需签名)。', + 'zh-HK': 'OAuth 方式:先通過 OAuth 2.0 流程獲取 access token,再用 Bearer 方式直接攜帶(無需簽名)。', + }, + oauthDoc: { + en: 'How to get an OAuth token → Getting Started', + 'zh-CN': '如何获取 OAuth token → 快速开始', + 'zh-HK': '如何獲取 OAuth token → 快速開始', + }, + sign: { en: 'API Key', 'zh-CN': 'API Key', 'zh-HK': 'API Key' }, + oauth: { en: 'OAuth 2.0', 'zh-CN': 'OAuth 2.0', 'zh-HK': 'OAuth 2.0' }, +} as const + +// Stable ASCII anchor of the "OAuth 2.0 (Recommended)" heading in Getting +// Started (defined via `{#oauth-2-0}` in getting-started.mdx, all locales). +const OAUTH_ANCHOR = 'oauth-2-0' +// autocorrect-enable + +// Render `[text](url)` links, `**bold**` and inline `` `code` `` in descriptions. +function rich(text: string): string { + return text + .replace( + /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, + '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>' + ) + .replace(/`([^`]+)`/g, '<code>$1</code>') + .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>') +} + +interface Row { + name: string + desc: string +} + +/** Auth-method dropdown for the Authorization section heading row (API Key / + * OAuth 2.0). Custom popover (not a native <select>) so the option list matches + * the docs styling. Shares state with the request panel via EnvContext. */ +export function AuthModeSelect({ locale }: { locale: Locale }) { + const { authMode, setAuthMode } = useEnv() + return ( + <Dropdown<AuthMode> + className="api-auth-select" + ariaLabel="auth method" + value={authMode} + options={[ + { value: 'sign', label: L.sign[locale] }, + { value: 'oauth', label: L.oauth[locale] }, + ]} + onChange={setAuthMode} + /> + ) +} + +export function AuthTable({ locale }: { locale: Locale }) { + const { authMode } = useEnv() + + const rows: Row[] = + authMode === 'oauth' + ? [{ name: 'Authorization', desc: L.bearer[locale] }] + : [ + { name: 'Authorization', desc: L.authToken[locale] }, + { name: 'X-Api-Key', desc: L.apiKey[locale] }, + { name: 'X-Timestamp', desc: L.timestamp[locale] }, + { name: 'X-Api-Signature', desc: L.signature[locale] }, + ] + + const prefix = locale === 'en' ? '' : `/${locale}` + return ( + <> + <p className="api-auth-note"> + {(authMode === 'oauth' ? L.oauthNote : L.signNote)[locale]} + {authMode === 'oauth' && ( + <> + {' '} + <a + href={`${prefix}/docs/getting-started#${OAUTH_ANCHOR}`} + className="api-auth-link" + data-astro-reload> + {L.oauthDoc[locale]} + </a> + </> + )} + </p> + <table className="api-fields"> + <thead> + <tr> + <th>{L.name[locale]}</th> + <th>{L.type[locale]}</th> + <th>{L.required[locale]}</th> + <th>{L.description[locale]}</th> + </tr> + </thead> + <tbody> + {rows.map((r) => ( + <tr key={r.name}> + <td> + <code>{r.name}</code> + </td> + <td>string · header</td> + <td>{L.required[locale]}</td> + <td dangerouslySetInnerHTML={{ __html: rich(r.desc) }} /> + </tr> + ))} + </tbody> + </table> + </> + ) +} diff --git a/packages/api-reference/src/CodeSample.tsx b/packages/api-reference/src/CodeSample.tsx index 129e697c4..891c330a6 100644 --- a/packages/api-reference/src/CodeSample.tsx +++ b/packages/api-reference/src/CodeSample.tsx @@ -5,6 +5,8 @@ * No dependency on @longbridge/openapi-ui — intentionally standalone. */ import type { CodeBlock } from './openapi-loader' +import { IconCopy, IconCheck } from './EndpointUrlBar' +import { Dropdown } from './Dropdown' // ── Escape ──────────────────────────────────────────────────────────────────── @@ -19,78 +21,44 @@ function esc(s: string): string { // ── Syntax highlighter ──────────────────────────────────────────────────────── -function highlightCode(code: string, lang: string): string { +export function highlightCode(code: string, lang: string): string { if (lang === 'json') { return code.replace( /("(?:[^"\\]|\\.)*")(\s*:)|("(?:[^"\\]|\\.)*")|(-?\b\d+\.?\d*(?:[eE][+-]?\d+)?\b)|\b(true|false|null)\b/g, (_m, key, colon, str, num, bool) => { - if (key !== undefined) - return `<span class="hl-k">${esc(key)}</span>${esc(colon ?? '')}` + if (key !== undefined) return `<span class="hl-k">${esc(key)}</span>${esc(colon ?? '')}` if (str !== undefined) return `<span class="hl-s">${esc(str)}</span>` if (num !== undefined) return `<span class="hl-n">${esc(num)}</span>` if (bool !== undefined) return `<span class="hl-b">${esc(bool)}</span>` return esc(_m) - }, + } ) } - if (['bash', 'shell', 'sh', 'curl'].includes(lang)) { - const lines = code.split('\n') - return lines - .map((line, idx) => { - // line continuation backslash - const continuation = line.endsWith('\\') - const bare = continuation ? line.slice(0, -1) : line - - const highlightLine = (s: string): string => { - // --flag or -f patterns - s = s.replace(/(--[\w-]+=?|-[a-zA-Z])\b/g, (f) => `<span class="hl-flag">${esc(f)}</span>`) - // quoted strings ' ... ' - s = s.replace(/'([^']*)'/g, (_m, inner) => `'<span class="hl-s">${esc(inner)}</span>'`) - // remaining plain text: escape non-tagged portions - // (already done inline above; non-matched chars pass through as-is after esc calls) - return s - } - - if (idx === 0) { - // first line: first token is the command - const firstSpace = bare.search(/\s/) - if (firstSpace === -1) { - const result = - `<span class="hl-cmd">${esc(bare)}</span>` + - (continuation ? '<span class="hl-punct"> \\</span>' : '') - return result - } - const cmd = bare.slice(0, firstSpace) - const rest = bare.slice(firstSpace) - return ( - `<span class="hl-cmd">${esc(cmd)}</span>` + - highlightLine(esc(rest)) + - (continuation ? '<span class="hl-punct"> \\</span>' : '') - ) - } - - return highlightLine(esc(bare)) + (continuation ? '<span class="hl-punct"> \\</span>' : '') - }) - .join('\n') - } - - if (['typescript', 'javascript', 'ts', 'js', 'python', 'py'].includes(lang)) { - const escaped = esc(code) - // comments first (so strings inside comments don't get double-wrapped) - return escaped - .replace(/(\/\/[^\n]*|#[^\n]*)/g, (c) => `<span class="hl-comment">${c}</span>`) - .replace( - /("[^&]*"|'[^&]*'|`[^`]*`)/g, - (s) => `<span class="hl-s">${s}</span>`, - ) - .replace( - /\b(const|let|var|function|return|import|export|from|async|await|class|new|typeof|instanceof|def|lambda|yield|for|while|if|elif|else|in|not|and|or|True|False|None|pass|with|as|raise|try|except|finally)\b/g, - (kw) => `<span class="hl-b">${kw}</span>`, - ) - } - - return esc(code) + // Generic single-pass highlighter for all other languages. + // Corruption-safe: strings are matched as whole tokens, so `https://` inside a + // string is never mis-parsed as a `//` comment, and no replacement ever runs + // over previously-inserted markup. + const escaped = esc(code) + const KW = + 'const|let|var|function|func|fn|return|import|export|from|use|mut|pub|async|await|' + + 'class|struct|type|interface|impl|match|new|typeof|instanceof|def|lambda|yield|for|while|' + + 'if|elif|else|in|not|and|or|with|as|raise|try|except|finally|pass|package|println|print|' + + 'true|false|null|nil|None|True|False' + const TOKEN = new RegExp( + '("(?:(?!").)*"|'(?:(?!').)*'|`(?:(?!`).)*`)' + // strings + '|(#[^\\n]*|\\/\\/[^\\n]*)' + // comments (# or //) + '|\\b(' + + KW + + ')\\b', + 'g' + ) + return escaped.replace(TOKEN, (m, str, comment, kw) => { + if (str !== undefined) return `<span class="hl-s">${str}</span>` + if (comment !== undefined) return `<span class="hl-comment">${comment}</span>` + if (kw !== undefined) return `<span class="hl-b">${kw}</span>` + return m + }) } // ── CodePanel ───────────────────────────────────────────────────────────────── @@ -124,15 +92,14 @@ export function CodePanel({ blocks, labelCopy, labelCopied }: CodePanelProps) { type="button" className="copy-btn" onClick={() => copyCode(block.label, block.code)} - > - {copiedLabel === block.label ? labelCopied : labelCopy} + title={copiedLabel === block.label ? labelCopied : labelCopy} + aria-label={copiedLabel === block.label ? labelCopied : labelCopy}> + {copiedLabel === block.label ? <IconCheck /> : <IconCopy />} </button> </div> <div className="card-body"> <pre className="code-pre"> - <code - dangerouslySetInnerHTML={{ __html: highlightCode(block.code, block.lang) }} - /> + <code dangerouslySetInnerHTML={{ __html: highlightCode(block.code, block.lang) }} /> </pre> </div> </div> @@ -141,5 +108,112 @@ export function CodePanel({ blocks, labelCopy, labelCopied }: CodePanelProps) { ) } +// ── CodeTabs (docs-style, light) ────────────────────────────────────────────── +// A single light code card with language tabs (used for Request Example). + +interface CodeTabsProps { + blocks: CodeBlock[] + labelCopy: string + labelCopied: string +} + +export function CodeTabs({ blocks, labelCopy, labelCopied }: CodeTabsProps) { + const [active, setActive] = React.useState(0) + const [copied, setCopied] = React.useState(false) + if (!blocks.length) return null + const block = blocks[Math.min(active, blocks.length - 1)] + + function copy() { + navigator.clipboard + .writeText(block.code) + .then(() => { + setCopied(true) + setTimeout(() => setCopied(false), 1800) + }) + .catch(() => {}) + } + + return ( + <div data-lbus-component="code-tabs" className="code-tabs"> + <div className="code-tabs-bar"> + <div className="code-tabs-list" role="tablist" aria-label="language"> + {blocks.map((b, i) => ( + <button + key={b.label} + type="button" + role="tab" + aria-selected={i === active} + className={`code-tab${i === active ? ' is-active' : ''}`} + onClick={() => setActive(i)}> + {b.label} + </button> + ))} + </div> + <button + type="button" + className="code-tabs-copy" + onClick={copy} + title={copied ? labelCopied : labelCopy} + aria-label={copied ? labelCopied : labelCopy}> + {copied ? <IconCheck /> : <IconCopy />} + </button> + </div> + <div className="code-tabs-body"> + <pre className="code-pre"> + <code dangerouslySetInnerHTML={{ __html: highlightCode(block.code, block.lang) }} /> + </pre> + </div> + </div> + ) +} + +// ── CodeDropdown ────────────────────────────────────────────────────────────── +// Same light code card, but the language is picked from a <select> instead of a +// tab strip — for narrow columns (the right rail) where 8 tabs would overflow. + +export function CodeDropdown({ blocks, labelCopy, labelCopied }: CodeTabsProps) { + const [active, setActive] = React.useState(0) + const [copied, setCopied] = React.useState(false) + if (!blocks.length) return null + const block = blocks[Math.min(active, blocks.length - 1)] + + function copy() { + navigator.clipboard + .writeText(block.code) + .then(() => { + setCopied(true) + setTimeout(() => setCopied(false), 1800) + }) + .catch(() => {}) + } + + return ( + <div data-lbus-component="code-dropdown" className="code-tabs"> + <div className="code-tabs-bar"> + <Dropdown + className="ar-lang" + ariaLabel="language" + value={block.label} + options={blocks.map((b) => ({ value: b.label, label: b.label }))} + onChange={(label) => setActive(blocks.findIndex((b) => b.label === label))} + /> + <button + type="button" + className="code-tabs-copy" + onClick={copy} + title={copied ? labelCopied : labelCopy} + aria-label={copied ? labelCopied : labelCopy}> + {copied ? <IconCheck /> : <IconCopy />} + </button> + </div> + <div className="code-tabs-body"> + <pre className="code-pre"> + <code dangerouslySetInnerHTML={{ __html: highlightCode(block.code, block.lang) }} /> + </pre> + </div> + </div> + ) +} + // React import needed for useState import React from 'react' diff --git a/packages/api-reference/src/Dropdown.tsx b/packages/api-reference/src/Dropdown.tsx new file mode 100644 index 000000000..2ae7d1932 --- /dev/null +++ b/packages/api-reference/src/Dropdown.tsx @@ -0,0 +1,108 @@ +/** + * Dropdown — a small custom <select> replacement: a trigger button plus a styled + * popover menu, so the option list matches the docs styling instead of the OS + * native select. The menu renders in a portal with fixed positioning so it is + * never clipped by an ancestor's `overflow: hidden` (e.g. the code card). Closes + * on outside click / Escape / scroll. Generic over the value type. + */ +import { useEffect, useRef, useState } from 'react' +import { createPortal } from 'react-dom' + +export interface DropdownOption<T extends string> { + value: T + label: string +} + +interface DropdownProps<T extends string> { + value: T + options: DropdownOption<T>[] + onChange: (value: T) => void + /** Extra class on the wrapper (for per-context width / placement). */ + className?: string + ariaLabel?: string +} + +export function Dropdown<T extends string>({ + value, + options, + onChange, + className, + ariaLabel, +}: DropdownProps<T>) { + const [open, setOpen] = useState(false) + const [pos, setPos] = useState<{ top: number; left: number; width: number } | null>(null) + const wrapRef = useRef<HTMLDivElement>(null) + const btnRef = useRef<HTMLButtonElement>(null) + const menuRef = useRef<HTMLDivElement>(null) + + useEffect(() => { + if (!open) return + const r = btnRef.current?.getBoundingClientRect() + if (r) setPos({ top: r.bottom + 6, left: r.left, width: r.width }) + + const onDown = (e: MouseEvent) => { + const t = e.target as Node + if (wrapRef.current?.contains(t) || menuRef.current?.contains(t)) return + setOpen(false) + } + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') setOpen(false) + } + const onScroll = () => setOpen(false) + document.addEventListener('mousedown', onDown) + document.addEventListener('keydown', onKey) + window.addEventListener('scroll', onScroll, true) + window.addEventListener('resize', onScroll) + return () => { + document.removeEventListener('mousedown', onDown) + document.removeEventListener('keydown', onKey) + window.removeEventListener('scroll', onScroll, true) + window.removeEventListener('resize', onScroll) + } + }, [open]) + + const current = options.find((o) => o.value === value)?.label ?? '' + + return ( + <div className={`ar-dropdown${className ? ` ${className}` : ''}`} ref={wrapRef}> + <button + ref={btnRef} + type="button" + className="ar-dropdown-btn" + aria-haspopup="listbox" + aria-expanded={open} + aria-label={ariaLabel} + onClick={() => setOpen((v) => !v)}> + <span className="ar-dropdown-value">{current}</span> + <svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"> + <path d="M6 9l6 6 6-6" /> + </svg> + </button> + {open && + pos && + createPortal( + <div + ref={menuRef} + className="ar-dropdown-menu" + role="listbox" + style={{ position: 'fixed', top: pos.top, left: pos.left, minWidth: pos.width }}> + {options.map((o) => ( + <button + key={o.value} + type="button" + role="option" + aria-selected={o.value === value} + className={`ar-dropdown-option${o.value === value ? ' is-active' : ''}`} + onClick={() => { + onChange(o.value) + setOpen(false) + }}> + {o.label} + </button> + ))} + </div>, + document.body + )} + </div> + ) +} diff --git a/packages/api-reference/src/EndpointUrlBar.tsx b/packages/api-reference/src/EndpointUrlBar.tsx new file mode 100644 index 000000000..e26cc2382 --- /dev/null +++ b/packages/api-reference/src/EndpointUrlBar.tsx @@ -0,0 +1,195 @@ +/** + * EndpointUrlBar — method badge + full URL, an env (生产/测试) segmented control, + * a copy-URL button and a split "copy page" dropdown (copy markdown / view as + * markdown / open in ChatGPT / open in Claude). Mirrors the reference design. + */ +import { useEffect, useRef, useState } from 'react' +import type { Locale } from '@longbridge/openapi-utils' +import { useEnv } from './EnvContext' + +const L = { + prod: { en: 'Prod', 'zh-CN': '生产', 'zh-HK': '生產' }, + test: { en: 'Test', 'zh-CN': '测试', 'zh-HK': '測試' }, + copyPage: { en: 'Copy page', 'zh-CN': '复制页面', 'zh-HK': '複製頁面' }, + copied: { en: 'Copied', 'zh-CN': '已复制', 'zh-HK': '已複製' }, + copyUrl: { en: 'Copy URL', 'zh-CN': '复制 URL', 'zh-HK': '複製 URL' }, + viewMd: { en: 'View as Markdown', 'zh-CN': '以 Markdown 查看', 'zh-HK': '以 Markdown 檢視' }, + openChatgpt: { en: 'Open in ChatGPT', 'zh-CN': '在 ChatGPT 中打开', 'zh-HK': '在 ChatGPT 中開啟' }, + openClaude: { en: 'Open in Claude', 'zh-CN': '在 Claude 中打开', 'zh-HK': '在 Claude 中開啟' }, +} as const + +export interface EndpointUrlBarProps { + method: string + path: string + locale: Locale + /** Narrow-screen "Try it" trigger, rendered after the copy button (CSS-gated). */ + onTryIt?: () => void + tryItLabel?: string +} + +export interface CopyPageMenuProps { + operationId: string + localePrefix: string + locale: Locale +} + +// Simple monochrome glyphs for the dropdown items. +const IconDoc = () => ( + <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"> + <path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" /> + <path d="M14 3v5h5M9 13h6M9 17h6" /> + </svg> +) +const IconChatgpt = () => ( + <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"> + <path d="M22 9.5a5 5 0 0 0-.55-4.15 5.06 5.06 0 0 0-5.44-2.42A5 5 0 0 0 7.6 4a5 5 0 0 0-3.34 2.42A5.06 5.06 0 0 0 4.9 13a5 5 0 0 0 .55 4.15 5.06 5.06 0 0 0 5.44 2.42A5 5 0 0 0 16.4 20a5 5 0 0 0 3.34-2.42A5.06 5.06 0 0 0 22 11zm-7.5 10a3.7 3.7 0 0 1-2.38-.86l3.3-1.9a.55.55 0 0 0 .27-.47v-4.65l1.4.81v3.86a3.73 3.73 0 0 1-2.6 3.2zM6.2 16.16a3.7 3.7 0 0 1-.44-2.49l3.3 1.9a.54.54 0 0 0 .54 0l4-2.32v1.62l-3.34 1.93a3.73 3.73 0 0 1-4.06-.64zm-.87-7.1a3.7 3.7 0 0 1 1.94-1.62v3.94a.54.54 0 0 0 .27.47l4 2.31-1.4.81-3.34-1.93a3.73 3.73 0 0 1-1.47-3.99zm11.9 2.77l-4-2.32 1.4-.8 3.34 1.92a3.72 3.72 0 0 1-.57 6.72V13.4a.55.55 0 0 0-.17-.57zm1.4-2.1l-3.3-1.9a.54.54 0 0 0-.54 0l-4 2.31V8.53l3.34-1.93a3.72 3.72 0 0 1 4.5 5.93zM9.9 12.63l-1.4-.81V7.96a3.72 3.72 0 0 1 6.1-2.86l-3.3 1.9a.55.55 0 0 0-.27.47zm.76-1.63L12 9.85l1.34.77v1.54L12 14l-1.34-.77z" /> + </svg> +) +const IconClaude = () => ( + <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"> + <path d="M12 2c.4 0 .74.26.86.64l2.1 6.4 6.4 2.1a.9.9 0 0 1 0 1.72l-6.4 2.1-2.1 6.4a.9.9 0 0 1-1.72 0l-2.1-6.4-6.4-2.1a.9.9 0 0 1 0-1.72l6.4-2.1 2.1-6.4A.9.9 0 0 1 12 2z" /> + </svg> +) +// Shared copy / copied / chevron glyphs — reused across every copy affordance +// so the whole reference speaks one icon language. +export const IconCopy = () => ( + <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> + <rect x="9" y="9" width="13" height="13" rx="2" /> + <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" /> + </svg> +) +export const IconCheck = () => ( + <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"> + <path d="M20 6 9 17l-5-5" /> + </svg> +) +// Matches the <select> chevron exactly (width 10, stroke-width 3) so the +// split-button toggle and the language / auth dropdowns read as one family. +const IconChevron = () => ( + <svg viewBox="0 0 24 24" width="10" height="10" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"> + <path d="M6 9l6 6 6-6" /> + </svg> +) + +export function EndpointUrlBar({ method, path, locale, onTryIt, tryItLabel }: EndpointUrlBarProps) { + const { displayBaseUrl } = useEnv() + const [urlCopied, setUrlCopied] = useState(false) + const fullUrl = `${displayBaseUrl}${path}` + + const copyUrl = () => { + navigator.clipboard.writeText(fullUrl).then(() => { + setUrlCopied(true) + setTimeout(() => setUrlCopied(false), 1500) + }) + } + + return ( + <div className="ep-urlbar" data-lbus-component="endpoint-url-bar"> + <span className={`ep-method-badge method-${method.toLowerCase()}`}>{method}</span> + <code className="ep-urlbar-url" title={fullUrl}> + {fullUrl} + </code> + <button type="button" className="ep-urlbar-icon" onClick={copyUrl} title={L.copyUrl[locale]} aria-label={L.copyUrl[locale]}> + {urlCopied ? <IconCheck /> : <IconCopy />} + </button> + {onTryIt && ( + <button type="button" className="ep-urlbar-tryit" onClick={onTryIt}> + {tryItLabel ?? 'Try it'} + </button> + )} + </div> + ) +} + +/** Page-level "copy page" split button + AI/markdown dropdown. Rendered on the + * endpoint title row so the heading owns the page-level action. */ +export function CopyPageMenu({ operationId, localePrefix, locale }: CopyPageMenuProps) { + const [pageCopied, setPageCopied] = useState(false) + const [menuOpen, setMenuOpen] = useState(false) + const wrapRef = useRef<HTMLDivElement>(null) + const toggleRef = useRef<HTMLButtonElement>(null) + + const origin = typeof window !== 'undefined' ? window.location.origin : '' + const mdUrl = `${localePrefix}/docs/api/${operationId}.md` + const absMdUrl = `${origin}${mdUrl}` + const prompt = `Read ${absMdUrl} so I can ask questions about this API.` + + // Close the dropdown on outside click or Escape (Escape returns focus to the toggle). + useEffect(() => { + if (!menuOpen) return + const onDown = (e: MouseEvent) => { + if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) setMenuOpen(false) + } + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + setMenuOpen(false) + toggleRef.current?.focus() + } + } + document.addEventListener('mousedown', onDown) + document.addEventListener('keydown', onKey) + return () => { + document.removeEventListener('mousedown', onDown) + document.removeEventListener('keydown', onKey) + } + }, [menuOpen]) + + const copyPage = async () => { + try { + const res = await fetch(mdUrl) + const md = await res.text() + await navigator.clipboard.writeText(md) + setPageCopied(true) + setTimeout(() => setPageCopied(false), 1500) + } catch { + /* ignore */ + } + } + const openExternal = (url: string) => { + window.open(url, '_blank', 'noopener,noreferrer') + setMenuOpen(false) + } + + return ( + <div className="ep-copypage" ref={wrapRef}> + <button type="button" className="ep-copypage-main" onClick={copyPage}> + <span className="ep-copypage-ic">{pageCopied ? <IconCheck /> : <IconCopy />}</span> + {pageCopied ? L.copied[locale] : L.copyPage[locale]} + </button> + <button + ref={toggleRef} + type="button" + className="ep-copypage-toggle" + aria-haspopup="menu" + aria-expanded={menuOpen} + aria-label={L.copyPage[locale]} + onClick={() => setMenuOpen((v) => !v)}> + <IconChevron /> + </button> + {menuOpen && ( + <div className="ep-copypage-menu" role="menu"> + <a className="ep-copypage-item" role="menuitem" href={mdUrl} target="_blank" rel="noopener noreferrer" onClick={() => setMenuOpen(false)}> + <IconDoc /> + {L.viewMd[locale]} + </a> + <button + type="button" + className="ep-copypage-item" + role="menuitem" + onClick={() => openExternal(`https://chatgpt.com/?hints=search&q=${encodeURIComponent(prompt)}`)}> + <IconChatgpt /> + {L.openChatgpt[locale]} + </button> + <button + type="button" + className="ep-copypage-item" + role="menuitem" + onClick={() => openExternal(`https://claude.ai/new?q=${encodeURIComponent(prompt)}`)}> + <IconClaude /> + {L.openClaude[locale]} + </button> + </div> + )} + </div> + ) +} diff --git a/packages/api-reference/src/EnvContext.tsx b/packages/api-reference/src/EnvContext.tsx new file mode 100644 index 000000000..6f5bee440 --- /dev/null +++ b/packages/api-reference/src/EnvContext.tsx @@ -0,0 +1,74 @@ +/** + * EnvContext — 生产/测试 environment for the API Reference. + * + * `displayBaseUrl` is the real domain shown in the URL bar and code examples. + * `baseUrl` is what the TryIt client actually calls: in dev it is a same-origin + * proxy prefix (/api-prod, /api-test — see astro.config.ts) to dodge CORS; in a + * production build it is the real domain. + */ +import React, { createContext, useContext, useState, useCallback } from 'react' + +export type ApiEnv = 'prod' +/** Auth scheme: `sign` = App Key + Secret HMAC signing; `oauth` = Bearer token. */ +export type AuthMode = 'sign' | 'oauth' + +const DISPLAY: Record<ApiEnv, string> = { + prod: 'https://openapi.longbridge.com', +} +const DEV_PROXY: Record<ApiEnv, string> = { + prod: '/api-prod', +} + +const AUTH_KEY = 'lb-apiref-auth-mode' +const isDev = typeof import.meta !== 'undefined' && (import.meta as any).env?.DEV + +function initialEnv(): ApiEnv { + return 'prod' +} + +function initialAuthMode(): AuthMode { + if (typeof window === 'undefined') return 'sign' + const v = window.localStorage.getItem(AUTH_KEY) + return v === 'oauth' ? 'oauth' : 'sign' +} + +interface EnvCtx { + env: ApiEnv + setEnv: (e: ApiEnv) => void + /** Real domain, for display (URL bar, code examples). */ + displayBaseUrl: string + /** What TryIt calls: dev proxy prefix, or real domain in prod builds. */ + baseUrl: string + /** Auth scheme selected in the token panel; drives auth table + code samples. */ + authMode: AuthMode + setAuthMode: (m: AuthMode) => void +} + +const Ctx = createContext<EnvCtx | null>(null) + +export function EnvProvider({ children }: { children: React.ReactNode }) { + const [env, setEnvState] = useState<ApiEnv>(initialEnv) + const setEnv = useCallback((e: ApiEnv) => { + setEnvState(e) + }, []) + const [authMode, setAuthModeState] = useState<AuthMode>(initialAuthMode) + const setAuthMode = useCallback((m: AuthMode) => { + setAuthModeState(m) + if (typeof window !== 'undefined') window.localStorage.setItem(AUTH_KEY, m) + }, []) + const value: EnvCtx = { + env, + setEnv, + displayBaseUrl: DISPLAY[env], + baseUrl: isDev ? DEV_PROXY[env] : DISPLAY[env], + authMode, + setAuthMode, + } + return <Ctx.Provider value={value}>{children}</Ctx.Provider> +} + +export function useEnv(): EnvCtx { + const ctx = useContext(Ctx) + if (!ctx) throw new Error('useEnv must be used within <EnvProvider>') + return ctx +} diff --git a/packages/api-reference/src/QuotePermission.css b/packages/api-reference/src/QuotePermission.css new file mode 100644 index 000000000..36b36d53e --- /dev/null +++ b/packages/api-reference/src/QuotePermission.css @@ -0,0 +1,288 @@ +/* + * QuotePermission.css + * Scoped to [data-lbus-component="quote-permission"] to avoid polluting global scope. + * Ported from QuotePermission.vue <style scoped> — @apply directives converted to + * plain CSS values using the same Tailwind v3 palette. + * Dark mode uses [data-mode="dark"] to match this project's convention. + */ + +/* ── CSS custom properties by level (light mode) ── */ +[data-lbus-component="quote-permission"].qp-alert[data-level="basic"] { + --lc-border: rgba(34, 197, 94, 0.3); + --lc-bg: rgba(34, 197, 94, 0.07); + --lc-icon: #16a34a; + --lc-badge-bg: rgba(34, 197, 94, 0.15); + --lc-badge-text: #15803d; + --lc-desc: rgba(22, 101, 52, 0.8); + --lc-link: #15803d; + --lc-link-hover: #14532d; + --lc-dim: rgba(22, 163, 74, 0.5); + --lc-market: rgba(22, 163, 74, 0.7); +} +[data-lbus-component="quote-permission"].qp-alert[data-level="lv1"] { + --lc-border: rgba(59, 130, 246, 0.3); + --lc-bg: rgba(59, 130, 246, 0.07); + --lc-icon: #2563eb; + --lc-badge-bg: rgba(59, 130, 246, 0.15); + --lc-badge-text: #1d4ed8; + --lc-desc: rgba(30, 64, 175, 0.8); + --lc-link: #1d4ed8; + --lc-link-hover: #1e3a8a; + --lc-dim: rgba(37, 99, 235, 0.5); + --lc-market: rgba(37, 99, 235, 0.7); +} +[data-lbus-component="quote-permission"].qp-alert[data-level="lv2"] { + --lc-border: rgba(249, 115, 22, 0.3); + --lc-bg: rgba(249, 115, 22, 0.07); + --lc-icon: #ea580c; + --lc-badge-bg: rgba(249, 115, 22, 0.15); + --lc-badge-text: #c2410c; + --lc-desc: rgba(154, 52, 18, 0.8); + --lc-link: #c2410c; + --lc-link-hover: #7c2d12; + --lc-dim: rgba(234, 88, 12, 0.5); + --lc-market: rgba(234, 88, 12, 0.7); +} +[data-lbus-component="quote-permission"].qp-alert[data-level="overnight"] { + --lc-border: rgba(234, 179, 8, 0.3); + --lc-bg: rgba(234, 179, 8, 0.07); + --lc-icon: #a16207; + --lc-badge-bg: rgba(234, 179, 8, 0.15); + --lc-badge-text: #854d0e; + --lc-desc: rgba(113, 63, 18, 0.8); + --lc-link: #a16207; + --lc-link-hover: #713f12; + --lc-dim: rgba(161, 98, 7, 0.5); + --lc-market: rgba(161, 98, 7, 0.7); +} +[data-lbus-component="quote-permission"].qp-alert[data-level="opra"] { + --lc-border: rgba(168, 85, 247, 0.3); + --lc-bg: rgba(168, 85, 247, 0.07); + --lc-icon: #9333ea; + --lc-badge-bg: rgba(168, 85, 247, 0.15); + --lc-badge-text: #7e22ce; + --lc-desc: rgba(107, 33, 168, 0.8); + --lc-link: #7e22ce; + --lc-link-hover: #581c87; + --lc-dim: rgba(147, 51, 234, 0.5); + --lc-market: rgba(147, 51, 234, 0.7); +} + +/* ── Dark mode overrides ── */ +[data-mode="dark"] [data-lbus-component="quote-permission"].qp-alert[data-level="basic"] { + --lc-icon: #4ade80; + --lc-badge-bg: rgba(34, 197, 94, 0.2); + --lc-badge-text: #86efac; + --lc-desc: rgba(134, 239, 172, 0.8); + --lc-link: #4ade80; + --lc-link-hover: #bbf7d0; + --lc-dim: rgba(34, 197, 94, 0.5); + --lc-market: rgba(74, 222, 128, 0.7); +} +[data-mode="dark"] [data-lbus-component="quote-permission"].qp-alert[data-level="lv1"] { + --lc-icon: #60a5fa; + --lc-badge-bg: rgba(59, 130, 246, 0.2); + --lc-badge-text: #93c5fd; + --lc-desc: rgba(147, 197, 253, 0.8); + --lc-link: #60a5fa; + --lc-link-hover: #bfdbfe; + --lc-dim: rgba(59, 130, 246, 0.5); + --lc-market: rgba(96, 165, 250, 0.7); +} +[data-mode="dark"] [data-lbus-component="quote-permission"].qp-alert[data-level="lv2"] { + --lc-icon: #fb923c; + --lc-badge-bg: rgba(249, 115, 22, 0.2); + --lc-badge-text: #fdba74; + --lc-desc: rgba(253, 186, 116, 0.8); + --lc-link: #fb923c; + --lc-link-hover: #fed7aa; + --lc-dim: rgba(249, 115, 22, 0.5); + --lc-market: rgba(251, 146, 60, 0.7); +} +[data-mode="dark"] [data-lbus-component="quote-permission"].qp-alert[data-level="overnight"] { + --lc-icon: #facc15; + --lc-badge-bg: rgba(234, 179, 8, 0.2); + --lc-badge-text: #fde047; + --lc-desc: rgba(253, 224, 71, 0.8); + --lc-link: #facc15; + --lc-link-hover: #fef08a; + --lc-dim: rgba(234, 179, 8, 0.5); + --lc-market: rgba(250, 204, 21, 0.7); +} +[data-mode="dark"] [data-lbus-component="quote-permission"].qp-alert[data-level="opra"] { + --lc-icon: #c084fc; + --lc-badge-bg: rgba(168, 85, 247, 0.2); + --lc-badge-text: #d8b4fe; + --lc-desc: rgba(216, 180, 254, 0.8); + --lc-link: #c084fc; + --lc-link-hover: #e9d5ff; + --lc-dim: rgba(168, 85, 247, 0.5); + --lc-market: rgba(192, 132, 252, 0.7); +} + +/* ── Dark mode via OS preference (when no explicit theme attr is set) ── */ +@media (prefers-color-scheme: dark) { + :root:not([data-mode="light"]) [data-lbus-component="quote-permission"].qp-alert[data-level="basic"] { + --lc-icon: #4ade80; + --lc-badge-bg: rgba(34, 197, 94, 0.2); + --lc-badge-text: #86efac; + --lc-desc: rgba(134, 239, 172, 0.8); + --lc-link: #4ade80; + --lc-link-hover: #bbf7d0; + --lc-dim: rgba(34, 197, 94, 0.5); + --lc-market: rgba(74, 222, 128, 0.7); + } + :root:not([data-mode="light"]) [data-lbus-component="quote-permission"].qp-alert[data-level="lv1"] { + --lc-icon: #60a5fa; + --lc-badge-bg: rgba(59, 130, 246, 0.2); + --lc-badge-text: #93c5fd; + --lc-desc: rgba(147, 197, 253, 0.8); + --lc-link: #60a5fa; + --lc-link-hover: #bfdbfe; + --lc-dim: rgba(59, 130, 246, 0.5); + --lc-market: rgba(96, 165, 250, 0.7); + } + :root:not([data-mode="light"]) [data-lbus-component="quote-permission"].qp-alert[data-level="lv2"] { + --lc-icon: #fb923c; + --lc-badge-bg: rgba(249, 115, 22, 0.2); + --lc-badge-text: #fdba74; + --lc-desc: rgba(253, 186, 116, 0.8); + --lc-link: #fb923c; + --lc-link-hover: #fed7aa; + --lc-dim: rgba(249, 115, 22, 0.5); + --lc-market: rgba(251, 146, 60, 0.7); + } + :root:not([data-mode="light"]) [data-lbus-component="quote-permission"].qp-alert[data-level="overnight"] { + --lc-icon: #facc15; + --lc-badge-bg: rgba(234, 179, 8, 0.2); + --lc-badge-text: #fde047; + --lc-desc: rgba(253, 224, 71, 0.8); + --lc-link: #facc15; + --lc-link-hover: #fef08a; + --lc-dim: rgba(234, 179, 8, 0.5); + --lc-market: rgba(250, 204, 21, 0.7); + } + :root:not([data-mode="light"]) [data-lbus-component="quote-permission"].qp-alert[data-level="opra"] { + --lc-icon: #c084fc; + --lc-badge-bg: rgba(168, 85, 247, 0.2); + --lc-badge-text: #d8b4fe; + --lc-desc: rgba(216, 180, 254, 0.8); + --lc-link: #c084fc; + --lc-link-hover: #e9d5ff; + --lc-dim: rgba(168, 85, 247, 0.5); + --lc-market: rgba(192, 132, 252, 0.7); + } +} + +/* ── Container ── */ +[data-lbus-component="quote-permission"].qp-alert { + border: 1px solid var(--lc-border); + background: var(--lc-bg); + border-radius: 0.5rem; + padding: 0.875rem 1rem; + margin: 1rem 0; +} + +/* ── Header ── */ +[data-lbus-component="quote-permission"] .qp-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + flex-wrap: wrap; +} + +[data-lbus-component="quote-permission"] .qp-icon { + display: inline-flex; + flex-shrink: 0; + color: var(--lc-icon); +} + +[data-lbus-component="quote-permission"] .qp-label { + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.025em; + text-transform: uppercase; + white-space: nowrap; + color: var(--lc-icon); +} + +[data-lbus-component="quote-permission"] .qp-market-tag { + font-size: 0.7rem; + font-weight: 500; + padding: 0 0.375rem; + border-radius: 9999px; + background: transparent; + white-space: nowrap; + color: var(--lc-market); + box-shadow: inset 0 0 0 1px var(--lc-border); +} + +[data-lbus-component="quote-permission"] .qp-badge { + display: inline-block; + font-size: 0.8125rem; + line-height: 1; + padding: 0.2rem 0.5rem; + border-radius: 0.25rem; + white-space: nowrap; + letter-spacing: 0.01em; + margin-left: auto; + background: var(--lc-badge-bg); + color: var(--lc-badge-text); + box-shadow: inset 0 0 0 1px var(--lc-border); +} + +/* ── Content ── */ +[data-lbus-component="quote-permission"] .qp-desc { + font-size: 0.8125rem; + line-height: 1.5; + margin: 0 0 0.5rem 0; + color: var(--lc-desc); +} + +[data-lbus-component="quote-permission"] .qp-list { + margin: 0 0 0.5rem 0; + padding-left: 1.125rem; + list-style: disc; +} + +[data-lbus-component="quote-permission"] .qp-list-item { + font-size: 0.8125rem; + line-height: 1.6; + margin: 0; + color: var(--lc-desc); +} + +/* ── Footer ── */ +[data-lbus-component="quote-permission"] .qp-footer { + display: flex; + align-items: center; + gap: 0.375rem; + flex-wrap: wrap; +} + +[data-lbus-component="quote-permission"] .qp-link { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 0.75rem; + font-weight: 500; + text-decoration: none; + transition: color 0.15s; + color: var(--lc-link); +} +[data-lbus-component="quote-permission"] .qp-link:hover { + text-decoration: underline; + color: var(--lc-link-hover); +} + +[data-lbus-component="quote-permission"] .qp-sep { + font-size: 0.75rem; + color: var(--lc-dim); +} + +[data-lbus-component="quote-permission"] .qp-note { + font-size: 0.7rem; + opacity: 0.8; + color: var(--lc-dim); +} diff --git a/packages/api-reference/src/QuotePermission.tsx b/packages/api-reference/src/QuotePermission.tsx index 9e8742134..6f36e8b9b 100644 --- a/packages/api-reference/src/QuotePermission.tsx +++ b/packages/api-reference/src/QuotePermission.tsx @@ -1,14 +1,13 @@ /** * QuotePermission.tsx - * Renders the permission badge + detail card for a quote command. - * Ported 1:1 from ApiReference.vue (QuotePermission inline template). - * Imports quote-permissions.yaml?raw via Vite raw import. + * Quote-permission badge + detail card — a faithful port of the docs MDX + * component (src/components/mdx/QuotePermission.tsx) so the reference renders it + * identically. Resolves command/level/market against quote-permissions.yaml. */ import { load } from 'js-yaml' import type { Locale } from '@longbridge/openapi-utils' - -// Vite raw import — resolved at build time import rawQP from '../../../quote-permissions.yaml?raw' +// CSS is loaded via api-reference.css (@import) so it ships with the layout. // ── YAML types ──────────────────────────────────────────────────────────────── @@ -16,90 +15,119 @@ interface LocaleString { en: string 'zh-CN': string 'zh-HK': string + [key: string]: string } - interface LevelDef { label: LocaleString description: LocaleString link_text: LocaleString } - interface CommandDef { level: string market?: string - description: LocaleString + description?: LocaleString } - interface QPData { ui: { link_url: string permission_title: LocaleString separate_note: LocaleString - market_labels: Record<string, LocaleString> + market_labels?: Record<string, LocaleString> } levels: Record<string, LevelDef> - commands: Record<string, CommandDef> -} - -// ── Level color map ─────────────────────────────────────────────────────────── - -const LEVEL_CLASS: Record<string, string> = { - basic: 'qp-badge--green', - lv1: 'qp-badge--blue', - lv2: 'qp-badge--orange', - overnight: 'qp-badge--yellow', - opra: 'qp-badge--purple', + commands?: Record<string, CommandDef> } -// ── Parse once at module level ──────────────────────────────────────────────── - let _qpData: QPData | null = null function getQPData(): QPData { - if (!_qpData) { - _qpData = load(rawQP) as QPData - } + if (!_qpData) _qpData = load(rawQP) as QPData return _qpData } +// ── Shield-check SVG (14×14) ────────────────────────────────────────────────── + +const ShieldCheckIcon = () => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="14" + height="14" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round"> + <path d="M20 13c0 5-3.5 7.5-7.76 8.95a1 1 0 0 1-.48 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /> + <path d="m9 12 2 2 4-4" /> + </svg> +) + // ── Component ───────────────────────────────────────────────────────────────── export interface QuotePermissionProps { - /** The x-quote-command value from the OpenAPI operation */ - command: string - locale: Locale + /** API command key — looked up in quote-permissions.yaml commands. */ + command?: string + /** Explicit level override ('basic' | 'lv1' | 'lv2' | 'overnight' | 'opra'). */ + level?: string + /** Explicit market override (e.g. 'US', 'HK'). */ + market?: string + locale?: Locale } -export function QuotePermission({ command, locale }: QuotePermissionProps) { +export function QuotePermission({ command, level, market, locale = 'en' }: QuotePermissionProps) { const qp = getQPData() - const cmd = qp.commands?.[command] - if (!cmd) return null - - const level = qp.levels?.[cmd.level] - if (!level) return null + const cmdEntry = command ? (qp.commands?.[command] ?? null) : null + const effectiveLevel = cmdEntry?.level ?? level ?? 'basic' + const effectiveMarket = market ?? cmdEntry?.market + const levelDef = qp.levels?.[effectiveLevel] + if (!levelDef) return null const ui = qp.ui - const locStr = (s: LocaleString) => s?.[locale] ?? s?.en ?? '' - const badgeClass = LEVEL_CLASS[cmd.level] ?? 'qp-badge--blue' - - const marketKey = cmd.market - const marketLabel = marketKey ? locStr(ui.market_labels?.[marketKey] ?? { en: marketKey, 'zh-CN': marketKey, 'zh-HK': marketKey }) : null + const l = (s: LocaleString | undefined): string => (s ? (s[locale] ?? s.en ?? '') : '') + + const title = l(ui?.permission_title) + const badgeLabel = l(levelDef.label) + const descriptionRaw = cmdEntry?.description ? l(cmdEntry.description) : l(levelDef.description) + const descriptionLines = descriptionRaw ? descriptionRaw.split('\n').filter(Boolean) : [] + const linkUrl = ui?.link_url ?? '' + const linkText = l(levelDef.link_text) + const separateNote = l(ui?.separate_note) + const marketLabel = effectiveMarket + ? l(ui?.market_labels?.[effectiveMarket]) || effectiveMarket + : null return ( - <div data-lbus-component="quote-permission" className="qp-wrapper"> + <div data-lbus-component="quote-permission" className="qp-alert" data-level={effectiveLevel}> <div className="qp-header"> - <span className="qp-title">{locStr(ui.permission_title)}</span> - <span className={`qp-badge ${badgeClass}`}>{locStr(level.label)}</span> - {marketLabel && <span className="qp-market">{marketLabel}</span>} + <span className="qp-icon"> + <ShieldCheckIcon /> + </span> + <span className="qp-label">{title}</span> + {marketLabel && <span className="qp-market-tag">{marketLabel}</span>} + <span className="qp-badge">{badgeLabel}</span> + </div> + + {descriptionLines.length > 1 ? ( + <ul className="qp-list"> + {descriptionLines.map((line) => ( + <li key={line} className="qp-list-item"> + {line} + </li> + ))} + </ul> + ) : descriptionLines.length === 1 ? ( + <p className="qp-desc">{descriptionLines[0]}</p> + ) : null} + + <div className="qp-footer"> + <a href={linkUrl} target="_blank" rel="noopener noreferrer" className="qp-link"> + {linkText} + </a> + <span className="qp-sep" aria-hidden="true"> + · + </span> + <span className="qp-note">{separateNote}</span> </div> - <p className="qp-desc">{locStr(cmd.description)}</p> - {ui.link_url && ( - <p className="qp-note"> - {locStr(ui.separate_note)}{' '} - <a href={ui.link_url} target="_blank" rel="noopener noreferrer" className="qp-link"> - {locStr(level.link_text)} - </a> - </p> - )} </div> ) } diff --git a/packages/api-reference/src/RequestPanel.tsx b/packages/api-reference/src/RequestPanel.tsx new file mode 100644 index 000000000..76f66b29c --- /dev/null +++ b/packages/api-reference/src/RequestPanel.tsx @@ -0,0 +1,281 @@ +/** + * RequestPanel — right-rail top card: an auth-mode dropdown (Signed / OAuth), a + * language-tabbed code example that follows the mode, a collapsible 设置 Token + * form whose fields also follow the mode, an editable parameters form and a 发送 + * button that fires a real request (signed or Bearer) against the environment. + */ +import { useMemo, useRef, useState } from 'react' +import type { Locale } from '@longbridge/openapi-utils' +import { + AuthorizationForm, + ParametersForm, + useAuthorization, + createQuickRequest, + type ParameterRow, + type ApiResponse, +} from '@longbridge/openapi-tryit' +import { CodeDropdown } from './CodeSample' +import { pickLocale, type CodeBlock, type XParameter } from './openapi-loader' +import { useEnv, type AuthMode } from './EnvContext' +import { Dropdown } from './Dropdown' +import { signedCodeBlocks } from './signing-samples' + +const L = { + request: { en: 'Request', 'zh-CN': '请求', 'zh-HK': '請求' }, + setToken: { en: 'Set Token', 'zh-CN': '设置 Token', 'zh-HK': '設置 Token' }, + send: { en: 'Try it', 'zh-CN': 'Try it', 'zh-HK': 'Try it' }, + sending: { en: 'Sending…', 'zh-CN': 'Sending…', 'zh-HK': 'Sending…' }, + sign: { en: 'API Key', 'zh-CN': 'API Key', 'zh-HK': 'API Key' }, + oauth: { en: 'OAuth 2.0', 'zh-CN': 'OAuth 2.0', 'zh-HK': 'OAuth 2.0' }, + accessToken: { en: 'Access Token', 'zh-CN': 'Access Token', 'zh-HK': 'Access Token' }, + settings: { en: 'Settings', 'zh-CN': '设置', 'zh-HK': '設置' }, + authMode: { en: 'Auth method', 'zh-CN': '鉴权方式', 'zh-HK': '鑑權方式' }, +} as const + +// Try-it requests are capped so a hung socket (proxy stall, dropped network) +// can't leave the button stuck on "Sending…" forever. The timer is always +// cleared (via .finally) so it never dangles after a fast success. +const REQUEST_TIMEOUT_MS = 30_000 +function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> { + let t: ReturnType<typeof setTimeout> + const timer = new Promise<never>((_, reject) => { + t = setTimeout(() => reject(new Error('Request timed out')), ms) + }) + return Promise.race([promise, timer]).finally(() => clearTimeout(t)) as Promise<T> +} + +const IconSettings = () => ( + <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"> + <circle cx="12" cy="12" r="3" /> + <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" /> + </svg> +) + +export interface RequestPanelProps { + method: string + path: string + xparams: XParameter[] + /** Authored OAuth (Bearer) samples from the spec, shown in OAuth mode. */ + oauthBlocks: CodeBlock[] + locale: Locale + onResponse: (r: ApiResponse) => void + labelCopy: string + labelCopied: string +} + +export function RequestPanel({ + method, + path, + xparams, + oauthBlocks, + locale, + onResponse, + labelCopy, + labelCopied, +}: RequestPanelProps) { + const { baseUrl, displayBaseUrl, authMode, setAuthMode } = useEnv() + const { authData, setAuthData, autoFilled } = useAuthorization() + const [showSettings, setShowSettings] = useState(false) + const [sending, setSending] = useState(false) + const [values, setValues] = useState<Record<string, unknown>>({}) + // Required params flagged as empty after a failed Try it (highlighted red). + const [invalidParams, setInvalidParams] = useState<Set<string>>(new Set()) + const panelRef = useRef<HTMLElement>(null) + const settingsRef = useRef<HTMLDivElement>(null) + + const paramRows = useMemo<ParameterRow[]>( + () => + xparams.map((p) => ({ + name: p.name, + type: p.type ?? 'string', + description: pickLocale(p.description, p['x-description-zh'], p['x-description-zh-hk'], locale), + required: p.required, + })), + [xparams, locale] + ) + + // Both modes expose the same 8 languages: OAuth shows the authored Bearer + // samples from the spec; Signed shows client-generated HMAC-signed samples. + const shownBlocks = useMemo<CodeBlock[]>( + () => (authMode === 'oauth' ? oauthBlocks : signedCodeBlocks(method, path, displayBaseUrl)), + [authMode, oauthBlocks, method, path, displayBaseUrl] + ) + + // Substitute what the user typed (token + params) into the code so the + // displayed/copied sample reflects their input instead of `<placeholders>`. + const filledBlocks = useMemo<CodeBlock[]>(() => { + const sub = (code: string): string => { + let out = code + if (authData.appKey) out = out.split('<app_key>').join(authData.appKey) + if (authData.appSecret) out = out.split('<app_secret>').join(authData.appSecret) + if (authData.accessToken) out = out.split('<access_token>').join(authData.accessToken) + for (const p of xparams) { + const v = values[p.name] + if (v === undefined || v === '') continue + out = out.split(`<${p.name}>`).join(String(v)) + } + return out + } + return shownBlocks.map((b) => ({ ...b, code: sub(b.code) })) + }, [shownBlocks, authData, values, xparams]) + + const isReq = (v?: string | boolean): boolean => { + if (typeof v === 'boolean') return v + if (!v) return false + const l = String(v).toLowerCase() + return l === 'true' || l === 'yes' || l === '是' + } + const authFilled = + authMode === 'oauth' + ? !!authData.accessToken?.trim() + : !!(authData.appKey?.trim() && authData.appSecret?.trim() && authData.accessToken?.trim()) + + // Try it validates on click (never disabled): if the token or a required param + // is missing, jump to and highlight the first empty field instead of sending. + const send = async () => { + if (!authFilled) { + setShowSettings(true) + setTimeout(() => { + const inputs = settingsRef.current?.querySelectorAll<HTMLInputElement>('input') + const target = inputs && Array.from(inputs).find((i) => !i.value.trim()) + const el = target ?? inputs?.[0] + el?.scrollIntoView({ block: 'center', behavior: 'smooth' }) + el?.focus() + }, 0) + return + } + const missing = xparams + .filter((p) => isReq(p.required) && String(values[p.name] ?? '').trim() === '') + .map((p) => p.name) + if (missing.length > 0) { + setInvalidParams(new Set(missing)) + setTimeout(() => { + const el = panelRef.current?.querySelector<HTMLElement>(`[data-param="${CSS.escape(missing[0])}"]`) + el?.scrollIntoView({ block: 'center', behavior: 'smooth' }) + el?.focus() + }, 0) + return + } + setInvalidParams(new Set()) + setSending(true) + try { + let finalPath = path + const query: Record<string, unknown> = {} + const body: Record<string, unknown> = {} + for (const p of xparams) { + const v = values[p.name] + if (v === undefined || v === '') continue + if (p.in === 'path') finalPath = finalPath.replace(`{${p.name}}`, String(v)) + else if (p.in === 'body') body[p.name] = v + else query[p.name] = v + } + finalPath = finalPath.replace(/\{[^}]+\}/g, '1') + const m = method.toLowerCase() + + if (authMode === 'oauth') { + // Raw Bearer request — no signing. + const qs = new URLSearchParams( + Object.entries(query).map(([k, v]) => [k, String(v)]) + ).toString() + const hasBody = m === 'post' || m === 'put' || m === 'patch' + const url = `${baseUrl}${finalPath}${qs ? `?${qs}` : ''}` + const ctrl = new AbortController() + const timer = setTimeout(() => ctrl.abort(), REQUEST_TIMEOUT_MS) + const res = await fetch(url, { + method: method.toUpperCase(), + headers: { + Authorization: `Bearer ${authData.accessToken}`, + ...(hasBody ? { 'Content-Type': 'application/json' } : {}), + }, + body: hasBody ? JSON.stringify(body) : undefined, + signal: ctrl.signal, + }).finally(() => clearTimeout(timer)) + // Read as text first so a non-JSON body (proxy/HTML error, empty 204) is + // surfaced instead of a generic "non-JSON response". + const text = await res.text() + let json: unknown + try { + json = text ? JSON.parse(text) : { code: res.status, msg: `HTTP ${res.status} ${res.statusText}`, data: null } + } catch { + json = { code: res.status, msg: text.slice(0, 800), data: null } + } + onResponse({ status: res.status, statusText: res.statusText, response: json as ApiResponse['response'] }) + return + } + + const client = createQuickRequest(authData.appKey, authData.accessToken, authData.appSecret, { baseUrl }) + const call = + m === 'post' ? client.post(finalPath, body) + : m === 'put' ? client.put(finalPath, body) + : m === 'delete' ? client.delete(finalPath, query) + : client.get(finalPath, query) + const res = await withTimeout(call, REQUEST_TIMEOUT_MS) + onResponse(res) + } catch (err) { + onResponse({ status: 0, statusText: 'Error', response: { code: -1, msg: err instanceof Error ? err.message : String(err), data: null } }) + } finally { + setSending(false) + } + } + + return ( + <section ref={panelRef} className="api-rail-card" data-lbus-component="request-panel"> + <div className="api-rail-head"> + <span className="api-rail-title">{L.request[locale]}</span> + <button + type="button" + className="api-rail-iconbtn" + aria-expanded={showSettings} + aria-label={L.settings[locale]} + title={L.settings[locale]} + onClick={() => setShowSettings((v) => !v)}> + <IconSettings /> + </button> + </div> + {showSettings && ( + <div className="api-rail-tokenform" ref={settingsRef}> + <Dropdown<AuthMode> + className="ar-auth-block" + ariaLabel={L.authMode[locale]} + value={authMode} + options={[ + { value: 'sign', label: L.sign[locale] }, + { value: 'oauth', label: L.oauth[locale] }, + ]} + onChange={setAuthMode} + /> + {authMode === 'sign' ? ( + <AuthorizationForm authData={authData} autoFilled={autoFilled} onChange={setAuthData} /> + ) : ( + <div className="api-rail-oauthform"> + <label className="api-rail-oauthlabel">{L.accessToken[locale]}</label> + <input + className="tryit-input" + type="password" + value={authData.accessToken} + placeholder={L.accessToken[locale]} + onChange={(e) => setAuthData({ ...authData, accessToken: e.target.value })} + /> + </div> + )} + </div> + )} + {filledBlocks.length > 0 && <CodeDropdown blocks={filledBlocks} labelCopy={labelCopy} labelCopied={labelCopied} />} + {paramRows.length > 0 && ( + <div className="api-rail-params"> + <ParametersForm + parameters={paramRows} + invalid={invalidParams} + onChange={(d) => { + setValues(d) + if (invalidParams.size) setInvalidParams(new Set()) + }} + /> + </div> + )} + <button type="button" className="api-rail-send" disabled={sending} onClick={send}> + {sending ? L.sending[locale] : L.send[locale]} + </button> + </section> + ) +} diff --git a/packages/api-reference/src/ResponsePanel.tsx b/packages/api-reference/src/ResponsePanel.tsx new file mode 100644 index 000000000..b6edfec6d --- /dev/null +++ b/packages/api-reference/src/ResponsePanel.tsx @@ -0,0 +1,68 @@ +/** + * ResponsePanel — right-rail bottom card: status tabs (200/400/401/403/408) + * showing documented example bodies; when a live TryIt response arrives it is + * routed to its status tab and flagged 实测。 + */ +import { useEffect, useState } from 'react' +import type { Locale } from '@longbridge/openapi-utils' +import type { ApiResponse } from '@longbridge/openapi-tryit' +import { highlightCode } from './CodeSample' +import type { ResponseExample } from './openapi-loader' + +const L = { + response: { en: 'Response', 'zh-CN': '响应', 'zh-HK': '響應' }, + live: { en: 'Live', 'zh-CN': '实测', 'zh-HK': '實測' }, + error: { en: 'Error', 'zh-CN': '错误', 'zh-HK': '錯誤' }, +} as const + +export interface ResponsePanelProps { + examples: ResponseExample[] + live: ApiResponse | null + locale: Locale +} + +export function ResponsePanel({ examples, live, locale }: ResponsePanelProps) { + const statuses = examples.map((e) => e.status) + // A live response always gets a tab — including a network/timeout failure, + // which surfaces as status 0 (rendered as an "Error" tab) so the user gets + // feedback instead of the request silently falling back to the doc example. + const liveStatus = live ? live.status : null + const [active, setActive] = useState<number>(statuses[0] ?? 200) + + // Jump to the live response's tab when one arrives (0 is a valid tab here). + useEffect(() => { + if (liveStatus != null) setActive(liveStatus) + }, [live, liveStatus]) + + const tabStatuses = liveStatus != null && !statuses.includes(liveStatus) ? [...statuses, liveStatus] : statuses + // Guard against a stale `active` (e.g. an error tab left over from a prior + // endpoint) that no longer exists in the current tab set. + const shownActive = tabStatuses.includes(active) ? active : (tabStatuses[0] ?? 200) + const isLiveTab = liveStatus != null && liveStatus === shownActive + const body = isLiveTab + ? JSON.stringify((live as any).response ?? live, null, 2) + : (examples.find((e) => e.status === shownActive)?.body ?? '') + + return ( + <section className="api-rail-card" data-lbus-component="response-panel"> + <div className="api-rail-head"> + <span className="api-rail-title">{L.response[locale]}</span> + {isLiveTab && <span className="api-rail-live">{L.live[locale]}</span>} + </div> + <div className="api-rail-tabs" role="tablist"> + {tabStatuses.map((s) => ( + <button + key={s} + type="button" + className={`api-rail-tab${s === shownActive ? ' is-active' : ''}${s === 0 || s >= 400 ? ' is-err' : ''}`} + onClick={() => setActive(s)}> + {s === 0 ? L.error[locale] : s} + </button> + ))} + </div> + <pre className="code-pre"> + <code dangerouslySetInnerHTML={{ __html: highlightCode(body, 'json') }} /> + </pre> + </section> + ) +} diff --git a/packages/api-reference/src/api-reference.css b/packages/api-reference/src/api-reference.css index c0f8fbf83..f3bccfcfb 100644 --- a/packages/api-reference/src/api-reference.css +++ b/packages/api-reference/src/api-reference.css @@ -5,15 +5,121 @@ * Syntax highlight classes: .hl-k .hl-s .hl-n .hl-b .hl-cmd .hl-flag .hl-punct .hl-comment */ -/* ── Root layout ─────────────────────────────────────────────────────────── */ +/* Quote-permission card + `:::` callout boxes — ported 1:1 from docs so they + render identically in the reference. */ +@import './QuotePermission.css'; +@import './callout.css'; + +/* ── Canonical design tokens ───────────────────────────────────────────────── + Status colors, focus ring, and radius scale. These names are shared across + files — keep them stable. Light values here; dark overrides below. */ +:root { + /* Status: success / info / warning / danger / note */ + --lb-c-success-bg: #dcfce7; + --lb-c-success-fg: #166534; + --lb-c-success-border: #86efac; + --lb-c-info-bg: #dbeafe; + --lb-c-info-fg: #1e40af; + --lb-c-info-border: #93c5fd; + --lb-c-warning-bg: #fef9c3; + --lb-c-warning-fg: #854d0e; + --lb-c-warning-border: #fde047; + --lb-c-danger-bg: #fee2e2; + --lb-c-danger-fg: #991b1b; + --lb-c-danger-border: #fca5a5; + --lb-c-note-bg: #f3e8ff; + --lb-c-note-fg: #6b21a8; + --lb-c-note-border: #d8b4fe; + + /* Bridge the phantom --lb-c-* vocabulary to the host site's real design + tokens, so the reference inherits the site's teal brand and light/dark + surfaces instead of silently falling back to VitePress blue/slate. These + reference dark-aware site tokens, so they need no per-mode overrides. */ + --lb-c-brand: var(--lb-brand, #00b8b8); + --lb-c-brand-soft: var(--lb-brand-2, #e5f8f8); + --lb-c-bg: var(--lb-bg-1, #fff); + --lb-c-bg-soft: var(--lb-bg-2, #f3f5f6); + --lb-c-bg-mute: var(--lb-bg-2, #f3f5f6); + --lb-c-text-1: var(--lb-fg-1, #0a0e19); + --lb-c-text-2: var(--lb-fg-2, #6c6e75); + --lb-c-text-3: var(--lb-fg-3, #a9abae); + --lb-c-divider: var(--lb-stroke, #e6e7e8); + --lb-c-divider-light: var(--lb-stroke, #e6e7e8); + + /* Focus ring — 2px page-colored gap + 2px brand ring for visible offset */ + --lb-focus-ring: 0 0 0 2px var(--lb-c-bg, #fff), 0 0 0 4px var(--lb-brand, #00b8b8); + + /* Height of the site's sticky TopNav — the reference's sticky rail/code + panels offset from it. Matches TopNav's h-[60px]; the old 64px fallback + left a 4px gap. */ + --lb-nav-height: 60px; + + /* Radius scale — private to the api-reference (prefixed --ar- so it never + collides with the host site's --lb-radius-* tokens). */ + --ar-radius-sm: 6px; + --ar-radius-md: 6px; /* aligned to the site's 6px (sm) step — the scale has no 8px */ + --ar-radius-lg: 12px; + --ar-radius-pill: 999px; +} + +:root[data-mode='dark'] { + --lb-c-success-bg: rgba(35, 134, 54, 0.18); + --lb-c-success-fg: #56d364; + --lb-c-success-border: rgba(63, 185, 80, 0.4); + --lb-c-info-bg: rgba(56, 139, 253, 0.18); + --lb-c-info-fg: #79c0ff; + --lb-c-info-border: rgba(56, 139, 253, 0.4); + --lb-c-warning-bg: rgba(187, 128, 9, 0.2); + --lb-c-warning-fg: #e3b341; + --lb-c-warning-border: rgba(187, 128, 9, 0.45); + --lb-c-danger-bg: rgba(248, 81, 73, 0.18); + --lb-c-danger-fg: #ff7b72; + --lb-c-danger-border: rgba(248, 81, 73, 0.4); + --lb-c-note-bg: rgba(163, 113, 247, 0.18); + --lb-c-note-fg: #d2a8ff; + --lb-c-note-border: rgba(163, 113, 247, 0.4); + + --lb-focus-ring: 0 0 0 2px var(--lb-c-bg, #fff), 0 0 0 4px var(--lb-brand, #00f0c4); +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode='light']) { + --lb-c-success-bg: rgba(35, 134, 54, 0.18); + --lb-c-success-fg: #56d364; + --lb-c-success-border: rgba(63, 185, 80, 0.4); + --lb-c-info-bg: rgba(56, 139, 253, 0.18); + --lb-c-info-fg: #79c0ff; + --lb-c-info-border: rgba(56, 139, 253, 0.4); + --lb-c-warning-bg: rgba(187, 128, 9, 0.2); + --lb-c-warning-fg: #e3b341; + --lb-c-warning-border: rgba(187, 128, 9, 0.45); + --lb-c-danger-bg: rgba(248, 81, 73, 0.18); + --lb-c-danger-fg: #ff7b72; + --lb-c-danger-border: rgba(248, 81, 73, 0.4); + --lb-c-note-bg: rgba(163, 113, 247, 0.18); + --lb-c-note-fg: #d2a8ff; + --lb-c-note-border: rgba(163, 113, 247, 0.4); + + --lb-focus-ring: 0 0 0 2px var(--lb-c-bg, #fff), 0 0 0 4px var(--lb-brand, #00f0c4); + } +} -.api-reference-page { - display: grid; - grid-template-columns: 280px 1fr; - grid-template-rows: 1fr; - min-height: calc(100vh - var(--lb-nav-height, 64px)); - background: var(--lb-c-bg, #fff); - color: var(--lb-c-text-1, #213547); +/* ── Focus ring (keyboard) — shared across all interactive controls ───────── + Never remove an outline without a visible replacement. */ +.ep-env-seg:focus-visible, +.code-tab:focus-visible, +.ep-urlbar-icon:focus-visible, +.ep-copypage-toggle:focus-visible, +.ep-copypage-main:focus-visible, +.ep-copypage-btn:focus-visible, +.ep-copypage-item:focus-visible, +.api-rail-authselect:focus-visible, +.api-rail-tab:focus-visible, +.api-rail-tokenbtn:focus-visible, +.api-rail-send:focus-visible, +.code-lang-select:focus-visible, +.intro-cat-card:focus-visible { + box-shadow: var(--lb-focus-ring); + outline: none; } /* ── Sidebar ──────────────────────────────────────────────────────────────── */ @@ -39,7 +145,7 @@ .search-input { width: 100%; padding: 6px 10px; - border-radius: 6px; + border-radius: var(--ar-radius-md); border: 1px solid var(--lb-c-divider, #e2e8f0); background: var(--lb-c-bg, #fff); color: var(--lb-c-text-1, #213547); @@ -69,63 +175,18 @@ border-radius: 2px; } -/* ── Sidebar nav items ────────────────────────────────────────────────────── */ - -.nav-item { - display: flex; - align-items: center; - gap: 6px; - width: 100%; - padding: 6px 16px; - text-align: left; - background: none; - border: none; - border-radius: 0; - cursor: pointer; - font-size: 13px; - color: var(--lb-c-text-2, #476582); - line-height: 1.4; - transition: background 0.15s, color 0.15s; -} - -.nav-item:hover { - background: var(--lb-c-bg-mute, #f0f4f8); - color: var(--lb-c-text-1, #213547); -} - .nav-item.is-active { background: var(--lb-c-brand-soft, #dbeafe); color: var(--lb-c-brand, #2563eb); font-weight: 500; } -/* Pages nav items have no method badge — just text */ -.page-group .nav-item { - font-weight: 500; - font-size: 13px; -} - -.tag-group { - margin-top: 4px; -} - -.tag-label { - padding: 8px 16px 4px; - font-size: 11px; - font-weight: 600; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--lb-c-text-3, #94a3b8); - margin: 0; -} - /* ── Method badges (nav + detail) ────────────────────────────────────────── */ -.nav-method, .ep-method-badge { display: inline-block; padding: 1px 5px; - border-radius: 3px; + border-radius: var(--ar-radius-sm); font-size: 10px; font-weight: 700; letter-spacing: 0.04em; @@ -133,46 +194,36 @@ flex-shrink: 0; } -.method-get { background: #dcfce7; color: #166534; } -.method-post { background: #dbeafe; color: #1e40af; } -.method-put { background: #fef9c3; color: #854d0e; } -.method-delete { background: #fee2e2; color: #991b1b; } -.method-patch { background: #f3e8ff; color: #6b21a8; } -.method-ws, -.method-websocket { background: #fce7f3; color: #9d174d; } - -.nav-label { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* ── Main area ────────────────────────────────────────────────────────────── */ - -.api-main, -.api-intro { - grid-column: 2; - padding: 40px 48px; - overflow-y: auto; -} - -/* Endpoint split: two columns */ -.api-main--split { - display: grid; - grid-template-columns: 1fr 440px; - gap: 40px; - align-items: start; +/* Sidebar method badge — fixed width so GET/POST/PUT/DELETE all align, and + subtle enough to sit next to the docs-style item text. */ +.nav-method { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 42px; + margin-right: 8px; + padding: 2px 0; + border-radius: var(--ar-radius-sm); + font-size: 9px; + font-weight: 600; + letter-spacing: 0.03em; + text-transform: uppercase; + line-height: 1.4; } -.api-content { - min-width: 0; -} +.method-get { background: var(--lb-c-success-bg); color: var(--lb-c-success-fg); } +.method-post { background: var(--lb-c-info-bg); color: var(--lb-c-info-fg); } +.method-put { background: var(--lb-c-warning-bg); color: var(--lb-c-warning-fg); } +.method-delete { background: var(--lb-c-danger-bg); color: var(--lb-c-danger-fg); } +.method-patch { background: var(--lb-c-note-bg); color: var(--lb-c-note-fg); } +.method-ws, +.method-websocket { background: #fce7f3; color: #9d174d; } /* ── Intro panel ─────────────────────────────────────────────────────────── */ .intro-content { - max-width: 660px; + max-width: 900px; } .intro-title { @@ -185,44 +236,117 @@ .intro-desc { font-size: 15px; color: var(--lb-c-text-2, #476582); - margin: 0 0 28px; + margin: 0 0 12px; line-height: 1.7; } -.intro-cards { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; - margin-bottom: 28px; +.intro-section { + margin: 28px 0 0; +} +.intro-h2 { + font-size: 15px; + font-weight: 600; + margin: 0 0 12px; + color: var(--lb-c-text-1, #213547); } -.intro-card { - padding: 20px; +/* Base URLs */ +.intro-urls { + display: flex; + flex-wrap: wrap; + gap: 10px; +} +.intro-url { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; border: 1px solid var(--lb-c-divider, #e2e8f0); - border-radius: 8px; - background: var(--lb-c-bg-soft, #f9fafb); + border-radius: var(--ar-radius-md); + background: var(--lb-bg-2, #f8fafc); +} +.intro-url code { + font-family: var(--lb-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); + font-size: 13px; + color: var(--lb-fg-1, #1f2937); +} +.intro-url-tag { + font-size: 11px; + font-weight: 600; + padding: 1px 8px; + border-radius: var(--ar-radius-pill); + background: rgba(22, 163, 74, 0.14); + color: #15803d; +} +.intro-url-tag--test { + background: rgba(100, 116, 139, 0.16); + color: #475569; } -.intro-card-title { - display: block; +/* Quick links */ +.intro-links { + display: flex; + flex-wrap: wrap; + gap: 8px 18px; +} +.intro-link { font-size: 14px; - font-weight: 600; - margin-bottom: 8px; - color: var(--lb-c-text-1, #213547); + font-weight: 500; + color: var(--lb-brand, #16a34a); + background: transparent; + border: none; + padding: 0; + cursor: pointer; + text-decoration: none; + text-align: left; +} +.intro-link:hover { + text-decoration: underline; } -.intro-card-desc { - font-size: 13px; - color: var(--lb-c-text-2, #476582); +/* Category cards */ +.intro-cards { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + gap: 12px; margin: 0; - line-height: 1.6; } - -.intro-hint { - font-size: 13px; +.intro-cat-card { + display: flex; + flex-direction: column; + gap: 4px; + padding: 14px 16px; + border: 1px solid var(--lb-c-divider, #e2e8f0); + border-radius: var(--ar-radius-md); + background: var(--lb-c-bg-soft, #f9fafb); + cursor: pointer; + text-align: left; + transition: border-color 0.15s, box-shadow 0.15s; +} +.intro-cat-card:hover:not(:disabled) { + border-color: var(--lb-brand, #16a34a); + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06); +} +.intro-cat-card:disabled { + opacity: 0.55; + cursor: default; +} +.intro-cat-card--ws { + border-style: dashed; +} +.intro-cat-name { + font-size: 14px; + font-weight: 600; + color: var(--lb-c-text-1, #213547); +} +.intro-cat-count { + font-size: 12px; color: var(--lb-c-text-3, #94a3b8); - margin: 0; - font-style: italic; +} +:root[data-mode='dark'] .intro-url, +:root[data-mode='dark'] .intro-cat-card { + background: #161b22; + border-color: #30363d; } /* ── Endpoint header ─────────────────────────────────────────────────────── */ @@ -230,8 +354,8 @@ .ep-tag { font-size: 11px; font-weight: 600; - letter-spacing: 0.06em; - text-transform: uppercase; + letter-spacing: normal; + text-transform: none; color: var(--lb-c-brand, #2563eb); margin: 0 0 8px; } @@ -251,7 +375,7 @@ padding: 10px 14px; background: var(--lb-c-bg-soft, #f9fafb); border: 1px solid var(--lb-c-divider, #e2e8f0); - border-radius: 6px; + border-radius: var(--ar-radius-md); margin-bottom: 20px; font-family: var(--lb-font-mono, 'Fira Code', 'JetBrains Mono', monospace); flex-wrap: wrap; @@ -270,21 +394,9 @@ color: var(--lb-c-text-2, #476582); } +/* Layout-only; visual treatment is the shared copy-button rule below. */ .path-copy-btn { margin-left: auto; - padding: 3px 8px; - border: 1px solid var(--lb-c-divider, #e2e8f0); - border-radius: 4px; - background: var(--lb-c-bg, #fff); - font-size: 11px; - cursor: pointer; - color: var(--lb-c-text-2, #476582); - transition: background 0.15s, color 0.15s; -} - -.path-copy-btn:hover { - background: var(--lb-c-bg-mute, #f0f4f8); - color: var(--lb-c-text-1, #213547); } /* ── Prose / vp-doc area ─────────────────────────────────────────────────── */ @@ -297,6 +409,10 @@ margin-bottom: 24px; } +/* Standalone content pages (Overview, Authentication, Realtime Quote, Error + Codes) use the full column width — their tables and code blocks need the room. + No max-width cap here. */ + .prose p, .vp-doc p { margin: 0 0 12px; } @@ -309,7 +425,31 @@ font-size: 0.875em; background: var(--lb-c-bg-mute, #f0f4f8); padding: 1px 4px; - border-radius: 3px; + border-radius: var(--ar-radius-sm); +} + +/* Fenced code blocks in doc markdown (e.g. WS protobuf schemas) render as a + code-card: white surface + border, matching the endpoint code panels. */ +.prose pre, +.vp-doc pre { + margin: 0 0 16px; + padding: 16px; + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, #e2e8f0); + border-radius: var(--ar-radius-md); + overflow-x: auto; + font-size: 12.5px; + line-height: 1.7; + font-family: var(--lb-font-mono, 'Fira Code', 'JetBrains Mono', monospace); + color: var(--lb-fg-1, #1f2937); + tab-size: 2; +} +.prose pre code, +.vp-doc pre code { + background: none; + padding: 0; + font-size: inherit; + border-radius: 0; } .prose h2, .vp-doc h2 { font-size: 18px; font-weight: 600; margin: 20px 0 12px; } @@ -321,63 +461,6 @@ margin-bottom: 32px; } -.section-title { - font-size: 13px; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--lb-c-text-3, #94a3b8); - margin: 0 0 12px; - padding-bottom: 6px; - border-bottom: 1px solid var(--lb-c-divider, #e2e8f0); -} - -.param-list { - display: flex; - flex-direction: column; - gap: 0; -} - -.param-row { - padding: 12px 0; - border-bottom: 1px solid var(--lb-c-divider-light, #f1f5f9); -} - -.param-row:last-child { - border-bottom: none; -} - -.param-meta { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 4px; -} - -.param-name { - font-family: var(--lb-font-mono, monospace); - font-size: 13px; - font-weight: 600; - color: var(--lb-c-text-1, #213547); - background: none; - padding: 0; -} - -.param-type { - font-size: 11px; - padding: 1px 6px; - border-radius: 4px; - background: var(--lb-c-bg-mute, #f0f4f8); - color: var(--lb-c-text-3, #94a3b8); - font-family: var(--lb-font-mono, monospace); -} - -.param-required { - font-size: 11px; - padding: 1px 6px; - border-radius: 4px; -} - .param-required.is-required { background: #fee2e2; color: #991b1b; @@ -388,13 +471,6 @@ color: var(--lb-c-text-3, #94a3b8); } -.param-desc { - font-size: 13px; - color: var(--lb-c-text-2, #476582); - margin: 0; - line-height: 1.6; -} - .param-fallback { font-size: 13px; color: var(--lb-c-text-3, #94a3b8); @@ -413,10 +489,10 @@ } .code-card { - border-radius: 8px; + border-radius: var(--ar-radius-md); overflow: hidden; - border: 1px solid var(--lb-c-divider, #e2e8f0); - background: #1e2a3a; + border: 1px solid var(--lb-stroke, #e2e8f0); + background: var(--lb-bg-1, #fff); } .card-header { @@ -424,8 +500,8 @@ align-items: center; justify-content: space-between; padding: 8px 14px; - background: #16202d; - border-bottom: 1px solid rgba(255,255,255,0.06); + background: var(--lbus-c-bg, #fff); + border-bottom: 1px solid var(--lb-stroke, rgba(0,0,0,0.06)); } .card-label { @@ -433,23 +509,50 @@ font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; - color: #94a3b8; + color: var(--lb-fg-2, #6c6e75); } -.copy-btn { - font-size: 11px; - padding: 2px 8px; - border: 1px solid rgba(255,255,255,0.12); - border-radius: 4px; - background: rgba(255,255,255,0.06); - color: #94a3b8; +/* Shared copy-ICON treatment — one ghost icon button used by every copy + affordance so the whole reference speaks one icon language. */ +.copy-btn, +.code-tabs-copy, +.page-copy-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 26px; + height: 26px; + padding: 0; + background: transparent; + border: none; + border-radius: var(--ar-radius-sm); + color: var(--lb-fg-2, #6c6e75); + flex: none; cursor: pointer; transition: background 0.15s, color 0.15s; } - -.copy-btn:hover { - background: rgba(255,255,255,0.1); - color: #e2e8f0; +.copy-btn:hover, +.code-tabs-copy:hover, +.page-copy-btn:hover { + background: var(--lb-bg-2, #f3f5f6); + color: var(--lb-fg-1, #1f2937); +} +/* Text copy button (labelled) keeps a ghost pill treatment. */ +.path-copy-btn { + background: transparent; + border: 1px solid var(--lb-stroke, #e2e8f0); + border-radius: var(--ar-radius-sm); + color: var(--lb-fg-2, #6c6e75); + font-size: 12px; + padding: 4px 8px; + white-space: nowrap; + flex: none; + cursor: pointer; + transition: background 0.15s, color 0.15s; +} +.path-copy-btn:hover { + background: var(--lb-bg-2, #f3f5f6); + color: var(--lb-fg-1, #1f2937); } .card-body { @@ -462,149 +565,1089 @@ font-size: 12.5px; line-height: 1.7; font-family: var(--lb-font-mono, 'Fira Code', 'JetBrains Mono', monospace); - color: #e2e8f0; + color: var(--lb-fg-1, #1f2937); white-space: pre; tab-size: 2; } +.code-card .code-pre { + background: var(--lb-bg-1, #fff); +} + +/* ── Syntax highlighting — GitHub token colors, theme-aware ────────────────── + Light (github-light) is the default; dark overrides live in the dark block. */ +.hl-k { color: #cf222e; } /* keywords */ +.hl-s { color: #0a3069; } /* strings */ +.hl-n { color: #0550ae; } /* numbers */ +.hl-b { color: #0550ae; } /* booleans / null */ +.hl-cmd { color: #8250df; } /* command name */ +.hl-flag { color: #0550ae; } /* flags / options */ +.hl-punct{ color: #24292f; } /* punctuation */ +.hl-comment { color: #6e7781; font-style: italic; } + +:root[data-mode="dark"] .hl-k { color: #ff7b72; } +:root[data-mode="dark"] .hl-s { color: #a5d6ff; } +:root[data-mode="dark"] .hl-n { color: #79c0ff; } +:root[data-mode="dark"] .hl-b { color: #79c0ff; } +:root[data-mode="dark"] .hl-cmd { color: #d2a8ff; } +:root[data-mode="dark"] .hl-flag { color: #79c0ff; } +:root[data-mode="dark"] .hl-punct{ color: #c9d1d9; } +:root[data-mode="dark"] .hl-comment { color: #8b949e; } +@media (prefers-color-scheme: dark) { + :root:not([data-mode="light"]) .hl-k { color: #ff7b72; } + :root:not([data-mode="light"]) .hl-s { color: #a5d6ff; } + :root:not([data-mode="light"]) .hl-n { color: #79c0ff; } + :root:not([data-mode="light"]) .hl-b { color: #79c0ff; } + :root:not([data-mode="light"]) .hl-cmd { color: #d2a8ff; } + :root:not([data-mode="light"]) .hl-flag { color: #79c0ff; } + :root:not([data-mode="light"]) .hl-punct{ color: #c9d1d9; } + :root:not([data-mode="light"]) .hl-comment { color: #8b949e; } +} -/* ── Syntax highlighting ─────────────────────────────────────────────────── */ +/* ── Responsive ──────────────────────────────────────────────────────────── */ -.hl-k { color: #f472b6; } /* keywords */ -.hl-s { color: #86efac; } /* strings */ -.hl-n { color: #fdba74; } /* numbers */ -.hl-b { color: #7dd3fc; } /* booleans / null */ -.hl-cmd { color: #60a5fa; } /* command name */ -.hl-flag { color: #c084fc; } /* flags / options */ -.hl-punct{ color: #94a3b8; } /* punctuation */ -.hl-comment { color: #475569; font-style: italic; } +@media (max-width: 1024px) { + .api-reference-page { + grid-template-columns: 240px 1fr; + } -/* ── QuotePermission ─────────────────────────────────────────────────────── */ + .api-main--split { + grid-template-columns: 1fr; + } -.qp-wrapper { - padding: 14px 16px; - border: 1px solid var(--lb-c-divider, #e2e8f0); - border-radius: 8px; - background: var(--lb-c-bg-soft, #f9fafb); - margin-bottom: 20px; + .code-panel { + position: static; + } } -.qp-header { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 8px; -} +@media (max-width: 768px) { -.qp-title { - font-size: 12px; - font-weight: 600; - color: var(--lb-c-text-3, #94a3b8); - text-transform: uppercase; - letter-spacing: 0.05em; + .api-sidebar { + position: static; + height: auto; + border-right: none; + border-bottom: 1px solid var(--lb-c-divider, #e2e8f0); + } + + .intro-cards { + grid-template-columns: 1fr; + } } -.qp-badge { - display: inline-block; - padding: 2px 8px; - border-radius: 10px; - font-size: 11px; +/* ── Dark mode ───────────────────────────────────────────────────────────── */ + +:root[data-mode="dark"] .code-card, +:root[data-mode="dark"] .code-card .code-pre, +:root[data-mode="dark"] .prose pre, +:root[data-mode="dark"] .vp-doc pre { + background: #0d1117; + color: #e6edf3; + border-color: rgba(255,255,255,0.08); +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode="light"]) .code-card, + :root:not([data-mode="light"]) .code-card .code-pre, + :root:not([data-mode="light"]) .prose pre, + :root:not([data-mode="light"]) .vp-doc pre { + background: #0d1117; + color: #e6edf3; + border-color: rgba(255,255,255,0.08); + } +} + +:root[data-mode="dark"] .card-header { + background: #161b22; + border-bottom-color: rgba(255,255,255,0.06); +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode="light"]) .card-header { + background: #161b22; + border-bottom-color: rgba(255,255,255,0.06); + } +} + +/* "SDK method parameters." note under the Parameters heading */ +.section-note { + margin: -4px 0 12px; + font-size: 13px; + color: #6b7280; +} + +/* Parameters / schema tables — mirror docs `.docs-content table` */ +.api-table-wrap { + overflow-x: auto; + max-width: 100%; +} +/* Fixed layout + shared column widths so every field table on a page lines up + (params, response fields, auth headers all use the same column geometry). + Scoped under `.docs-content` to override the site's `table{display:block; + width:max-content}` rule, which would otherwise size each table to its own + content and break alignment. */ +.docs-content .api-param-table, +.docs-content .api-fields { + display: table; + border-collapse: collapse; + table-layout: fixed; + width: 100%; + max-width: 100%; + margin: 8px 0 4px; + font-size: 14px; +} +.api-param-table th:nth-child(1), +.api-param-table td:nth-child(1), +.api-fields th:nth-child(1), +.api-fields td:nth-child(1) { + width: 24%; +} +.api-param-table th:nth-child(2), +.api-param-table td:nth-child(2), +.api-fields th:nth-child(2), +.api-fields td:nth-child(2) { + width: 15%; +} +.api-param-table th:nth-child(3), +.api-param-table td:nth-child(3), +.api-fields th:nth-child(3), +.api-fields td:nth-child(3) { + width: 9%; +} +/* Type / Required columns must not wrap (esp. the 2-char 必填/可选). Name and + Description may wrap within their fixed column. */ +.api-param-table th:nth-child(2), +.api-param-table td:nth-child(2), +.api-param-table th:nth-child(3), +.api-param-table td:nth-child(3), +.api-fields th:nth-child(2), +.api-fields td:nth-child(2), +.api-fields th:nth-child(3), +.api-fields td:nth-child(3) { + white-space: nowrap; +} +/* Long nested names (e.g. attached_params.profit_taker_submit_price) must wrap + inside the fixed Name column instead of overflowing into the Type column. + Scoped under .docs-content to beat the site's `table td` rule. */ +.docs-content .api-param-table td:nth-child(1), +.docs-content .api-param-table td:nth-child(1) code, +.docs-content .api-fields td:nth-child(1), +.docs-content .api-fields td:nth-child(1) code { + white-space: normal; + word-break: break-all; + overflow-wrap: anywhere; +} +.api-param-table .param-required.is-required { + color: #b91c1c; font-weight: 600; + font-size: 12px; +} +.api-param-table .param-required.is-optional { + color: #94a3b8; + font-size: 12px; } -.qp-badge--green { background: #dcfce7; color: #166534; } -.qp-badge--blue { background: #dbeafe; color: #1e40af; } -.qp-badge--orange { background: #ffedd5; color: #9a3412; } -.qp-badge--yellow { background: #fef9c3; color: #854d0e; } -.qp-badge--purple { background: #f3e8ff; color: #6b21a8; } +/* Code examples flow in the single column (not sticky right rail) */ +.api-main--docs .code-panel { + position: static; + top: auto; + max-width: 100%; +} +.api-section--code { + margin-top: 8px; +} +/* Let the grid track cap the content width (matches docs .docs-inner) */ +.api-doc-layout .api-content--docs { + max-width: none; +} +.api-toc-list li.is-sub a { + padding-left: 26px; + font-size: 12px; +} +.error-code-note { + color: var(--lb-fg-2, #64748b); + font-size: 14px; +} -.qp-market { - font-size: 11px; - padding: 2px 6px; - border-radius: 4px; - background: var(--lb-c-bg-mute, #f0f4f8); - color: var(--lb-c-text-2, #476582); +/* ── Docs-style LIGHT code (tabs + single) ───────────────────────────────── */ +.code-tabs { + margin: 8px 0 4px; + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-md); + overflow: hidden; + background: var(--lb-bg-1, #fff); +} +.code-tabs-bar { + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + background: var(--lbus-c-bg, #fff); +} +.code-tabs-list { + display: flex; + overflow-x: auto; +} +.code-tab { + padding: 8px 14px; + font-size: 13px; + color: var(--lb-fg-2, #64748b); + background: transparent; + border: none; + border-bottom: 2px solid transparent; + cursor: pointer; + white-space: nowrap; +} +.code-tab.is-active { + color: var(--lb-fg-1, #1f2937); + border-bottom-color: var(--lb-brand, #16a34a); + font-weight: 600; +} +.code-tabs-copy { + margin-right: 8px; +} +.code-tabs-body { + overflow-x: auto; +} +.code-tabs .code-pre { + margin: 0; + padding: 16px; + font-family: var(--lb-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); + font-size: 13px; + line-height: 1.6; + color: var(--lb-fg-1, #1f2937); + background: var(--lb-bg-1, #fff); +} + +:root[data-mode="dark"] .code-tabs, +:root[data-mode="dark"] .code-tabs .code-pre { + background: #0d1117; + color: #e6edf3; +} +:root[data-mode="dark"] .code-tabs-bar { + background: #161b22; +} + +/* Field-name column in parameter/response tables: plain monospace, no inline- + code background pill (docs param tables show names without a background). */ +.docs-content .api-fields td code { + background: none; + border: none; + padding: 0; + font-size: 0.92em; + color: inherit; +} + +/* Page-item icon shares the same fixed column width as the method badge so all + sidebar item text (pages + endpoints) aligns to one left edge. */ +.nav-page-icon { + width: 42px; + margin-right: 8px; } -.qp-desc { +/* Markdown code blocks inside x-pages — styled like the CodeTabs card (light, + rounded, bordered) instead of bare text. */ +.api-page-md pre { + margin: 16px 0; + padding: 16px; + overflow-x: auto; + background: var(--lb-bg-2, #f3f5f6); + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-md); + font-family: var(--lb-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); font-size: 13px; - color: var(--lb-c-text-2, #476582); - margin: 0 0 6px; line-height: 1.6; + color: var(--lb-fg-1, #1f2937); +} +.api-page-md pre code { + background: none; + border: none; + padding: 0; + font-size: inherit; + color: inherit; +} +/* Inline code inside x-page prose */ +.api-page-md :not(pre) > code { + background: rgba(0, 0, 0, 0.05); + padding: 2px 5px; + border-radius: var(--ar-radius-sm); + font-size: 0.9em; + font-family: var(--lb-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); +} +:root[data-mode="dark"] .api-page-md pre { + background: #0d1117; + color: #e6edf3; +} +:root[data-mode="dark"] .api-page-md :not(pre) > code { + background: rgba(255, 255, 255, 0.1); } -.qp-note { +/* Copy button injected into x-page markdown code blocks */ +.api-page-md pre .page-copy-btn { + position: absolute; + top: 8px; + right: 8px; + opacity: 0; + transition: opacity 0.15s, background 0.15s, color 0.15s; +} +.api-page-md pre:hover .page-copy-btn { + opacity: 1; +} + +/* Push the slim DocFooter to the bottom on short pages (e.g. the intro) instead + of letting it float up right under the content. Scoped to the reference. */ +[data-lbus-component="api-reference"] .docs-inner { + display: flex; + flex-direction: column; + min-height: calc(100vh - 60px); +} +[data-lbus-component="api-reference"] .docs-main { + flex: 1 0 auto; +} + +/* ── Endpoint URL bar (method + URL + env toggle + copy page) ────────────── */ +.ep-urlbar { + display: flex; + align-items: center; + gap: 8px; + margin: 16px 0 20px; + padding: 8px 10px; + border: 1px solid var(--lbus-c-divider, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-md); + background: var(--lb-bg-2, #f8fafc); +} +.ep-urlbar-url { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: var(--lb-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); + font-size: 13px; + color: var(--lb-fg-1, #1f2937); + background: none; + border: none; + padding: 0; +} +.ep-urlbar-icon { + flex: none; + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + color: var(--lb-fg-2, #64748b); + background: transparent; + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-sm); + cursor: pointer; + transition: background 0.15s, color 0.15s; +} +.ep-urlbar-icon:hover { + background: var(--lb-bg-2, #f3f5f6); + color: var(--lb-fg-1, #1f2937); +} +.ep-env-seg { + flex: none; + padding: 3px 12px; font-size: 12px; - color: var(--lb-c-text-3, #94a3b8); + white-space: nowrap; + letter-spacing: normal; + color: var(--lb-fg-2, #64748b); + background: transparent; + border: none; + cursor: pointer; +} +.ep-env-seg.is-active { + color: #fff; + background: var(--lb-brand, #16a34a); +} +/* Split "copy page" dropdown */ +.ep-copypage { + flex: none; + position: relative; + display: inline-flex; + align-items: stretch; +} +.ep-copypage-main, +.ep-copypage-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + height: 32px; + font-size: 12px; + white-space: nowrap; + color: var(--lb-fg-1, #1f2937); + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + cursor: pointer; + transition: background 0.15s, color 0.15s; +} +.ep-copypage-main { + padding: 0 10px; + border-radius: var(--ar-radius-sm) 0 0 var(--ar-radius-sm); +} +.ep-copypage-toggle { + padding: 0 7px; + border-left: none; + border-radius: 0 var(--ar-radius-sm) var(--ar-radius-sm) 0; + font-size: 11px; +} +.ep-copypage-main:hover, +.ep-copypage-toggle:hover { + background: var(--lb-bg-2, #f3f5f6); +} +.ep-copypage-ic { + display: inline-flex; + align-items: center; + color: var(--lb-fg-2, #64748b); +} +/* Chevron on the split-button toggle matches the <select> chevron (muted). */ +.ep-copypage-toggle { + color: var(--lb-fg-2, #64748b); + justify-content: center; +} +.ep-copypage-menu { + position: absolute; + top: calc(100% + 6px); + right: 0; + z-index: 20; + min-width: 208px; + padding: 6px; + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-md); + box-shadow: var(--lb-shadow-menu, 0 2px 9px rgba(43, 62, 92, 0.14)); + display: flex; + flex-direction: column; + gap: 2px; +} +.ep-copypage-item { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 8px 10px; + font-size: 13px; + color: var(--lb-fg-1, #1f2937); + text-decoration: none; + background: transparent; + border: none; + border-radius: var(--ar-radius-md); + cursor: pointer; + text-align: left; +} +.ep-copypage-item:hover { + background: var(--lb-bg-2, #f3f5f6); +} +.ep-copypage-item svg { + flex: none; + color: var(--lb-fg-2, #64748b); +} +:root[data-mode='dark'] .ep-urlbar { + background: #161b22; +} +:root[data-mode='dark'] .ep-urlbar-env, +:root[data-mode='dark'] .ep-copypage-main, +:root[data-mode='dark'] .ep-copypage-toggle, +:root[data-mode='dark'] .ep-copypage-menu { + background: #0d1117; + color: var(--lb-fg-1, #e6edf3); +} +:root[data-mode='dark'] .ep-copypage-item { + color: #e6edf3; +} +:root[data-mode='dark'] .ep-copypage-main:hover, +:root[data-mode='dark'] .ep-copypage-toggle:hover, +:root[data-mode='dark'] .ep-copypage-item:hover { + background: #161b22; +} + +/* WebSocket command detail view */ +.ws-badges { + display: flex; + align-items: center; + gap: 8px; + margin: 4px 0 20px; +} +.ws-cmd, +.ws-dir { + font-size: 12px; + padding: 2px 8px; + border-radius: var(--ar-radius-md); + font-family: var(--lb-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); + color: var(--lb-fg-2, #64748b); + background: var(--lb-bg-2, #f1f5f9); +} +.ws-response { margin: 0; + padding: 12px 14px; + border-radius: var(--ar-radius-md); + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, #e6e7e8); + color: var(--lb-fg-1, #1f2937); + font-size: 12.5px; + line-height: 1.6; + overflow-x: auto; +} +:root[data-mode='dark'] .ws-response { + background: #0d1117; + color: #e6edf3; +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode='light']) .ws-response { + background: #0d1117; + color: #e6edf3; + } +} +:root[data-mode='dark'] .ws-cmd, +:root[data-mode='dark'] .ws-dir { + background: #161b22; } -.qp-link { - color: var(--lb-c-brand, #2563eb); +/* Authorization heading row: title on the left, auth-method dropdown on the right. */ +.api-auth-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} +.api-auth-head h2 { + margin: 0; +} +/* Custom dropdown (button + popover menu) — replaces native <select> so the + option list matches the docs styling. Width/placement via per-context class. */ +.ar-dropdown { + position: relative; + flex: none; +} +.ar-dropdown-btn { + display: inline-flex; + align-items: center; + justify-content: space-between; + gap: 8px; + width: 100%; + height: 32px; + padding: 0 10px; + font-size: 13px; + white-space: nowrap; + color: var(--lb-fg-1, #1f2937); + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-sm); + cursor: pointer; +} +.ar-dropdown-value { + overflow: hidden; + text-overflow: ellipsis; +} +.ar-dropdown-btn svg { + flex: none; + color: var(--lb-fg-2, #64748b); +} +.ar-dropdown-btn:hover, +.ar-dropdown-btn[aria-expanded='true'] { + background: var(--lb-bg-2, #f3f5f6); +} +.ar-dropdown-btn:focus-visible { + box-shadow: var(--lb-focus-ring); + outline: none; +} +/* Positioned via inline fixed coords (portal to <body>) so it is never clipped. */ +.ar-dropdown-menu { + z-index: 1080; + max-height: min(320px, 60vh); + overflow-y: auto; + padding: 4px; + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, #e6e7e8); + border-radius: var(--ar-radius-md); + box-shadow: var(--lb-shadow-menu, 0 2px 9px rgba(43, 62, 92, 0.14)); + display: flex; + flex-direction: column; + gap: 2px; +} +.ar-dropdown-option { + width: 100%; + padding: 7px 10px; + font-size: 13px; + text-align: left; + white-space: nowrap; + color: var(--lb-fg-1, #1f2937); + background: transparent; + border: none; + border-radius: var(--ar-radius-sm); + cursor: pointer; +} +.ar-dropdown-option:hover { + background: var(--lb-bg-2, #f3f5f6); +} +.ar-dropdown-option.is-active { + color: var(--lb-brand, #00b8b8); + font-weight: 600; +} +.ar-dropdown-option:focus-visible { + box-shadow: var(--lb-focus-ring); + outline: none; +} +:root[data-mode='dark'] .ar-dropdown-btn, +:root[data-mode='dark'] .ar-dropdown-menu { + background: #161b22; + border-color: #2c3039; + color: #e6edf3; +} +/* Per-context sizing/placement — the value dropdowns share one width. */ +.ar-dropdown.api-auth-select, +.ar-dropdown.ar-lang { + width: 130px; +} +.ar-dropdown.ar-lang { + margin: 6px 8px; +} +.ar-dropdown.ar-auth-block { + width: 100%; + margin: 4px 0 10px; +} +.api-auth-note { + margin: 0 0 10px; + font-size: 13px; + color: var(--lb-fg-2, #64748b); +} +.api-auth-link { + color: var(--lb-brand, #16a34a); + text-decoration: none; + font-weight: 500; +} +.api-auth-link:hover { text-decoration: underline; } -/* ── Page content area ───────────────────────────────────────────────────── */ +/* ── Right rail: Request + Response panels ───────────────────────────────── */ +.api-rail { + position: sticky; + top: calc(var(--lb-nav-height, 64px) + 24px); + display: flex; + flex-direction: column; + gap: 16px; + max-height: calc(100vh - var(--lb-nav-height, 64px) - 40px); + overflow-y: auto; +} +.api-rail-card { + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-md); + background: var(--lb-bg-2, #f3f5f6); + padding: 12px; + min-width: 0; +} +/* Keep code from widening the 22rem rail — it scrolls inside its own box. */ +.api-rail-card .code-tabs, +.api-rail-card .code-tabs-body, +.api-rail-card .code-pre { + min-width: 0; + max-width: 100%; +} +.api-rail-card .code-pre { + overflow-x: auto; +} +/* Call example (SDK snippet) wraps instead of scrolling horizontally; the JSON + response example (.ws-response) keeps its own scroll. */ +.api-rail-card .code-pre:not(.ws-response) { + white-space: pre-wrap; + overflow-wrap: anywhere; + overflow-x: visible; +} +/* Response JSON: theme-aware editor box — light on the site's secondary + surface in light mode, GitHub-dark in dark mode. */ +[data-lbus-component='response-panel'] .code-pre { + margin: 0; + padding: 12px 14px; + border-radius: var(--ar-radius-md); + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, #e6e7e8); + color: var(--lb-fg-1, #1f2937); + font-size: 12.5px; + line-height: 1.6; + max-height: 420px; + overflow: auto; +} +:root[data-mode='dark'] [data-lbus-component='response-panel'] .code-pre { + background: #0d1117; + color: #e6edf3; +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode='light']) [data-lbus-component='response-panel'] .code-pre { + background: #0d1117; + color: #e6edf3; + } +} +.api-rail-head { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 10px; +} +.api-rail-title { + font-size: 14px; + font-weight: 600; + color: var(--lb-fg-1, #1f2937); +} +.api-rail-authselect { + margin-left: auto; + padding: 3px 24px 3px 10px; + font-size: 12px; + white-space: nowrap; + color: var(--lb-fg-1, #1f2937); + background: var(--lb-bg-1, #fff) + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='3'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") + no-repeat right 8px center; + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-sm); + cursor: pointer; + appearance: none; + -webkit-appearance: none; +} +.api-rail-authselect:hover { + background-color: var(--lb-bg-2, #f3f5f6); +} +.api-rail-oauthform { + display: flex; + flex-direction: column; + gap: 6px; + padding: 12px; + border: 1px solid var(--lbus-c-divider, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-md); + background: var(--lb-bg-1, #fff); +} +.api-rail-oauthlabel { + font-size: 12px; + font-weight: 500; + color: var(--lb-fg-2, #64748b); +} +.api-rail-tokenbtn { + font-size: 12px; + white-space: nowrap; + color: var(--lb-fg-2, #64748b); + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-sm); + padding: 3px 8px; + cursor: pointer; +} +.api-rail-tokenform { + margin-bottom: 10px; +} +/* The TryIt AuthorizationForm/ParametersForm reference legacy `--vp-c-*` tokens + that don't exist on this site, so its inputs fall back to raw browser styling. + Re-style them here with the site's `--lb-*` tokens for a coherent look. */ +.api-rail .tryit-base-form { + border: 1px solid var(--lbus-c-divider, rgba(0, 0, 0, 0.08)) !important; + background: var(--lb-bg-1, #fff); +} +.api-rail .tryit-form-header { + padding: 10px 12px; +} +.api-rail .tryit-form-header h2 { + font-size: 13px; + color: var(--lb-fg-1, #1f2937); +} +.api-rail .tryit-input { + width: 100%; + padding: 6px 9px; + border-radius: var(--ar-radius-md); + border: 1px solid var(--lbus-c-divider, rgba(0, 0, 0, 0.12)); + background: var(--lb-bg-1, #fff); + color: var(--lb-fg-1, #1f2937); + font-size: 12.5px; + line-height: 1.5; + outline: none; + transition: border-color 0.15s; +} +.api-rail .tryit-input:focus { + border-color: var(--lb-brand, #16a34a); +} +.api-rail .tryit-input::placeholder { + color: var(--lb-fg-3, #9ca3af); +} +.api-rail .tryit-form-content { + max-height: 1000px; + overflow: hidden; + transition: max-height 0.3s ease; +} +.api-rail .tryit-form-content.tryit-collapsed { + max-height: 0; +} +:root[data-mode='dark'] .api-rail .tryit-base-form, +:root[data-mode='dark'] .api-rail .tryit-input { + background: #0d1117; + color: #e6edf3; +} +.api-rail-params { + margin-top: 10px; +} +.api-rail-send { + margin-top: 12px; + width: 100%; + padding: 8px; + font-size: 13px; + font-weight: 600; + white-space: nowrap; + letter-spacing: normal; + color: var(--lb-fg-invert, #fff); + background: var(--lb-fg-1, #0a0e19); + border: none; + border-radius: var(--ar-radius-md); + cursor: pointer; +} +.api-rail-send:not(:disabled):hover { + background: color-mix(in srgb, var(--lb-fg-1, #0a0e19) 86%, #fff); +} +.api-rail-send:disabled { + opacity: 0.6; + cursor: default; +} +.api-rail-tabs { + display: flex; + gap: 4px; + margin-bottom: 8px; + flex-wrap: wrap; +} +.api-rail-tab { + padding: 3px 10px; + font-size: 12px; + white-space: nowrap; + letter-spacing: normal; + font-family: ui-monospace, Menlo, monospace; + color: var(--lb-fg-2, #64748b); + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, rgba(0, 0, 0, 0.08)); + border-radius: var(--ar-radius-sm); + cursor: pointer; +} +/* One selected treatment across the whole reference: brand fill + white text, + matching the env (生产/测试) segmented control. The status code itself + (200 vs 4xx) carries the success/error meaning, so no color-only cue is lost. */ +.api-rail-tab.is-active { + color: var(--lb-fg-invert, #fff); + background: var(--lb-fg-1, #0a0e19); + border-color: var(--lb-fg-1, #0a0e19); +} +.api-rail-live { + font-size: 11px; + padding: 1px 7px; + border-radius: var(--ar-radius-pill); + background: rgba(22, 163, 74, 0.15); + color: #15803d; +} +:root[data-mode='dark'] .api-rail-card { + background: #0d1117; +} +:root[data-mode='dark'] .api-rail-tokenbtn, +:root[data-mode='dark'] .api-rail-authselect, +:root[data-mode='dark'] .api-rail-oauthform, +:root[data-mode='dark'] .api-rail-tab { + background: #161b22; +} +:root[data-mode='dark'] .api-rail-authselect { + color: #e6edf3; + /* restore the chevron the `background:#161b22` shorthand above wiped out, + with a light-enough stroke for the dark surface */ + background: #161b22 + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%238b949e' stroke-width='3'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") + no-repeat right 8px center; +} +/* The API reference is a 3-column console. Instead of the docs pages' asymmetric + header-aligned gutter (which left a big empty left margin), center the whole + app block (nav + content + rail) at a fixed max width with equal side gutters, + and let the content region fill that centered width. */ +@media (min-width: 1024px) { + [data-lbus-component='api-reference'].docs-layout { + max-width: none; + padding-left: 0; + } +} +[data-lbus-component='api-reference'] .docs-inner { + max-width: none; + margin: 0; +} -.api-page-content { - max-width: 720px; +/* three-column widths — only when the right rail is present. Full-bleed: the + center fills, the rail takes a generous fixed width so nothing looks empty. */ +[data-lbus-component='api-reference'] .docs-main.has-rail { + display: grid; + grid-template-columns: minmax(0, 1fr) 30rem; + gap: 2rem; + align-items: start; } -/* ── Responsive ──────────────────────────────────────────────────────────── */ +/* Standalone content pages (no rail) have no TOC column — the host `.docs-main` + otherwise reserves a 14rem TOC track, squeezing the article. Give the column + the full track so the centered content below can use it. */ +[data-lbus-component='api-reference'] .docs-main:not(.has-rail) { + grid-template-columns: minmax(0, 1fr); +} -@media (max-width: 1024px) { - .api-reference-page { - grid-template-columns: 240px 1fr; - } +/* Content column: capped to a comfortable measure and horizontally centered + within its track — on every page (standalone content pages and the 1fr cell + beside a right rail). */ +[data-lbus-component='api-reference'] .docs-content { + max-width: 52rem; + margin-inline: auto; +} - .api-main--split { - grid-template-columns: 1fr; - } - .code-panel { - position: static; - } +/* Drawer chrome is hidden while the rail sits beside the content (wide screens). */ +.ep-urlbar-tryit, +.api-rail-scrim, +.api-rail-close { + display: none; } -@media (max-width: 768px) { - .api-reference-page { +/* Narrow screens: the content goes full-width and the request rail becomes an + on-demand right-side drawer opened from the "Try it" button below the URL. */ +@media (max-width: 1600px) { + [data-lbus-component='api-reference'] .docs-main.has-rail { grid-template-columns: 1fr; } - - .api-sidebar { - position: static; - height: auto; - border-right: none; - border-bottom: 1px solid var(--lb-c-divider, #e2e8f0); + /* "Try it" trigger sits inline in the URL bar, right after the copy button. */ + .ep-urlbar-tryit { + flex: none; + display: inline-flex; + align-items: center; + height: 28px; + padding: 0 14px; + font-size: 12.5px; + font-weight: 600; + white-space: nowrap; + color: var(--lb-fg-invert, #fff); + background: var(--lb-fg-1, #0a0e19); + border: none; + border-radius: var(--ar-radius-pill); + cursor: pointer; + transition: background 0.15s; } - - .api-main, - .api-intro { - padding: 24px 20px; + .ep-urlbar-tryit:hover { + background: color-mix(in srgb, var(--lb-fg-1, #0a0e19) 86%, #fff); } - - .intro-cards { - grid-template-columns: 1fr; + .ep-urlbar-tryit:focus-visible { + box-shadow: var(--lb-focus-ring); + outline: none; + } + .api-rail { + position: fixed; + top: 0; + right: 0; + height: 100vh; + width: min(640px, 96vw); + max-width: 96vw; + max-height: none; + overflow-y: auto; + padding: 16px 18px 32px; + background: var(--lb-bg-1, #fff); + border-left: 1px solid var(--lb-stroke, #e6e7e8); + box-shadow: -12px 0 40px rgba(10, 14, 25, 0.14); + transform: translateX(100%); + transition: transform 0.24s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 1050; + } + .api-rail.open { + transform: none; + } + :root[data-mode='dark'] .api-rail { + background: #0d1117; + border-left-color: #2c3039; + } + .api-rail-scrim { + display: block; + position: fixed; + inset: 0; + background: rgba(10, 14, 25, 0.35); + opacity: 0; + pointer-events: none; + transition: opacity 0.2s ease; + z-index: 1040; + } + .api-rail-scrim.open { + opacity: 1; + pointer-events: auto; + } + .api-rail-close { + display: inline-flex; + align-items: center; + justify-content: center; + align-self: flex-end; + width: 30px; + height: 30px; + border: 1px solid var(--lb-stroke, #e6e7e8); + border-radius: var(--ar-radius-sm); + background: var(--lb-bg-1, #fff); + color: var(--lb-fg-2, #6c6e75); + cursor: pointer; } } -/* ── Dark mode ───────────────────────────────────────────────────────────── */ - -:root[data-theme="dark"] .code-card { - background: #1e2a3a; - border-color: rgba(255,255,255,0.08); -} -@media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) .code-card { - background: #1e2a3a; - border-color: rgba(255,255,255,0.08); +/* ── Reduced motion ────────────────────────────────────────────────────────── + Disable decorative transitions/animations for users who prefer reduced + motion. Focus rings and layout are unaffected. */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + transition: none !important; + animation: none !important; } } -:root[data-theme="dark"] .card-header { - background: #16202d; +/* Endpoint title row: heading owns the page-level "copy page" action. */ +.ep-titlebar { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; } -@media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) .card-header { - background: #16202d; - } +.ep-titlebar .ep-title { + margin-bottom: 0; +} +.ep-titlebar .ep-copypage { + flex: none; + margin-top: 6px; +} +/* Settings button sits on the right of the request panel head. */ +.api-rail-head .api-rail-iconbtn { + margin-left: auto; +} +/* Auth-method select, shown full-width inside the collapsible settings panel. */ +.api-rail-authselect--block { + display: block; + width: 100%; + margin: 4px 0 10px; +} +/* Settings toggle in the request-panel head — icon-only ghost button. */ +.api-rail-iconbtn { + flex: none; + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + color: var(--lb-fg-2, #6c6e75); + background: var(--lb-bg-1, #fff); + border: 1px solid var(--lb-stroke, #e6e7e8); + border-radius: var(--ar-radius-sm); + cursor: pointer; + transition: background 0.15s, color 0.15s; +} +.api-rail-iconbtn:hover, +.api-rail-iconbtn[aria-expanded='true'] { + background: var(--lb-bg-2, #f3f5f6); + color: var(--lb-fg-1, #0a0e19); +} +.api-rail-iconbtn[aria-expanded='true'] { + border-color: var(--lb-fg-3, #a9abae); +} +.api-rail-iconbtn:focus-visible { + box-shadow: var(--lb-focus-ring); + outline: none; +} +:root[data-mode='dark'] .api-rail-iconbtn { + background: #161b22; + border-color: #2c3039; + color: #9d9fa3; } diff --git a/packages/api-reference/src/callout.css b/packages/api-reference/src/callout.css new file mode 100644 index 000000000..6ef9f28cb --- /dev/null +++ b/packages/api-reference/src/callout.css @@ -0,0 +1,172 @@ +/** + * callout.css — VitePress-style `:::` container directives (remarkCallout emits + * `.callout.callout-<type>` with a `.callout-title` paragraph). + * + * Styled to match the legacy site's `<TipContainer>` (packages/ui/src/ + * TipContainer.tsx), which the old site renders for these blocks: a full 1px + * border + 8px radius + faint tinted bg, an icon before the title, and the + * whole block's text in the accent colour. Colours/icons are copied verbatim + * from that component so `:::` blocks and any direct <TipContainer> usage look + * identical. Palette is the Tailwind semantic set (fixed across light/dark, as + * in the component) — NOT the --lb-* tokens. + */ + +/* ── Base ─────────────────────────────────────────────── */ +.callout { + margin: 0.25rem 0; + padding: 1rem; + border: 1px solid; + border-radius: 0.5rem; + font-size: 0.875rem; + line-height: 1.625; +} + +.callout > *:last-child { + margin-bottom: 0; +} + +.callout > *:first-child { + margin-top: 0; +} + +/* ── Title row (icon + label) ─────────────────────────── */ +.callout-title { + display: flex; + align-items: center; + gap: 0.5rem; + margin: 0 0 0.5rem; + font-size: 0.875rem; + font-weight: 600; + line-height: 1.4; +} + +.callout-title::before { + content: ''; + width: 16px; + height: 16px; + flex-shrink: 0; + background: var(--callout-icon) center / contain no-repeat; +} + +/* ── Variants: border + bg + text colour, and the accent icon ───────────── + Text colour is set on the whole block so body copy inherits the accent + (matching legacy); inline code keeps its own neutral chip styling. */ +.callout-tip { + --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%233b82f6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5'/%3E%3Cpath d='M9 18h6'/%3E%3Cpath d='M10 22h4'/%3E%3C/svg%3E"); + border-color: rgba(59, 130, 246, 0.3); + background: rgba(59, 130, 246, 0.05); + color: #1d4ed8; +} + +.callout-warning { + --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23eab308' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E"); + border-color: rgba(234, 179, 8, 0.3); + background: rgba(234, 179, 8, 0.05); + color: #a16207; +} + +.callout-danger { + --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23ef4444' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m15 9-6 6'/%3E%3Cpath d='m9 9 6 6'/%3E%3C/svg%3E"); + border-color: rgba(239, 68, 68, 0.3); + background: rgba(239, 68, 68, 0.05); + color: #b91c1c; +} + +.callout-success { + --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2322c55e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E"); + border-color: rgba(34, 197, 94, 0.3); + background: rgba(34, 197, 94, 0.05); + color: #15803d; +} + +.callout-info, +.callout-note { + --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2306b6d4' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4'/%3E%3Cpath d='M12 8h.01'/%3E%3C/svg%3E"); + border-color: rgba(6, 182, 212, 0.3); + background: rgba(6, 182, 212, 0.05); + color: #0e7490; +} + +.callout-caution { + --callout-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f97316' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E"); + border-color: rgba(249, 115, 22, 0.3); + background: rgba(249, 115, 22, 0.05); + color: #c2410c; +} + +/* ── Nested code blocks keep readable bg ─────────────── */ +.callout pre { + background: color-mix(in oklch, currentcolor 4%, transparent); +} + +/* ── Dark mode ──────────────────────────────────────────── + The light values above (Tailwind semantic palette) are the fallback. + On dark pages route each variant's bg/border/text through the canonical + --lb-c-* tokens (with literal fallbacks so these files still render if the + token layer loads later or not at all). Inline SVG icons keep their light + accent strokes, which read fine on the dark tinted backgrounds. */ +:root[data-mode="dark"] .callout-tip { + border-color: var(--lb-c-info-border, rgba(96, 165, 250, 0.4)); + background: var(--lb-c-info-bg, rgba(59, 130, 246, 0.15)); + color: var(--lb-c-info-fg, #93c5fd); +} + +:root[data-mode="dark"] .callout-info, +:root[data-mode="dark"] .callout-note { + border-color: var(--lb-c-info-border, rgba(34, 211, 238, 0.4)); + background: var(--lb-c-info-bg, rgba(6, 182, 212, 0.15)); + color: var(--lb-c-info-fg, #67e8f9); +} + +:root[data-mode="dark"] .callout-warning, +:root[data-mode="dark"] .callout-caution { + border-color: var(--lb-c-warning-border, rgba(250, 204, 21, 0.4)); + background: var(--lb-c-warning-bg, rgba(234, 179, 8, 0.15)); + color: var(--lb-c-warning-fg, #fde047); +} + +:root[data-mode="dark"] .callout-danger { + border-color: var(--lb-c-danger-border, rgba(248, 113, 113, 0.4)); + background: var(--lb-c-danger-bg, rgba(239, 68, 68, 0.15)); + color: var(--lb-c-danger-fg, #fca5a5); +} + +:root[data-mode="dark"] .callout-success { + border-color: var(--lb-c-success-border, rgba(74, 222, 128, 0.4)); + background: var(--lb-c-success-bg, rgba(34, 197, 94, 0.15)); + color: var(--lb-c-success-fg, #86efac); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-mode="light"]) .callout-tip { + border-color: var(--lb-c-info-border, rgba(96, 165, 250, 0.4)); + background: var(--lb-c-info-bg, rgba(59, 130, 246, 0.15)); + color: var(--lb-c-info-fg, #93c5fd); + } + + :root:not([data-mode="light"]) .callout-info, + :root:not([data-mode="light"]) .callout-note { + border-color: var(--lb-c-info-border, rgba(34, 211, 238, 0.4)); + background: var(--lb-c-info-bg, rgba(6, 182, 212, 0.15)); + color: var(--lb-c-info-fg, #67e8f9); + } + + :root:not([data-mode="light"]) .callout-warning, + :root:not([data-mode="light"]) .callout-caution { + border-color: var(--lb-c-warning-border, rgba(250, 204, 21, 0.4)); + background: var(--lb-c-warning-bg, rgba(234, 179, 8, 0.15)); + color: var(--lb-c-warning-fg, #fde047); + } + + :root:not([data-mode="light"]) .callout-danger { + border-color: var(--lb-c-danger-border, rgba(248, 113, 113, 0.4)); + background: var(--lb-c-danger-bg, rgba(239, 68, 68, 0.15)); + color: var(--lb-c-danger-fg, #fca5a5); + } + + :root:not([data-mode="light"]) .callout-success { + border-color: var(--lb-c-success-border, rgba(74, 222, 128, 0.4)); + background: var(--lb-c-success-bg, rgba(34, 197, 94, 0.15)); + color: var(--lb-c-success-fg, #86efac); + } +} diff --git a/packages/api-reference/src/docs-order.generated.ts b/packages/api-reference/src/docs-order.generated.ts new file mode 100644 index 000000000..c93986870 --- /dev/null +++ b/packages/api-reference/src/docs-order.generated.ts @@ -0,0 +1,103 @@ +// AUTO-GENERATED from docs/{lang}/docs sidebar_position. Maps an endpoint +// operationId / ws command id to its docs page position. Items without a docs +// page are absent (they sort after documented items). +export const DOCS_ORDER: Record<string, number> = { + "us_crypto_overview": 10, + "ws-static": 1, + "ws-quote": 2, + "ws-pull-depth": 5, + "ws-pull-brokers": 6, + "ws-broker-ids": 7, + "ws-pull-trade": 8, + "ws-intraday": 9, + "ws-pull-candlestick": 20, + "ws-history-candlestick": 10, + "option_volume": 26, + "option_volume_daily": 27, + "ws-option-quote": 3, + "ws-optionchain-date": 11, + "ws-calc-index": 19, + "ws-subscription": 4, + "ws-subscribe": 2, + "ws-unsubscribe": 3, + "ws-push-quote": 5, + "ws-push-depth": 6, + "ws-push-trade": 8, + "ws-push-candlestick": 9, + "institution_rating_views": 23, + "financial_report": 1, + "financial_report_snapshot": 26, + "consensus": 14, + "forecast_eps": 13, + "valuation_history": 15, + "valuation_comparison": 29, + "industry_valuation": 16, + "industry_valuation_dist": 17, + "industry_rank": 24, + "industry_peers": 25, + "company_profile": 5, + "executives": 6, + "corporate_actions": 9, + "invest_relation": 18, + "operating": 19, + "buyback": 20, + "dividends": 3, + "dividend_detail": 12, + "shareholders": 7, + "shareholder_top": 27, + "shareholder_detail": 28, + "fund_holdings": 8, + "institution_rating": 10, + "institution_rating_detail": 11, + "business_segments": 21, + "business_segments_history": 22, + "us_company_overview": 30, + "us_valuation_overview": 31, + "us_financial_overview": 32, + "us_financial_statement": 33, + "us_key_financial_metrics": 34, + "us_analyst_consensus": 35, + "us_etf_dividend_info": 36, + "us_company_dividends": 37, + "us_etf_files": 38, + "ah_premium": 3, + "ah_premium_intraday": 9, + "broker_positions": 2, + "broker_holding_daily": 8, + "broker_holding_detail": 7, + "index_components": 999, + "trading_stats": 4, + "top_movers": 7, + "market_status": 1, + "rank_categories": 8, + "rank_list": 9, + "unusual_items": 5, + "market_temperature": 2, + "create_topic": 4, + "create_topic_reply": 7, + "topic_detail": 5, + "create_sharelist": 2, + "sharelist_detail": 5, + "delete_sharelist": 4, + "submit_multileg": 8, + "us_query_orders": 10, + "today_orders": 2, + "history_orders": 3, + "us_order_detail": 11, + "all_executions": 3, + "today_executions": 2, + "history_executions": 1, + "cash_flow": 999, + "margin_ratio": 999, + "us_asset_overview": 10, + "us_realized_pl": 11, + "profit_analysis_summary": 2, + "profit_analysis_by_market": 4, + "profit_analysis_detail": 3, + "profit_analysis_flows": 5, + "list_alerts": 1, + "create_alert": 2, + "delete_alert": 4, + "dca_history": 5, + "dca_stats": 8 +} diff --git a/packages/api-reference/src/docs-order.ts b/packages/api-reference/src/docs-order.ts new file mode 100644 index 000000000..d0997285a --- /dev/null +++ b/packages/api-reference/src/docs-order.ts @@ -0,0 +1,96 @@ +// Leaf order within a subgroup, keyed by operationId / ws command id → the docs +// guide page sidebar_position. `docs-order.generated.ts` is the slug-matched +// auto layer; OVERRIDES below hand-map the items whose openapi id doesn't match +// the docs filename (submit_order↔submit, dca_create↔create_dca, grid_*↔*, +// list_topics↔topics, ai_workspaces↔workspaces, valuation↔valuations, …). +// Items with no docs page at all (signals, security_facts, list_securities, …) +// stay absent and sort after the documented leaves. +import { DOCS_ORDER as AUTO } from './docs-order.generated' + +const OVERRIDES: Record<string, number> = { + // Quote / Options + 'ws-optionchain-strike': 12, + // Quote / Warrants (own subgroup, docs quote/warrants positions) + 'ws-warrant-quote': 4, + 'ws-issuers': 13, + 'ws-warrant-filter': 14, + // News & Contents / News + list_news: 1, + // Trade / Assets (docs positions the account/stock/fund pages together) + account_balance: 999, + stock_positions: 999, + fund_positions: 999, + // Quote / Analytics + 'ws-capital-flow': 17, + 'ws-capital-dist': 18, + list_filings: 20.5, + short_positions_hk: 25, + short_positions_us: 25.1, + short_trades_hk: 27, + short_trades_us: 27.1, + // Quote / Watchlist + create_watchlist_group: 2, + update_watchlist_group: 4, + watchlist_pinned: 5, + // Quote / Subscribe + 'ws-push-brokers': 7, + // Fundamental + valuation: 4, + 'macrodata_indicator': 20, + macrodata: 21, + // Market / Market Status + list_market_temperature: 3, + // News & Contents / Topics + list_topics: 2, + list_my_topics: 3, + // News & Contents / Sharelist + list_sharelists: 1, + popular_sharelists: 6, + sharelist_add_securities: 7, + sharelist_remove_securities: 8, + sharelist_sort_securities: 9, + // Trade / Order + submit_order: 1, + replace_order: 5, + estimate_max_purchase: 7, + // Trade / Grid Trading + grid_symbol_info: 0.5, + grid_submit: 1, + grid_replace: 2, + grid_list_by_ids: 4, + grid_detail: 5, + grid_trigger_history: 6, + grid_cancel: 7, + grid_suspend: 8, + grid_restart: 9, + // Trade / Notification (one docs page, keep the 3 commands adjacent) + 'ws-trade-sub': 6, + 'ws-trade-unsub': 6.1, + 'ws-trade-notify': 6.2, + // Account / Portfolio + exchange_rate: 1, + // Account / DCA + dca_list: 1, + dca_create: 2, + dca_update: 3, + dca_toggle: 5, + dca_check_support: 9, + dca_calc_date: 10, + dca_set_reminder: 11, + // AI Agent / Workspace + ai_workspaces: 1, + ai_workspace_agents: 2, + // AI Agent / Conversation + ai_conversation: 2, + ai_continue: 3, +} + +// Force certain leaves to the very end of their subgroup, after even the +// undocumented items (which fall back to LEAF_FALLBACK). +export const LEAF_FALLBACK = 1e6 +const TRAIL: Record<string, number> = { + us_crypto_overview: 1e7, +} + +export const DOCS_ORDER: Record<string, number> = { ...AUTO, ...OVERRIDES, ...TRAIL } + diff --git a/packages/api-reference/src/index.ts b/packages/api-reference/src/index.ts index 7a5d7c40b..3b4248833 100644 --- a/packages/api-reference/src/index.ts +++ b/packages/api-reference/src/index.ts @@ -1 +1,8 @@ export { ApiReference } from './ApiReference' +export { + referenceMarkdown, + endpointMarkdown, + endpointMarkdownById, + endpointList, +} from './openapi-markdown' +export { parseSpec, pickLocale, epId } from './openapi-loader' diff --git a/packages/api-reference/src/openapi-loader.test.ts b/packages/api-reference/src/openapi-loader.test.ts new file mode 100644 index 000000000..9dfa00ac9 --- /dev/null +++ b/packages/api-reference/src/openapi-loader.test.ts @@ -0,0 +1,72 @@ +import { describe, it, expect } from 'vitest' +import { pickLocale, parseSpec } from './openapi-loader' + +describe('pickLocale', () => { + it('en returns english', () => { + expect(pickLocale('E', 'C', 'H', 'en')).toBe('E') + }) + it('zh-CN returns simplified, falls back to en', () => { + expect(pickLocale('E', 'C', 'H', 'zh-CN')).toBe('C') + expect(pickLocale('E', undefined, 'H', 'zh-CN')).toBe('E') + }) + it('zh-HK prefers traditional, falls back to simplified then en', () => { + expect(pickLocale('E', 'C', 'H', 'zh-HK')).toBe('H') + expect(pickLocale('E', 'C', undefined, 'zh-HK')).toBe('C') + expect(pickLocale('E', undefined, undefined, 'zh-HK')).toBe('E') + }) +}) + +describe('parseSpec zh-HK tag/page fields', () => { + const yaml = ` +openapi: 3.0.3 +info: { title: t, version: '1' } +tags: + - name: Quote + x-name-zh: 行情 + x-name-zh-hk: 行情(繁) +x-pages: + - id: intro + title: Intro + x-title-zh: 介绍 + x-title-zh-hk: 介紹 + content: hi + x-content-zh: 你好 + x-content-zh-hk: 你好(繁) +paths: + /v1/x: + get: + operationId: get_x + summary: Get X + tags: [Quote] +` + it('exposes nameZhHk on groups', () => { + const { groups } = parseSpec(yaml) + expect(groups.find((g) => g.name === 'Quote')?.nameZhHk).toBe('行情(繁)') + }) + it('exposes titleZhHk / contentZhHk on pages', () => { + const { pages } = parseSpec(yaml) + expect(pages[0].titleZhHk).toBe('介紹') + expect(pages[0].contentZhHk).toBe('你好(繁)') + }) +}) + +describe('parseSpec websocket', () => { + const yaml = ` +openapi: 3.0.3 +info: { title: t, version: '1' } +tags: + - name: Realtime (WebSocket) +paths: + quote/subscribe: + websocket: + operationId: quote_subscribe + summary: Subscribe Quote + tags: [Realtime (WebSocket)] +` + it('parses websocket op with WEBSOCKET method', () => { + const { groups } = parseSpec(yaml) + const g = groups.find((x) => x.name === 'Realtime (WebSocket)') + expect(g?.endpoints[0].method).toBe('WEBSOCKET') + expect(g?.endpoints[0].operation.operationId).toBe('quote_subscribe') + }) +}) diff --git a/packages/api-reference/src/openapi-loader.ts b/packages/api-reference/src/openapi-loader.ts index 13637f3ef..f5a131bc7 100644 --- a/packages/api-reference/src/openapi-loader.ts +++ b/packages/api-reference/src/openapi-loader.ts @@ -4,6 +4,7 @@ * Ported 1:1 from ApiReference.vue (chunks A + B). */ import { load } from 'js-yaml' +import type { Locale } from '@longbridge/openapi-utils' // ── Types ───────────────────────────────────────────────────────────────────── @@ -13,6 +14,7 @@ export interface Parameter { required?: boolean description?: string 'x-description-zh'?: string + 'x-description-zh-hk'?: string schema?: { type?: string } } @@ -27,10 +29,23 @@ export interface ParamRow { export interface Section { key: string title: string + note?: string params: ParamRow[] fallback?: boolean } +/** A documented HTTP parameter, split by location. */ +export interface XParameter { + name: string + /** Where the parameter goes in the HTTP request. */ + in?: 'path' | 'query' | 'body' + type?: string + required?: boolean + description?: string + 'x-description-zh'?: string + 'x-description-zh-hk'?: string +} + export interface CodeSample { lang: string label: string @@ -41,9 +56,19 @@ export interface Operation { operationId: string summary: string 'x-summary-zh'?: string + 'x-summary-zh-hk'?: string description?: string 'x-description-zh'?: string + 'x-description-zh-hk'?: string 'x-quote-command'?: string + 'x-quote-level'?: string + 'x-quote-market'?: string + 'x-subgroup'?: string + 'x-subgroup-zh'?: string + 'x-subgroup-zh-hk'?: string + 'x-parameters'?: XParameter[] + 'x-response-properties'?: XParameter[] + 'x-request-examples'?: CodeSample[] tags?: string[] parameters?: Parameter[] 'x-codeSamples'?: CodeSample[] @@ -83,15 +108,32 @@ export interface PageItem { id: string title: string titleZh?: string + titleZhHk?: string content: string contentZh?: string + contentZhHk?: string icon?: string + /** Optional multi-language code tabs, injected where `[[SIGNING_TABS]]` appears. */ + codeTabs?: CodeSample[] +} + +export interface SubGroup { + name: string + nameZh?: string + nameZhHk?: string + endpoints: EndpointItem[] + /** WebSocket commands filed under this subgroup, rendered after the endpoints. */ + wsCommands?: WsCommandItem[] } export interface TagGroup { name: string nameZh?: string + nameZhHk?: string + /** Endpoints not assigned to any subgroup (flat groups render these directly). */ endpoints: EndpointItem[] + /** Ordered docs subsections; empty for flat groups. */ + subgroups: SubGroup[] } export interface CodeBlock { @@ -105,6 +147,39 @@ export interface PathSeg { isParam: boolean } +// ── WebSocket commands (x-websocket) ───────────────────────────────────────── + +export interface WsCommandItem { + id: string + name: string + nameZh?: string + nameZhHk?: string + cmd?: number + direction: 'request' | 'push' + description: string + descriptionZh?: string + descriptionZhHk?: string + fields?: XParameter[] + responseFields?: XParameter[] + requestExamples: CodeSample[] + responseExample?: string + /** Topical subgroup this command merges into (matches a REST subgroup name). */ + subgroup?: string + subgroupZh?: string + subgroupZhHk?: string + /** Quote-permission command key (quote-permissions.yaml), for the permission card. */ + quoteCommand?: string +} + +export interface WsGroupData { + name: string + nameZh?: string + nameZhHk?: string + /** HTTP tag this WS group is filed under (so it merges into that tag's nav group). */ + tag?: string + commands: WsCommandItem[] +} + export const PAGE_ICONS: Record<string, string> = { lock: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>`, activity: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>`, @@ -114,10 +189,15 @@ export const PAGE_ICONS: Record<string, string> = { // ── Spec parsing ────────────────────────────────────────────────────────────── -export function parseSpec(rawYaml: string): { groups: TagGroup[]; pages: PageItem[]; serverUrl: string } { +export function parseSpec(rawYaml: string): { + groups: TagGroup[] + pages: PageItem[] + wsGroups: WsGroupData[] + serverUrl: string +} { const parsed = load(rawYaml) as any const serverUrl: string = parsed.servers?.[0]?.url ?? '' - const methods = ['get', 'post', 'put', 'delete', 'patch'] + const methods = ['get', 'post', 'put', 'delete', 'patch', 'websocket'] const byTag: Record<string, EndpointItem[]> = {} for (const [path, pathItem] of Object.entries((parsed.paths ?? {}) as Record<string, any>)) { @@ -134,29 +214,188 @@ export function parseSpec(rawYaml: string): { groups: TagGroup[]; pages: PageIte const specTagObjs: any[] = (parsed.tags ?? []) as any[] const tagZhMap: Record<string, string> = {} + const tagZhHkMap: Record<string, string> = {} + // tag name → ordered subgroup definitions from the tag's `x-subgroups`. + const tagSubOrder: Record<string, Array<{ name: string; nameZh?: string; nameZhHk?: string }>> = {} for (const t of specTagObjs) { if (t['x-name-zh']) tagZhMap[t.name] = t['x-name-zh'] + if (t['x-name-zh-hk']) tagZhHkMap[t.name] = t['x-name-zh-hk'] + if (Array.isArray(t['x-subgroups'])) { + tagSubOrder[t.name] = t['x-subgroups'].map((s: any) => ({ + name: s.name, + nameZh: s['x-name-zh'], + nameZhHk: s['x-name-zh-hk'], + })) + } } const specTags: string[] = specTagObjs.map((x: any) => x.name) - const ordered = [...specTags, ...Object.keys(byTag).filter((x) => !specTags.includes(x))] + const allTags = [...specTags, ...Object.keys(byTag).filter((x) => !specTags.includes(x))] + // Order the top-level groups to follow the docs guide category order + // (docs/{lang}/docs/*/_category_.json positions). Tags not listed keep their + // spec order at the end (stable sort). + const DOCS_TAG_ORDER = [ + 'Quote', + 'Fundamental', + 'Market', + 'News & Contents', + 'Screener', + 'Trade', + 'Account', + 'AI Agent', + ] + const tagRank = (t: string) => { + const i = DOCS_TAG_ORDER.indexOf(t) + return i === -1 ? Number.MAX_SAFE_INTEGER : i + } + const ordered = [...allTags].sort((a, b) => tagRank(a) - tagRank(b)) + + // Partition a tag's endpoints into ordered subgroups (by op x-subgroup) plus + // the leftover flat endpoints (ops without a subgroup). + const buildSubgroups = ( + tag: string, + eps: EndpointItem[] + ): { subgroups: SubGroup[]; flat: EndpointItem[] } => { + const order = tagSubOrder[tag] ?? [] + const flat: EndpointItem[] = [] + const bySub: Record<string, EndpointItem[]> = {} + for (const ep of eps) { + const key = ep.operation['x-subgroup'] + if (key) (bySub[key] ??= []).push(ep) + else flat.push(ep) + } + const seen = new Set<string>() + const subgroups: SubGroup[] = [] + const push = (name: string, nameZh?: string, nameZhHk?: string) => { + if (seen.has(name) || !bySub[name]?.length) return + seen.add(name) + const z = bySub[name][0].operation + subgroups.push({ + name, + nameZh: nameZh ?? z['x-subgroup-zh'], + nameZhHk: nameZhHk ?? z['x-subgroup-zh-hk'], + endpoints: bySub[name], + }) + } + for (const s of order) push(s.name, s.nameZh, s.nameZhHk) + for (const name of Object.keys(bySub)) push(name) // any subgroup not listed in order + return { subgroups, flat } + } const rawPages: any[] = (parsed['x-pages'] ?? []) as any[] const pages: PageItem[] = rawPages.map((p: any) => ({ id: p.id, title: p.title, titleZh: p['x-title-zh'], + titleZhHk: p['x-title-zh-hk'], content: p.content ?? '', contentZh: p['x-content-zh'], + contentZhHk: p['x-content-zh-hk'], icon: p['x-icon'], + codeTabs: p['x-code-tabs'], })) + // Overview is the landing page, so it leads; Authentication and the rest + // follow in their spec order. + const PAGE_ORDER = ['overview', 'authentication'] + const pageRank = (id: string) => { + const i = PAGE_ORDER.indexOf(id) + return i === -1 ? Number.MAX_SAFE_INTEGER : i + } + pages.sort((a, b) => pageRank(a.id) - pageRank(b.id)) + + const mapWsCommand = (c: any): WsCommandItem => ({ + id: c.id, + name: c.name, + nameZh: c['x-name-zh'], + nameZhHk: c['x-name-zh-hk'], + cmd: c.cmd, + direction: c.direction === 'push' ? 'push' : 'request', + description: c.description ?? '', + descriptionZh: c['x-description-zh'], + descriptionZhHk: c['x-description-zh-hk'], + fields: c['x-fields'], + responseFields: c['x-response-fields'], + requestExamples: c['x-request-examples'] ?? [], + responseExample: c['x-response-example'], + subgroup: c['x-subgroup'], + subgroupZh: c['x-subgroup-zh'], + subgroupZhHk: c['x-subgroup-zh-hk'], + quoteCommand: c['x-quote-command'], + }) + + // x-websocket supports either a list of groups (`groups:`) or a single group + // (`commands:` directly). Normalize to an array of groups. + const rawWs = parsed['x-websocket'] as any + let wsGroups: WsGroupData[] = [] + if (rawWs) { + const rawGroups: any[] = rawWs.groups ?? [{ name: rawWs.name, 'x-name-zh': rawWs['x-name-zh'], 'x-name-zh-hk': rawWs['x-name-zh-hk'], commands: rawWs.commands }] + wsGroups = rawGroups + .filter((g) => (g.commands ?? []).length > 0) + .map((g) => ({ + name: g.name ?? 'WebSocket', + nameZh: g['x-name-zh'], + nameZhHk: g['x-name-zh-hk'], + tag: g['x-tag'], + commands: (g.commands ?? []).map(mapWsCommand), + })) + } + + const groups: TagGroup[] = ordered + .filter((x) => byTag[x]) + .map((x) => { + const { subgroups, flat } = buildSubgroups(x, byTag[x]) + return { name: x, nameZh: tagZhMap[x], nameZhHk: tagZhHkMap[x], endpoints: flat, subgroups } + }) + + // Merge topical WebSocket commands into their REST subgroup (same tag + + // matching subgroup name), so pull commands sit next to their REST peers. + // Commands without an x-subgroup stay in their protocol group (Subscription / + // Push / Notification), which keeps rendering as its own nav group. + for (const wg of wsGroups) { + if (!wg.tag) continue + const group = groups.find((g) => g.name === wg.tag) + if (!group) continue + const remaining: WsCommandItem[] = [] + for (const cmd of wg.commands) { + if (!cmd.subgroup) { + remaining.push(cmd) + continue + } + let sub = group.subgroups.find((s) => s.name === cmd.subgroup) + if (!sub) { + sub = { name: cmd.subgroup, nameZh: cmd.subgroupZh, nameZhHk: cmd.subgroupZhHk, endpoints: [] } + group.subgroups.push(sub) + } + ;(sub.wsCommands ??= []).push(cmd) + } + wg.commands = remaining + } + const prunedWsGroups = wsGroups.filter((wg) => wg.commands.length > 0) return { - groups: ordered.filter((x) => byTag[x]).map((x) => ({ name: x, nameZh: tagZhMap[x], endpoints: byTag[x] })), + groups, pages, + wsGroups: prunedWsGroups, serverUrl, } } +/** + * Resolve a localized string with fallback: + * zh-HK → zh-hk ?? zh ?? en + * zh-CN → zh ?? en + * en → en + */ +export function pickLocale( + en: string | undefined, + zh: string | undefined, + zhHk: string | undefined, + locale: Locale +): string { + if (locale === 'zh-HK') return zhHk ?? zh ?? en ?? '' + if (locale === 'zh-CN') return zh ?? en ?? '' + return en ?? '' +} + // ── Helpers ─────────────────────────────────────────────────────────────────── export function splitDescriptionAndCode(text: string): { prose: string; codeBlocks: string[] } { @@ -196,25 +435,39 @@ export function epId(ep: EndpointItem): string { } export function buildCurl(ep: EndpointItem, serverUrl: string): string { + const isBody = ['POST', 'PUT', 'PATCH'].includes(ep.method) + const xp = ep.operation['x-parameters'] + let url = `${serverUrl}${ep.path}` + const body: Record<string, any> = {} + + if (xp?.length) { + // Real HTTP call derived from the documented parameters. + const required = xp.filter((p) => p.required) + if (isBody) { + for (const p of required) body[p.name] = p.type === 'integer' ? 0 : `<${p.name}>` + } else if (required.length) { + url += '?' + required.map((p) => `${p.name}=<${p.name}>`).join('&') + } + } else { + // Fallback: derive the body from the requestBody JSON schema (legacy ops). + const schema = ep.operation.requestBody?.content?.['application/json']?.schema + if (schema?.properties && isBody) { + const required: string[] = schema.required ?? [] + for (const [k, v] of Object.entries(schema.properties as Record<string, any>)) { + if (required.includes(k)) body[k] = (v as any).type === 'integer' ? 0 : `<${k}>` + } + } + } + const lines: string[] = [ `curl --request ${ep.method} \\`, - ` --url '${serverUrl}${ep.path}' \\`, + ` --url '${url}' \\`, ` --header 'Authorization: Bearer <token>'`, ] - const schema = ep.operation.requestBody?.content?.['application/json']?.schema - if (schema?.properties && ['POST', 'PUT', 'PATCH'].includes(ep.method)) { - const required: string[] = schema.required ?? [] - const body: Record<string, any> = {} - for (const [k, v] of Object.entries(schema.properties as Record<string, any>)) { - if (required.includes(k)) { - body[k] = (v as any).type === 'integer' ? 0 : `<${k}>` - } - } - if (Object.keys(body).length) { - lines[lines.length - 1] += ' \\' - lines.push(` --header 'Content-Type: application/json' \\`) - lines.push(` --data '${JSON.stringify(body)}'`) - } + if (Object.keys(body).length) { + lines[lines.length - 1] += ' \\' + lines.push(` --header 'Content-Type: application/json' \\`) + lines.push(` --data '${JSON.stringify(body)}'`) } return lines.join('\n') } @@ -245,3 +498,35 @@ export function buildResponseExample(ep: EndpointItem): string | null { } return null } + +/** + * Canonical gateway error envelopes per HTTP status, observed live against the + * OpenAPI gateway. Applied uniformly to every endpoint's Response panel so each + * status tab shows a real-shaped body without hand-authoring per endpoint. + */ +export const STANDARD_ERROR_EXAMPLES: Record<number, { code: number; message: string }> = { + 400: { code: 400, message: 'request invalid' }, + 401: { code: 401004, message: 'token invalid' }, + 403: { code: 403, message: 'This API is only available to authorized users.' }, + 408: { code: 408, message: 'internal server timeout' }, +} + +export interface ResponseExample { + status: number + body: string +} + +/** + * Per-status response examples for an endpoint's Response panel: + * 200 = the endpoint's real success example (falls back to a bare success + * envelope); 4xx = the standard gateway error envelope for that status. + */ +export function endpointResponseExamples(ep: EndpointItem): ResponseExample[] { + const ok = buildResponseExample(ep) ?? JSON.stringify({ code: 0, message: 'success', data: {} }, null, 2) + const out: ResponseExample[] = [{ status: 200, body: ok }] + for (const status of [400, 401, 403, 408]) { + const e = STANDARD_ERROR_EXAMPLES[status] + out.push({ status, body: JSON.stringify({ code: e.code, message: e.message, data: null }, null, 2) }) + } + return out +} diff --git a/packages/api-reference/src/openapi-markdown.ts b/packages/api-reference/src/openapi-markdown.ts new file mode 100644 index 000000000..6cc2b5fab --- /dev/null +++ b/packages/api-reference/src/openapi-markdown.ts @@ -0,0 +1,235 @@ +/** + * openapi-markdown.ts + * Render openapi.yaml into plain Markdown for AI/LLM consumption — a single + * source used by the `.md` routes (/docs/api.md, /docs/api/<op>.md) and by + * llms.txt / llms-full.txt. No JS execution needed by the consumer. + */ +import type { Locale } from '@longbridge/openapi-utils' +import { load } from 'js-yaml' +import { parseSpec, pickLocale, buildResponseExample, type EndpointItem, type WsCommandItem, type XParameter } from './openapi-loader' +import rawQuotePermissions from '../../../quote-permissions.yaml?raw' + +// ── Quote-permission callout (mirrors the <QuotePermission> MDX component) ───── + +interface QPLocaleString { + en?: string + 'zh-CN'?: string + 'zh-HK'?: string +} +interface QPData { + ui: { permission_title: QPLocaleString; separate_note: QPLocaleString; market_labels?: Record<string, QPLocaleString> } + levels: Record<string, { label: QPLocaleString; description: QPLocaleString }> + commands?: Record<string, { level: string; market?: string; description?: QPLocaleString }> +} +let _qp: QPData | null = null +const qpData = (): QPData => (_qp ??= load(rawQuotePermissions) as QPData) + +const qpLocaleKey = (locale: Locale): keyof QPLocaleString => + locale === 'zh-CN' ? 'zh-CN' : locale === 'zh-HK' ? 'zh-HK' : 'en' +const qpStr = (s: QPLocaleString | undefined, locale: Locale): string => + (s ? (s[qpLocaleKey(locale)] ?? s.en ?? '') : '') + +/** + * Render an operation's quote-permission requirement as a Markdown blockquote, + * resolving `x-quote-command` / `x-quote-level` / `x-quote-market` against + * quote-permissions.yaml. Returns '' when the operation has no permission marker. + */ +function quotePermissionMarkdown( + op: { 'x-quote-command'?: string; 'x-quote-level'?: string; 'x-quote-market'?: string }, + locale: Locale +): string { + const command = op['x-quote-command'] + if (!command && !op['x-quote-level'] && !op['x-quote-market']) return '' + const qp = qpData() + const cmd = command ? qp.commands?.[command] : undefined + const level = cmd?.level ?? op['x-quote-level'] ?? 'basic' + const levelDef = qp.levels?.[level] + if (!levelDef) return '' + const market = op['x-quote-market'] ?? cmd?.market + const title = qpStr(qp.ui?.permission_title, locale) + const badge = qpStr(levelDef.label, locale) + const marketLabel = market ? qpStr(qp.ui?.market_labels?.[market], locale) || market : '' + const desc = qpStr(cmd?.description ?? levelDef.description, locale) + const note = qpStr(qp.ui?.separate_note, locale) + + const head = [title, marketLabel, badge].filter(Boolean).join(' · ') + const lines = [`> **${head}**`] + for (const l of desc.split('\n').map((s) => s.trim()).filter(Boolean)) lines.push(`> ${l}`) + if (note) lines.push(`> _${note}_`) + return lines.join('\n') + '\n\n' +} + +const loc = ( + p: { description?: string; 'x-description-zh'?: string; 'x-description-zh-hk'?: string }, + locale: Locale +) => pickLocale(p.description, p['x-description-zh'], p['x-description-zh-hk'], locale).replace(/\s*\n\s*/g, ' ').trim() + +function paramTable(rows: XParameter[], heading: string, hLevel: string, locale: Locale): string { + if (!rows.length) return '' + let s = `${hLevel} ${heading}\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n` + for (const p of rows) { + s += `| \`${p.name}\` | ${p.type ?? 'string'} | ${p.required ? 'Yes' : 'No'} | ${loc(p, locale)} |\n` + } + return s + '\n' +} + +/** Markdown for a single endpoint. `base` is the top heading level (1 = `#`). */ +export function endpointMarkdown(ep: EndpointItem, locale: Locale, base = 1): string { + const op = ep.operation + const h = (n: number) => '#'.repeat(base + n - 1) + const title = pickLocale(op.summary, op['x-summary-zh'], op['x-summary-zh-hk'], locale) + const desc = pickLocale(op.description, op['x-description-zh'], op['x-description-zh-hk'], locale) + + let md = `${h(1)} ${title}\n\n\`${ep.method}\` \`${ep.path}\`\n\n` + md += quotePermissionMarkdown(op, locale) + if (desc.trim()) md += desc.trim() + '\n\n' + + const xp = op['x-parameters'] ?? [] + const path = xp.filter((p) => p.in === 'path') + const query = xp.filter((p) => p.in === 'query') + const body = xp.filter((p) => p.in === 'body') + if (path.length || query.length || body.length) { + md += `${h(2)} Parameters\n\n` + md += paramTable(path, 'Path Parameters', h(3), locale) + md += paramTable(query, 'Query Parameters', h(3), locale) + md += paramTable(body, 'Request Body', h(3), locale) + } + + const curl = + op['x-request-examples']?.find((s) => s.label === 'cURL')?.source ?? + op['x-codeSamples']?.find((s) => s.label === 'cURL')?.source + if (curl) md += `${h(2)} Request Example\n\n\`\`\`bash\n${curl.trimEnd()}\n\`\`\`\n\n` + + const rp = op['x-response-properties'] ?? [] + const respEx = buildResponseExample(ep) + if (rp.length || respEx) { + md += `${h(2)} Response\n\n` + if (rp.length) md += paramTable(rp, 'Response Properties', h(3), locale) + if (respEx) md += `${h(3)} Response JSON Example\n\n\`\`\`json\n${respEx}\n\`\`\`\n\n` + } + return md.trimEnd() + '\n' +} + +/** Flat list of endpoints (for getStaticPaths / indexing). */ +export function endpointList( + rawYaml: string +): Array<{ operationId: string; method: string; path: string; tag: string; summary: string }> { + const { groups } = parseSpec(rawYaml) + const out: Array<{ operationId: string; method: string; path: string; tag: string; summary: string }> = [] + // Dedupe by operationId: a multi-tag op appears in more than one group, which + // would otherwise emit duplicate static paths (a fatal Astro build error). + const seen = new Set<string>() + for (const g of groups) { + // Flat endpoints plus every subsection's endpoints. + const eps = [...g.endpoints, ...g.subgroups.flatMap((sg) => sg.endpoints)] + for (const ep of eps) { + const id = ep.operation.operationId + if (seen.has(id)) continue + seen.add(id) + out.push({ + operationId: id, + method: ep.method, + path: ep.path, + tag: g.name, + summary: ep.operation.summary, + }) + } + } + return out +} + +/** Markdown for a single endpoint by operationId (null if not found). */ +export function endpointMarkdownById(rawYaml: string, operationId: string, locale: Locale): string | null { + const { groups } = parseSpec(rawYaml) + for (const g of groups) { + const ep = [...g.endpoints, ...g.subgroups.flatMap((sg) => sg.endpoints)].find( + (e) => e.operation.operationId === operationId + ) + if (ep) return endpointMarkdown(ep, locale, 1) + } + return null +} + +// ── WebSocket commands ──────────────────────────────────────────────────────── + +/** Markdown for a single WebSocket command. `base` is the top heading level. */ +export function wsCommandMarkdown(cmd: WsCommandItem, locale: Locale, base = 1): string { + const h = (n: number) => '#'.repeat(base + n - 1) + const title = pickLocale(cmd.name, cmd.nameZh, cmd.nameZhHk, locale) + const desc = pickLocale(cmd.description, cmd.descriptionZh, cmd.descriptionZhHk, locale) + const dir = cmd.direction === 'push' ? 'push' : 'request' + + let md = `${h(1)} ${title}\n\n\`WS\` \`${dir}${cmd.cmd != null ? ` · cmd ${cmd.cmd}` : ''}\`\n\n` + if (cmd.quoteCommand) md += quotePermissionMarkdown({ 'x-quote-command': cmd.quoteCommand }, locale) + if (desc.trim()) md += desc.trim() + '\n\n' + if (cmd.fields?.length) md += paramTable(cmd.fields, 'Request Parameters', h(2), locale) + if (cmd.responseFields?.length) + md += paramTable(cmd.responseFields, cmd.direction === 'push' ? 'Push Fields' : 'Response Fields', h(2), locale) + if (cmd.responseExample) md += `${h(2)} Response JSON Example\n\n\`\`\`json\n${cmd.responseExample.trim()}\n\`\`\`\n\n` + return md.trimEnd() + '\n' +} + +/** All WS commands (merged into topical subgroups + any standalone ws groups). */ +function allWsCommands(rawYaml: string): WsCommandItem[] { + const { groups, wsGroups } = parseSpec(rawYaml) + const out: WsCommandItem[] = [] + for (const g of groups) for (const sg of g.subgroups) out.push(...(sg.wsCommands ?? [])) + for (const g of wsGroups) out.push(...g.commands) + return out +} + +/** Flat list of WS commands (for getStaticPaths / indexing). Callers that only + * need the id keep working; llms.txt uses name/cmd/direction to label pages. */ +export function wsCommandList( + rawYaml: string +): Array<{ id: string; name: string; cmd?: number; direction: 'request' | 'push' }> { + return allWsCommands(rawYaml).map((c) => ({ id: c.id, name: c.name, cmd: c.cmd, direction: c.direction })) +} + +/** Markdown for a single WS command by id (null if not found). */ +export function wsCommandMarkdownById(rawYaml: string, id: string, locale: Locale): string | null { + const cmd = allWsCommands(rawYaml).find((c) => c.id === id) + return cmd ? wsCommandMarkdown(cmd, locale, 1) : null +} + +/** Full reference: intro + every page + every endpoint, grouped by tag. */ +export function referenceMarkdown(rawYaml: string, locale: Locale): string { + const { groups, pages, wsGroups } = parseSpec(rawYaml) + let md = `# Longbridge OpenAPI Reference\n\nMachine-readable reference for all REST and WebSocket endpoints.\n\n` + + for (const pg of pages) { + const title = pickLocale(pg.title, pg.titleZh, pg.titleZhHk, locale) + const content = pickLocale(pg.content, pg.contentZh, pg.contentZhHk, locale).replace('[[SIGNING_TABS]]', '') + // Drop a leading heading in the page body that repeats the page title, so + // the title is not rendered twice (e.g. an "Error Codes" page whose content + // also opens with `## Error Codes`). + const body = content + .trim() + .replace(/^#{1,6}[ \t]+(.+?)[ \t]*\n+/, (m, h) => (h.trim() === title.trim() ? '' : m)) + md += `## ${title}\n\n${body}\n\n` + } + + for (const g of groups) { + const tag = pickLocale(g.name, g.nameZh, g.nameZhHk, locale) + md += `## ${tag}\n\n` + // Flat endpoints (groups without subsections, e.g. Screener) at level 3. + for (const ep of g.endpoints) md += endpointMarkdown(ep, locale, 3) + '\n' + // Subsections (docs subgroups): `### Subsection` then endpoints at level 4, + // followed by any WebSocket commands filed under the same subsection. + for (const sg of g.subgroups) { + const sub = pickLocale(sg.name, sg.nameZh, sg.nameZhHk, locale) + md += `### ${sub}\n\n` + for (const ep of sg.endpoints) md += endpointMarkdown(ep, locale, 4) + '\n' + for (const c of sg.wsCommands ?? []) md += wsCommandMarkdown(c, locale, 4) + '\n' + } + } + + // Standalone WebSocket protocol groups (commands not merged into a topical + // subgroup) — usually empty after the merge, rendered here for completeness. + for (const wg of wsGroups) { + if (!wg.commands.length) continue + md += `## ${pickLocale(wg.name, wg.nameZh, wg.nameZhHk, locale)}\n\n` + for (const c of wg.commands) md += wsCommandMarkdown(c, locale, 3) + '\n' + } + return md.trimEnd() + '\n' +} diff --git a/packages/api-reference/src/openapi-yaml.test.ts b/packages/api-reference/src/openapi-yaml.test.ts new file mode 100644 index 000000000..1495a09a8 --- /dev/null +++ b/packages/api-reference/src/openapi-yaml.test.ts @@ -0,0 +1,38 @@ +import { describe, it, expect } from 'vitest' +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { parseSpec } from './openapi-loader' +import { load } from 'js-yaml' + +const yamlPath = fileURLToPath(new URL('../../../openapi.yaml', import.meta.url)) +const raw = readFileSync(yamlPath, 'utf8') +const parsed = load(raw) as any +const { groups } = parseSpec(raw) +// Include subgroup endpoints, not just the flat ones — otherwise the invariants +// below only cover the handful of endpoints that live directly on a tag group. +const allEps = groups.flatMap((g) => [...g.endpoints, ...g.subgroups.flatMap((sg) => sg.endpoints)]) +const declaredTags: string[] = (parsed.tags ?? []).map((t: any) => t.name) + +describe('openapi.yaml invariants', () => { + it('every operationId is unique', () => { + const ids = allEps.map((e) => e.operation.operationId) + expect(new Set(ids).size).toBe(ids.length) + }) + it('every operation has summary + x-summary-zh + x-summary-zh-hk', () => { + const bad = allEps.filter( + (e) => !e.operation.summary || !e.operation['x-summary-zh'] || !e.operation['x-summary-zh-hk'], + ) + expect(bad.map((e) => e.operation.operationId)).toEqual([]) + }) + it('every operation tag is declared in top-level tags', () => { + const bad = allEps.filter((e) => (e.operation.tags ?? []).some((t) => !declaredTags.includes(t))) + expect(bad.map((e) => e.operation.operationId)).toEqual([]) + }) + it('every websocket op has x-codeSamples or a protobuf code block in description', () => { + const ws = allEps.filter((e) => e.method === 'WEBSOCKET') + const bad = ws.filter( + (e) => !e.operation['x-codeSamples']?.length && !/```protobuf/.test(e.operation.description ?? ''), + ) + expect(bad.map((e) => e.operation.operationId)).toEqual([]) + }) +}) diff --git a/packages/api-reference/src/signing-samples.ts b/packages/api-reference/src/signing-samples.ts new file mode 100644 index 000000000..d0e28241c --- /dev/null +++ b/packages/api-reference/src/signing-samples.ts @@ -0,0 +1,321 @@ +/** + * signing-samples.ts — client-side generators for HMAC-signed request examples. + * + * The Longbridge gateway does not accept a bare `Authorization: Bearer <token>`; + * every request must carry `X-Api-Key`, `X-Timestamp` and an `X-Api-Signature` + * computed as (verified against the staging gateway): + * + * ts = unix seconds + * signedHeaders = "authorization;x-api-key;x-timestamp" + * signedValues = "authorization:<token>\nx-api-key:<key>\nx-timestamp:<ts>\n" + * canonical = "<METHOD>|<path>|<query>|<signedValues>|<signedHeaders>|" + * + (body ? sha1_hex(body) : "") + * payload = "HMAC-SHA256|" + sha1_hex(canonical) + * signature = hex(hmac_sha256(<secret>, payload)) + * X-Api-Signature: HMAC-SHA256 SignedHeaders=<signedHeaders>, Signature=<signature> + * + * The signed samples emit that algorithm inline for all eight documented + * languages so a copy-pasted sample works without an SDK. + */ +import type { CodeBlock } from './openapi-loader' + +const hasBodyMethod = (m: string) => m === 'POST' || m === 'PUT' || m === 'PATCH' + +// ── Signed (HMAC) samples ───────────────────────────────────────────────────── + +function curlSample(method: string, path: string, base: string, withBody: boolean): string { + const bodyLines = withBody + ? `BODY='{}' # request body (compact JSON, no spaces)\n` + : `BODY=''\n` + const bodyHash = withBody + ? `[ -n "$BODY" ] && CANON="$CANON$(printf '%s' "$BODY" | openssl dgst -sha1 | awk '{print $2}')"\n` + : '' + const contentType = withBody ? ` --header 'Content-Type: application/json' \\\n` : '' + const dataFlag = withBody ? ` --data "$BODY"` : '' + return ( + `# Requires: bash, openssl, awk\n` + + `APP_KEY='<app_key>'\nAPP_SECRET='<app_secret>'\nACCESS_TOKEN='<access_token>'\n` + + `QUERY='' # e.g. symbol=DOGEUSD.BKKT\n` + + bodyLines + + `TS=$(date +%s)\n` + + `SIGNED_HEADERS='authorization;x-api-key;x-timestamp'\n` + + `# Build the canonical string in ONE printf — the newline after x-timestamp\n` + + `# (before the final |) must be kept; a separate $(...) would strip it and the\n` + + `# signature would be invalid.\n` + + `CANON=$(printf '%s|%s|%s|authorization:%s\\nx-api-key:%s\\nx-timestamp:%s\\n|%s|' "${method}" "${path}" "$QUERY" "$ACCESS_TOKEN" "$APP_KEY" "$TS" "$SIGNED_HEADERS")\n` + + bodyHash + + `PAYLOAD="HMAC-SHA256|$(printf '%s' "$CANON" | openssl dgst -sha1 | awk '{print $2}')"\n` + + `SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$APP_SECRET" | awk '{print $2}')\n\n` + + `curl --request ${method} \\\n` + + ` --url "${base}${path}$([ -n "$QUERY" ] && echo "?$QUERY")" \\\n` + + ` --header "Authorization: $ACCESS_TOKEN" \\\n` + + ` --header "X-Api-Key: $APP_KEY" \\\n` + + ` --header "X-Timestamp: $TS" \\\n` + + contentType + + ` --header "X-Api-Signature: HMAC-SHA256 SignedHeaders=$SIGNED_HEADERS, Signature=$SIG"` + + (dataFlag ? ` \\\n${dataFlag}` : '') + ) +} + +// Shared Python signing preamble (used by sync + async). +function pyPreamble(method: string, path: string, base: string, withBody: boolean): string { + return ( + `import hashlib, hmac, time, json\n\n` + + `APP_KEY = "<app_key>"\nAPP_SECRET = "<app_secret>"\nACCESS_TOKEN = "<access_token>"\n` + + `BASE = "${base}"\n` + + `method, path = "${method}", "${path}"\n` + + `query = "" # e.g. "symbol=DOGEUSD.BKKT"\n` + + (withBody ? `body = {} # request payload\n` : `body = None\n`) + + `\n` + + `def sha1_hex(s): return hashlib.sha1(s.encode()).hexdigest()\n\n` + + `ts = str(int(time.time()))\n` + + `body_str = json.dumps(body, separators=(",", ":")) if body is not None else ""\n` + + `signed_headers = "authorization;x-api-key;x-timestamp"\n` + + `signed_values = f"authorization:{ACCESS_TOKEN}\\nx-api-key:{APP_KEY}\\nx-timestamp:{ts}\\n"\n` + + `canonical = f"{method}|{path}|{query}|{signed_values}|{signed_headers}|"\n` + + `if body_str:\n canonical += sha1_hex(body_str)\n` + + `payload = "HMAC-SHA256|" + sha1_hex(canonical)\n` + + `sig = hmac.new(APP_SECRET.encode(), payload.encode(), hashlib.sha256).hexdigest()\n` + + `headers = {\n` + + ` "Authorization": ACCESS_TOKEN,\n` + + ` "X-Api-Key": APP_KEY,\n` + + ` "X-Timestamp": ts,\n` + + ` "X-Api-Signature": f"HMAC-SHA256 SignedHeaders={signed_headers}, Signature={sig}",\n` + + (withBody ? ` **({"Content-Type": "application/json"} if body_str else {}),\n` : ``) + + `}\n` + + `url = BASE + path + (f"?{query}" if query else "")\n` + ) +} + +function pythonSample(method: string, path: string, base: string, withBody: boolean): string { + return ( + `import requests\n` + + pyPreamble(method, path, base, withBody) + + `resp = requests.request(method, url, headers=headers` + + (withBody ? `, data=body_str or None` : ``) + + `)\n` + + `print(resp.json())` + ) +} + +function pythonAsyncSample(method: string, path: string, base: string, withBody: boolean): string { + return ( + `import asyncio, aiohttp\n` + + pyPreamble(method, path, base, withBody) + + `\n` + + `async def main():\n` + + ` async with aiohttp.ClientSession() as session:\n` + + ` async with session.request(method, url, headers=headers` + + (withBody ? `, data=body_str or None` : ``) + + `) as resp:\n` + + ` print(await resp.json())\n\n` + + `asyncio.run(main())` + ) +} + +function nodeSample(method: string, path: string, base: string, withBody: boolean): string { + return ( + `const crypto = require("crypto")\n\n` + + `const APP_KEY = "<app_key>"\nconst APP_SECRET = "<app_secret>"\nconst ACCESS_TOKEN = "<access_token>"\n` + + `const BASE = "${base}"\n` + + `const method = "${method}", path = "${path}"\n` + + `const query = "" // e.g. "symbol=DOGEUSD.BKKT"\n` + + (withBody ? `const body = {} // request payload\n` : `const body = null\n`) + + `\n` + + `const sha1Hex = (s) => crypto.createHash("sha1").update(s).digest("hex")\n` + + `const ts = String(Math.floor(Date.now() / 1000))\n` + + `const bodyStr = body != null ? JSON.stringify(body) : ""\n` + + `const signedHeaders = "authorization;x-api-key;x-timestamp"\n` + + `const signedValues = ` + '`authorization:${ACCESS_TOKEN}\\nx-api-key:${APP_KEY}\\nx-timestamp:${ts}\\n`' + `\n` + + `let canonical = ` + '`${method}|${path}|${query}|${signedValues}|${signedHeaders}|`' + `\n` + + `if (bodyStr) canonical += sha1Hex(bodyStr)\n` + + `const payload = "HMAC-SHA256|" + sha1Hex(canonical)\n` + + `const sig = crypto.createHmac("sha256", APP_SECRET).update(payload).digest("hex")\n\n` + + `const headers = {\n` + + ` "Authorization": ACCESS_TOKEN,\n` + + ` "X-Api-Key": APP_KEY,\n` + + ` "X-Timestamp": ts,\n` + + ' "X-Api-Signature": `HMAC-SHA256 SignedHeaders=${signedHeaders}, Signature=${sig}`,\n' + + (withBody ? ` ...(bodyStr ? { "Content-Type": "application/json" } : {}),\n` : ``) + + `}\n` + + 'const url = BASE + path + (query ? `?${query}` : "")\n' + + `fetch(url, { method, headers` + + (withBody ? `, body: bodyStr || undefined` : ``) + + ` })\n` + + ` .then((r) => r.json())\n` + + ` .then(console.log)` + ) +} + +function javaSample(method: string, path: string, base: string, withBody: boolean): string { + return ( + `import java.net.URI;\nimport java.net.http.*;\n` + + `import java.security.MessageDigest;\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\n` + + `import java.time.Instant;\nimport java.util.HexFormat;\n\n` + + `class Example {\n` + + ` static String sha1Hex(String s) throws Exception {\n` + + ` var d = MessageDigest.getInstance("SHA-1").digest(s.getBytes("UTF-8"));\n` + + ` return HexFormat.of().formatHex(d);\n` + + ` }\n\n` + + ` public static void main(String[] args) throws Exception {\n` + + ` String appKey = "<app_key>", appSecret = "<app_secret>", accessToken = "<access_token>";\n` + + ` String base = "${base}", method = "${method}", path = "${path}";\n` + + ` String query = ""; // e.g. "symbol=DOGEUSD.BKKT"\n` + + (withBody ? ` String body = "{}"; // compact JSON\n` : ` String body = "";\n`) + + ` String ts = String.valueOf(Instant.now().getEpochSecond());\n` + + ` String signedHeaders = "authorization;x-api-key;x-timestamp";\n` + + ` String signedValues = "authorization:" + accessToken + "\\nx-api-key:" + appKey + "\\nx-timestamp:" + ts + "\\n";\n` + + ` String canonical = method + "|" + path + "|" + query + "|" + signedValues + "|" + signedHeaders + "|";\n` + + ` if (!body.isEmpty()) canonical += sha1Hex(body);\n` + + ` String payload = "HMAC-SHA256|" + sha1Hex(canonical);\n` + + ` Mac mac = Mac.getInstance("HmacSHA256");\n` + + ` mac.init(new SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA256"));\n` + + ` String sig = HexFormat.of().formatHex(mac.doFinal(payload.getBytes("UTF-8")));\n\n` + + ` var builder = HttpRequest.newBuilder()\n` + + ` .uri(URI.create(base + path + (query.isEmpty() ? "" : "?" + query)))\n` + + ` .header("Authorization", accessToken)\n` + + ` .header("X-Api-Key", appKey)\n` + + ` .header("X-Timestamp", ts)\n` + + ` .header("X-Api-Signature", "HMAC-SHA256 SignedHeaders=" + signedHeaders + ", Signature=" + sig)\n` + + (withBody + ? ` .header("Content-Type", "application/json")\n .method(method, HttpRequest.BodyPublishers.ofString(body));\n` + : ` .method(method, HttpRequest.BodyPublishers.noBody());\n`) + + ` HttpResponse<String> resp = HttpClient.newHttpClient()\n` + + ` .send(builder.build(), HttpResponse.BodyHandlers.ofString());\n` + + ` System.out.println(resp.body());\n` + + ` }\n}` + ) +} + +function rustSample(method: string, path: string, base: string, withBody: boolean): string { + // deps: reqwest (blocking), hmac, sha2, sha1, hex, chrono + return ( + `// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }\n` + + `// hmac = "0.12", sha2 = "0.10", sha1 = "0.10", hex = "0.4"\n` + + `use hmac::{Hmac, Mac};\nuse sha2::Sha256;\nuse sha1::{Digest, Sha1};\n` + + `use std::time::{SystemTime, UNIX_EPOCH};\n\n` + + `fn sha1_hex(s: &str) -> String { hex::encode(Sha1::digest(s.as_bytes())) }\n\n` + + `fn main() -> Result<(), Box<dyn std::error::Error>> {\n` + + ` let (app_key, app_secret, access_token) = ("<app_key>", "<app_secret>", "<access_token>");\n` + + ` let (base, method, path) = ("${base}", "${method}", "${path}");\n` + + ` let query = ""; // e.g. "symbol=DOGEUSD.BKKT"\n` + + (withBody ? ` let body = "{}"; // compact JSON\n` : ` let body = "";\n`) + + ` let ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs().to_string();\n` + + ` let signed_headers = "authorization;x-api-key;x-timestamp";\n` + + ` let signed_values = format!("authorization:{}\\nx-api-key:{}\\nx-timestamp:{}\\n", access_token, app_key, ts);\n` + + ` let mut canonical = format!("{}|{}|{}|{}|{}|", method, path, query, signed_values, signed_headers);\n` + + ` if !body.is_empty() { canonical.push_str(&sha1_hex(body)); }\n` + + ` let payload = format!("HMAC-SHA256|{}", sha1_hex(&canonical));\n` + + ` let mut mac = Hmac::<Sha256>::new_from_slice(app_secret.as_bytes())?;\n` + + ` mac.update(payload.as_bytes());\n` + + ` let sig = hex::encode(mac.finalize().into_bytes());\n\n` + + ` let url = format!("{}{}{}", base, path, if query.is_empty() { String::new() } else { format!("?{}", query) });\n` + + ` let client = reqwest::blocking::Client::new();\n` + + ` let mut req = client.request(method.parse()?, &url)\n` + + ` .header("Authorization", access_token)\n` + + ` .header("X-Api-Key", app_key)\n` + + ` .header("X-Timestamp", &ts)\n` + + ` .header("X-Api-Signature", format!("HMAC-SHA256 SignedHeaders={}, Signature={}", signed_headers, sig));\n` + + (withBody ? ` req = req.header("Content-Type", "application/json").body(body);\n` : ``) + + ` let resp = req.send()?;\n` + + ` println!("{}", resp.text()?);\n` + + ` Ok(())\n}` + ) +} + +function cppSample(method: string, path: string, base: string, withBody: boolean): string { + // libcurl + OpenSSL + return ( + `// Requires: libcurl, OpenSSL. Link: -lcurl -lcrypto\n` + + `#include <curl/curl.h>\n#include <openssl/hmac.h>\n#include <openssl/sha.h>\n` + + `#include <ctime>\n#include <string>\n#include <cstdio>\n\n` + + `static std::string toHex(const unsigned char* d, unsigned n) {\n` + + ` static const char* h = "0123456789abcdef"; std::string o;\n` + + ` for (unsigned i = 0; i < n; i++) { o += h[d[i] >> 4]; o += h[d[i] & 0xf]; } return o;\n` + + `}\n` + + `static std::string sha1Hex(const std::string& s) {\n` + + ` unsigned char d[SHA_DIGEST_LENGTH];\n` + + ` SHA1((const unsigned char*)s.data(), s.size(), d);\n` + + ` return toHex(d, SHA_DIGEST_LENGTH);\n` + + `}\n\n` + + `int main() {\n` + + ` std::string appKey = "<app_key>", appSecret = "<app_secret>", accessToken = "<access_token>";\n` + + ` std::string base = "${base}", method = "${method}", path = "${path}", query = "";\n` + + (withBody ? ` std::string body = "{}";\n` : ` std::string body = "";\n`) + + ` std::string ts = std::to_string((long)time(nullptr));\n` + + ` std::string signedHeaders = "authorization;x-api-key;x-timestamp";\n` + + ` std::string signedValues = "authorization:" + accessToken + "\\nx-api-key:" + appKey + "\\nx-timestamp:" + ts + "\\n";\n` + + ` std::string canonical = method + "|" + path + "|" + query + "|" + signedValues + "|" + signedHeaders + "|";\n` + + ` if (!body.empty()) canonical += sha1Hex(body);\n` + + ` std::string payload = "HMAC-SHA256|" + sha1Hex(canonical);\n` + + ` unsigned char mac[32]; unsigned macLen = 0;\n` + + ` HMAC(EVP_sha256(), appSecret.data(), (int)appSecret.size(),\n` + + ` (const unsigned char*)payload.data(), payload.size(), mac, &macLen);\n` + + ` std::string sig = toHex(mac, macLen);\n\n` + + ` CURL* curl = curl_easy_init();\n` + + ` std::string url = base + path + (query.empty() ? "" : "?" + query);\n` + + ` curl_slist* h = nullptr;\n` + + ` h = curl_slist_append(h, ("Authorization: " + accessToken).c_str());\n` + + ` h = curl_slist_append(h, ("X-Api-Key: " + appKey).c_str());\n` + + ` h = curl_slist_append(h, ("X-Timestamp: " + ts).c_str());\n` + + ` h = curl_slist_append(h, ("X-Api-Signature: HMAC-SHA256 SignedHeaders=" + signedHeaders + ", Signature=" + sig).c_str());\n` + + (withBody ? ` h = curl_slist_append(h, "Content-Type: application/json");\n` : ``) + + ` curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n` + + ` curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());\n` + + ` curl_easy_setopt(curl, CURLOPT_HTTPHEADER, h);\n` + + (withBody ? ` curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());\n` : ``) + + ` curl_easy_perform(curl);\n` + + ` curl_easy_cleanup(curl); curl_slist_free_all(h);\n` + + ` return 0;\n}` + ) +} + +function goSample(method: string, path: string, base: string, withBody: boolean): string { + return ( + `package main\n\n` + + `import (\n` + + `\t"crypto/hmac"\n\t"crypto/sha1"\n\t"crypto/sha256"\n\t"encoding/hex"\n` + + `\t"fmt"\n\t"io"\n\t"net/http"\n\t"strconv"\n\t"strings"\n\t"time"\n)\n\n` + + `func sha1Hex(s string) string { h := sha1.Sum([]byte(s)); return hex.EncodeToString(h[:]) }\n\n` + + `func main() {\n` + + `\tappKey, appSecret, accessToken := "<app_key>", "<app_secret>", "<access_token>"\n` + + `\tbase, method, path := "${base}", "${method}", "${path}"\n` + + `\tquery := "" // e.g. "symbol=DOGEUSD.BKKT"\n` + + (withBody ? `\tbody := "{}" // compact JSON\n` : `\tbody := ""\n`) + + `\tts := strconv.FormatInt(time.Now().Unix(), 10)\n` + + `\tsignedHeaders := "authorization;x-api-key;x-timestamp"\n` + + "\tsignedValues := fmt.Sprintf(\"authorization:%s\\nx-api-key:%s\\nx-timestamp:%s\\n\", accessToken, appKey, ts)\n" + + `\tcanonical := fmt.Sprintf("%s|%s|%s|%s|%s|", method, path, query, signedValues, signedHeaders)\n` + + `\tif body != "" {\n\t\tcanonical += sha1Hex(body)\n\t}\n` + + `\tpayload := "HMAC-SHA256|" + sha1Hex(canonical)\n` + + `\tmac := hmac.New(sha256.New, []byte(appSecret))\n` + + `\tmac.Write([]byte(payload))\n` + + `\tsig := hex.EncodeToString(mac.Sum(nil))\n\n` + + `\turl := base + path\n\tif query != "" {\n\t\turl += "?" + query\n\t}\n` + + (withBody + ? `\treq, _ := http.NewRequest(method, url, strings.NewReader(body))\n\treq.Header.Set("Content-Type", "application/json")\n` + : `\treq, _ := http.NewRequest(method, url, nil)\n\t_ = strings.NewReader\n`) + + `\treq.Header.Set("Authorization", accessToken)\n` + + `\treq.Header.Set("X-Api-Key", appKey)\n` + + `\treq.Header.Set("X-Timestamp", ts)\n` + + `\treq.Header.Set("X-Api-Signature", "HMAC-SHA256 SignedHeaders="+signedHeaders+", Signature="+sig)\n` + + `\tresp, _ := http.DefaultClient.Do(req)\n\tdefer resp.Body.Close()\n` + + `\tout, _ := io.ReadAll(resp.Body)\n\tfmt.Println(string(out))\n}` + ) +} + +/** Signed request samples for all eight documented languages. */ +export function signedCodeBlocks(method: string, path: string, base: string): CodeBlock[] { + const m = method.toUpperCase() + const b = hasBodyMethod(m) + return [ + { lang: 'shell', label: 'cURL', code: curlSample(m, path, base, b) }, + { lang: 'python', label: 'Python', code: pythonSample(m, path, base, b) }, + { lang: 'python', label: 'Python (async)', code: pythonAsyncSample(m, path, base, b) }, + { lang: 'javascript', label: 'Node.js', code: nodeSample(m, path, base, b) }, + { lang: 'java', label: 'Java', code: javaSample(m, path, base, b) }, + { lang: 'rust', label: 'Rust', code: rustSample(m, path, base, b) }, + { lang: 'cpp', label: 'C++', code: cppSample(m, path, base, b) }, + { lang: 'go', label: 'Go', code: goSample(m, path, base, b) }, + ] +} diff --git a/packages/tryit/src/AuthorizationForm.tsx b/packages/tryit/src/AuthorizationForm.tsx index b2229114a..076eaa290 100644 --- a/packages/tryit/src/AuthorizationForm.tsx +++ b/packages/tryit/src/AuthorizationForm.tsx @@ -20,9 +20,12 @@ export function AuthorizationForm({ authData, autoFilled, onChange }: Authorizat } return ( - <div className="tryit-base-form rounded-xl overflow-hidden" style={{ border: '1px solid var(--vp-c-border)' }}> - <div - className="flex items-center justify-between cursor-pointer select-none p-4 tryit-form-header" + <div className="tryit-base-form overflow-hidden" style={{ border: '1px solid var(--lb-stroke, #e6e7e8)', borderRadius: 'var(--ar-radius-md, 6px)' }}> + <button + type="button" + aria-expanded={!collapsed} + className="flex items-center justify-between select-none p-4 tryit-form-header w-full text-left" + style={{ background: 'transparent', border: 'none' }} onClick={() => setCollapsed((c) => !c)} > <h2 className="font-semibold m-0" style={{ color: 'var(--vp-c-text-1)' }}> @@ -33,7 +36,7 @@ export function AuthorizationForm({ authData, autoFilled, onChange }: Authorizat Auto filled </span> )} - </div> + </button> <div className={`tryit-form-content${collapsed ? ' tryit-collapsed' : ''}`}> <div className="px-4 pb-4 flex flex-col gap-3"> diff --git a/packages/tryit/src/ParametersForm.tsx b/packages/tryit/src/ParametersForm.tsx index 80223e5cf..619a49f94 100644 --- a/packages/tryit/src/ParametersForm.tsx +++ b/packages/tryit/src/ParametersForm.tsx @@ -16,6 +16,8 @@ export interface ParameterRow { interface ParametersFormProps { parameters?: ParameterRow[] onChange: (data: Record<string, unknown>) => void + /** Names of required params flagged as empty after a failed submit. */ + invalid?: Set<string> } function isRequired(val?: string | boolean): boolean { @@ -33,8 +35,16 @@ function normalizeType(type: string): 'number' | 'boolean' | 'array' | 'text' { return 'text' } -export function ParametersForm({ parameters = [], onChange }: ParametersFormProps) { +// Above this many total parameters, extra optional fields collapse by default. +const MANY_PARAMS = 6 +// Always keep at least this many fields visible up front — if there aren't +// enough required ones, lead optional fields fill the gap so the form is never +// just a bare "show optional" toggle. +const MIN_VISIBLE = 4 + +export function ParametersForm({ parameters = [], onChange, invalid }: ParametersFormProps) { const [collapsed, setCollapsed] = useState(false) + const [showOptional, setShowOptional] = useState(false) const [values, setValues] = useState<Record<string, unknown>>({}) if (parameters.length === 0) { @@ -47,26 +57,61 @@ export function ParametersForm({ parameters = [], onChange }: ParametersFormProp onChange(next) } + // Required first, optional after. When there are many params, collapse only the + // OVERFLOW optional fields — always keep at least MIN_VISIBLE fields showing so + // an all-optional endpoint doesn't render as just a toggle. + const required = parameters.filter((p) => isRequired(p.required)) + const optional = parameters.filter((p) => !isRequired(p.required)) + const manyParams = parameters.length > MANY_PARAMS + const leadCount = manyParams ? Math.max(0, MIN_VISIBLE - required.length) : optional.length + const leadOptional = optional.slice(0, leadCount) + const restOptional = optional.slice(leadCount) + return ( - <div className="tryit-base-form rounded-xl overflow-hidden" style={{ border: '1px solid var(--vp-c-border)' }}> - <div - className="flex items-center justify-between cursor-pointer select-none p-4 tryit-form-header" + <div className="tryit-base-form overflow-hidden" style={{ border: '1px solid var(--lb-stroke, #e6e7e8)', borderRadius: 'var(--ar-radius-md, 6px)' }}> + <button + type="button" + aria-expanded={!collapsed} + className="flex items-center justify-between select-none p-4 tryit-form-header w-full text-left" + style={{ background: 'transparent', border: 'none' }} onClick={() => setCollapsed((c) => !c)} > - <h2 className="font-semibold m-0" style={{ color: 'var(--vp-c-text-1)' }}> + <h2 className="font-semibold m-0" style={{ color: 'var(--lb-fg-1, #0a0e19)' }}> Parameters </h2> - </div> + </button> <div className={`tryit-form-content${collapsed ? ' tryit-collapsed' : ''}`}> <div className="px-4 pb-4 flex flex-col gap-3"> - {parameters.map((param) => ( + {[...required, ...leadOptional].map((param) => ( <ParameterField key={param.name} param={param} value={values[param.name]} + invalid={invalid?.has(param.name)} onChange={(val) => update(param.name, val)} /> ))} + {restOptional.length > 0 && ( + <button + type="button" + aria-expanded={showOptional} + className="tryit-optional-toggle text-xs font-medium self-start" + style={{ background: 'transparent', border: 'none', cursor: 'pointer', color: 'var(--lb-brand, #00b8b8)', padding: '2px 0' }} + onClick={() => setShowOptional((v) => !v)} + > + {showOptional ? '▾ Hide optional parameters' : `▸ Show ${restOptional.length} more optional parameters`} + </button> + )} + {showOptional && + restOptional.map((param) => ( + <ParameterField + key={param.name} + param={param} + value={values[param.name]} + invalid={invalid?.has(param.name)} + onChange={(val) => update(param.name, val)} + /> + ))} </div> </div> </div> @@ -77,19 +122,21 @@ interface ParameterFieldProps { param: ParameterRow value: unknown onChange: (val: unknown) => void + invalid?: boolean } -function ParameterField({ param, value, onChange }: ParameterFieldProps) { +function ParameterField({ param, value, onChange, invalid }: ParameterFieldProps) { const kind = normalizeType(param.type) const required = isRequired(param.required) + const controlClass = `tryit-input${invalid ? ' tryit-input--invalid' : ''}` return ( <div className="flex flex-col gap-1"> - <label className="text-xs font-medium flex gap-1 items-center" style={{ color: 'var(--vp-c-text-2)' }}> + <label className="text-xs font-medium flex gap-1 items-center" style={{ color: 'var(--lb-fg-2, #6c6e75)' }}> {param.name} - {required && <span style={{ color: 'var(--vp-c-danger-1)' }}>*</span>} + {required && <span style={{ color: 'var(--lb-risk-danger, #ff3a3a)' }}>*</span>} {param.description && ( - <span className="font-normal ml-1" style={{ color: 'var(--vp-c-text-3)' }}> + <span className="font-normal ml-1" style={{ color: 'var(--lb-fg-3, #a9abae)' }}> — {param.description} </span> )} @@ -97,13 +144,15 @@ function ParameterField({ param, value, onChange }: ParameterFieldProps) { {kind === 'boolean' ? ( <select + data-param={param.name} + aria-invalid={invalid || undefined} value={value === undefined ? '' : String(value)} onChange={(e) => { const v = e.target.value if (v === '') onChange(undefined) else onChange(v === 'true') }} - className="tryit-input" + className={controlClass} > <option value="">—</option> <option value="true">true</option> @@ -111,21 +160,25 @@ function ParameterField({ param, value, onChange }: ParameterFieldProps) { </select> ) : kind === 'number' ? ( <input + data-param={param.name} + aria-invalid={invalid || undefined} type="number" value={value === undefined ? '' : String(value)} onChange={(e) => { const v = e.target.value onChange(v === '' ? undefined : Number(v)) }} - className="tryit-input" + className={controlClass} placeholder={param.name} /> ) : ( <input + data-param={param.name} + aria-invalid={invalid || undefined} type="text" value={value === undefined ? '' : String(value)} onChange={(e) => onChange(e.target.value || undefined)} - className="tryit-input" + className={controlClass} placeholder={kind === 'array' ? 'comma-separated values' : param.name} /> )} diff --git a/packages/tryit/src/ResponseView.tsx b/packages/tryit/src/ResponseView.tsx index b7860c07a..13474c4ec 100644 --- a/packages/tryit/src/ResponseView.tsx +++ b/packages/tryit/src/ResponseView.tsx @@ -86,6 +86,35 @@ export function ResponseView({ result }: ResponseViewProps) { if (!result) return null + // Client-side failure (network/CORS/DNS, timeout, missing App Secret) — show a + // readable failure state, not a pretty-printed exception. + if (result.networkError) { + const message = result.errorMessage || "Couldn't send the request. Check your connection and try again." + return ( + <div + className="w-full rounded-xl p-4 flex flex-col gap-2" + style={{ backgroundColor: 'var(--vp-c-bg-soft)', border: '1px solid var(--vp-c-border)' }} + role="alert" + > + <div className="flex items-center space-x-2"> + <div className={`w-3.5 h-3.5 rounded-full ${getStatusIconClass(result.status)}`} /> + <div className="text-sm font-semibold" style={{ color: 'var(--vp-c-text-1)' }}> + Request failed + </div> + </div> + <p className="text-sm m-0" style={{ color: 'var(--vp-c-text-2)' }}> + {message} + </p> + {result.errorDetail && ( + <details className="text-xs" style={{ color: 'var(--vp-c-text-3)' }}> + <summary className="cursor-pointer select-none">Details</summary> + <pre className="mt-2 whitespace-pre-wrap font-mono break-words m-0">{result.errorDetail}</pre> + </details> + )} + </div> + ) + } + return ( <div className="w-full rounded-xl p-0.5" @@ -106,9 +135,9 @@ export function ResponseView({ result }: ResponseViewProps) { {/* Download */} <button onClick={downloadResponse} - className="h-7 w-7 flex items-center justify-center cursor-pointer rounded-md transition-all duration-200 hover:scale-110" + className="h-7 w-7 flex items-center justify-center cursor-pointer rounded-md transition-opacity duration-200 hover:opacity-70" style={{ backgroundColor: 'transparent' }} - title="download" + title="Download" > <svg className="w-4 h-4" @@ -125,15 +154,15 @@ export function ResponseView({ result }: ResponseViewProps) { {/* Copy */} <button onClick={copyResponse} - className="h-7 w-7 flex items-center justify-center rounded-md transition-all duration-200 hover:scale-110" + className="h-7 w-7 flex items-center justify-center cursor-pointer rounded-md transition-opacity duration-200 hover:opacity-70" title={copySuccess ? 'Copied' : 'Copy'} > {copySuccess ? ( - <svg width="18" height="18" viewBox="0 0 18 18" fill="none" style={{ color: 'var(--vp-c-success-1)' }}> + <svg width="18" height="18" viewBox="0 0 18 18" fill="none" style={{ color: 'var(--lb-brand, #00b8b8)' }}> <path d="M15 4.5L7.5 12L3 7.5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> </svg> ) : ( - <svg width="18" height="18" viewBox="0 0 18 18" fill="none" style={{ color: 'var(--vp-c-text-2)' }}> + <svg width="18" height="18" viewBox="0 0 18 18" fill="none" style={{ color: 'var(--lb-fg-2, #6c6e75)' }}> <path d="M14.25 5.25H7.25C6.14543 5.25 5.25 6.14543 5.25 7.25V14.25C5.25 15.3546 6.14543 16.25 7.25 16.25H14.25C15.3546 16.25 16.25 15.3546 16.25 14.25V7.25C16.25 6.14543 15.3546 5.25 14.25 5.25Z" stroke="currentColor" diff --git a/packages/tryit/src/TryIt.tsx b/packages/tryit/src/TryIt.tsx index 769fdfe2d..f35047d2e 100644 --- a/packages/tryit/src/TryIt.tsx +++ b/packages/tryit/src/TryIt.tsx @@ -17,6 +17,7 @@ import { useTryItMode } from './hooks/useTryItMode' import { useAuthorization } from './hooks/useAuthorization' import { useResponse } from './hooks/useResponse' import { createQuickRequest } from './utils/request' +import { MissingAppSecretError } from './clients/http-client' export interface TryItProps { operationId?: string @@ -117,11 +118,23 @@ export function TryIt({ method, path, parameters = [] }: TryItProps) { } setResult(res) } catch (err) { - const errorMsg = err instanceof Error ? err.message : String(err) + // This is almost always a client-side failure (network/CORS/DNS, timeout, + // or a missing App Secret) — NOT a server 500. Represent it as such and show + // a human message; keep the raw detail as secondary info. + const detail = err instanceof Error ? err.message : String(err) + const isMissingSecret = + err instanceof MissingAppSecretError || + (err as { code?: string })?.code === 'MISSING_APP_SECRET' + const message = isMissingSecret + ? 'Enter your App Secret to sign this request.' + : "Couldn't send the request. Check your connection and try again." setResult({ - status: 500, - statusText: 'Internal Server Error', - response: { code: -1, msg: errorMsg, data: null }, + status: 0, + statusText: 'Request failed', + networkError: true, + errorMessage: message, + errorDetail: detail, + response: { code: -1, msg: message, data: null }, }) } finally { setIsLoading(false) @@ -153,9 +166,14 @@ export function TryIt({ method, path, parameters = [] }: TryItProps) { {/* Back */} <button + type="button" onClick={handleGoBack} - className="ml-auto text-xs underline cursor-pointer" - style={{ color: 'var(--vp-c-text-3)', background: 'none', border: 'none', padding: 0 }} + className="ml-auto inline-flex items-center text-xs px-2 py-1 rounded-md transition-colors duration-200 hover:opacity-80" + style={{ + color: 'var(--vp-c-text-3)', + background: 'transparent', + border: '1px solid var(--vp-c-border)', + }} > ← Back </button> diff --git a/packages/tryit/src/clients/http-client.ts b/packages/tryit/src/clients/http-client.ts index 9aafd0892..ef954e3fa 100644 --- a/packages/tryit/src/clients/http-client.ts +++ b/packages/tryit/src/clients/http-client.ts @@ -21,6 +21,15 @@ export interface ApiConfig { export interface ApiResponse<T = any> { status: number statusText: string + /** + * 客户端侧失败(网络/CORS/DNS/缺少 App Secret 等),并非服务器返回的 HTTP 状态。 + * 为 true 时 UI 应展示可读的失败提示,而非把异常当作 JSON 渲染。 + */ + networkError?: boolean + /** 面向用户的可读失败提示(networkError 为 true 时使用) */ + errorMessage?: string + /** 原始异常详情,作为次要信息(可折叠)展示 */ + errorDetail?: string response: { /** 业务状态码,0 表示成功 */ code: number @@ -44,13 +53,28 @@ export interface RequestOptions { timeout?: number } +// ==================== 错误类型 ==================== + +/** + * App Secret 缺失时抛出。UI 可通过 `err instanceof MissingAppSecretError` + * 或 `err.code === 'MISSING_APP_SECRET'` 识别并展示友好提示。 + */ +export class MissingAppSecretError extends Error { + readonly code = 'MISSING_APP_SECRET' + constructor(message = 'Enter your App Secret to sign this request.') { + super(message) + this.name = 'MissingAppSecretError' + } +} + // ==================== 工具函数 ==================== /** - * 生成时间戳 + * 生成时间戳 —— 整数秒 (Unix epoch)。网关要求整数秒;带小数会导致 + * 签名/时间戳校验失败。 */ function getTimestamp(): string { - return (Date.now() / 1000).toString() + return Math.floor(Date.now() / 1000).toString() } /** @@ -89,8 +113,12 @@ async function generateSignature( const signStr = `HMAC-SHA256|${canonicalHashHex}` - // 确保密钥是有效的字符串格式 - const normalizedSecret = secret?.trim() || 'unknown' + // App Secret 缺失时,不能用占位符签名(会产生令人困惑的签名校验失败)。 + // 明确抛出可识别的错误,供 UI 展示可读提示。 + const normalizedSecret = secret?.trim() + if (!normalizedSecret) { + throw new MissingAppSecretError() + } const secretKey = await crypto.subtle.importKey( 'raw', @@ -175,7 +203,18 @@ export class LongbridgeApiClient { clearTimeout(timeoutId) - const result = await response.json() + // Read as text first so a non-JSON body (204 No Content, empty body, or a + // proxy/gateway HTML error page) is surfaced with its real HTTP status + // instead of throwing a JSON parse error that masquerades as status 0. + const text = await response.text() + let result: any + try { + result = text + ? JSON.parse(text) + : { code: response.status, msg: `HTTP ${response.status} ${response.statusText}`, data: null } + } catch { + result = { code: response.status, msg: text.slice(0, 800), data: null } + } return { status: response.status, statusText: response.statusText, diff --git a/packages/tryit/src/index.ts b/packages/tryit/src/index.ts index 305bbbf53..392067821 100644 --- a/packages/tryit/src/index.ts +++ b/packages/tryit/src/index.ts @@ -1 +1,11 @@ export { TryIt } from './TryIt' + +// Pieces reused by the API Reference right rail (RequestPanel / ResponsePanel). +export { AuthorizationForm } from './AuthorizationForm' +export { ParametersForm } from './ParametersForm' +export type { ParameterRow } from './ParametersForm' +export { ResponseView } from './ResponseView' +export { useAuthorization } from './hooks/useAuthorization' +export type { AuthData } from './hooks/useAuthorization' +export { createQuickRequest } from './utils/request' +export type { ApiResponse } from './clients/http-client' diff --git a/packages/tryit/src/tryit.css b/packages/tryit/src/tryit.css index 56a20f95f..f7500c133 100644 --- a/packages/tryit/src/tryit.css +++ b/packages/tryit/src/tryit.css @@ -7,27 +7,27 @@ /* ── PlayButton color variants ───────────────────────────────────────────── */ .tryit-btn-success { - background-color: var(--vp-c-success-1); + background-color: var(--vp-c-success-1, var(--lb-c-success-fg, #16a34a)); color: #fff; } .tryit-btn-brand { - background-color: var(--vp-c-brand-1); + background-color: var(--vp-c-brand-1, var(--lb-c-info-fg, #3b82f6)); color: #fff; } .tryit-btn-warning { - background-color: var(--vp-c-warning-1); + background-color: var(--vp-c-warning-1, var(--lb-c-warning-fg, #d97706)); color: #fff; } .tryit-btn-danger { - background-color: var(--vp-c-danger-1); + background-color: var(--vp-c-danger-1, var(--lb-c-danger-fg, #dc2626)); color: #fff; } .tryit-btn-important { - background-color: var(--vp-c-important-1); + background-color: var(--vp-c-important-1, #8b5cf6); color: #fff; } @@ -36,49 +36,92 @@ color: #fff; } +/* Focus ring for all interactive controls styled here (buttons + input) */ +.tryit-btn-success:focus-visible, +.tryit-btn-brand:focus-visible, +.tryit-btn-warning:focus-visible, +.tryit-btn-danger:focus-visible, +.tryit-btn-important:focus-visible, +.tryit-btn-default:focus-visible, +.tryit-input:focus-visible { + box-shadow: var(--lb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #3b82f6); + outline: none; +} + /* ── Method badge text + bg colors ──────────────────────────────────────── */ .method-get { - color: var(--vp-c-success-1); - background-color: var(--vp-c-success-soft); + color: var(--vp-c-success-1, var(--lb-c-success-fg, #16a34a)); + background-color: var(--vp-c-success-soft, var(--lb-c-success-bg, rgba(34, 197, 94, 0.1))); } .method-post { - color: var(--vp-c-brand-1); - background-color: var(--vp-c-brand-soft); + color: var(--vp-c-brand-1, var(--lb-c-info-fg, #3b82f6)); + background-color: var(--vp-c-brand-soft, var(--lb-c-info-bg, rgba(59, 130, 246, 0.1))); } .method-put { - color: var(--vp-c-warning-1); - background-color: var(--vp-c-warning-soft); + color: var(--vp-c-warning-1, var(--lb-c-warning-fg, #d97706)); + background-color: var(--vp-c-warning-soft, var(--lb-c-warning-bg, rgba(234, 179, 8, 0.1))); } .method-delete { - color: var(--vp-c-danger-1); - background-color: var(--vp-c-danger-soft); + color: var(--vp-c-danger-1, var(--lb-c-danger-fg, #dc2626)); + background-color: var(--vp-c-danger-soft, var(--lb-c-danger-bg, rgba(239, 68, 68, 0.1))); } .method-patch { - color: var(--vp-c-important-1); - background-color: var(--vp-c-important-soft); + color: var(--vp-c-important-1, #8b5cf6); + background-color: var(--vp-c-important-soft, rgba(139, 92, 246, 0.1)); } /* ── Response status dot ─────────────────────────────────────────────────── */ +/* Dots carry a distinct glyph so status is not conveyed by hue alone. */ +.tryit-status-success, +.tryit-status-danger, +.tryit-status-warning, +.tryit-status-default { + display: inline-flex; + align-items: center; + justify-content: center; +} + +.tryit-status-success::before, +.tryit-status-danger::before, +.tryit-status-warning::before, +.tryit-status-default::before { + font-size: 9px; + line-height: 1; + color: #fff; +} + .tryit-status-success { - background-color: var(--vp-c-success-1); + background-color: var(--vp-c-success-1, var(--lb-c-success-fg, #16a34a)); +} +.tryit-status-success::before { + content: "\2713"; /* ✓ */ } .tryit-status-danger { - background-color: var(--vp-c-danger-1); + background-color: var(--vp-c-danger-1, var(--lb-c-danger-fg, #dc2626)); +} +.tryit-status-danger::before { + content: "\2715"; /* ✕ */ } .tryit-status-warning { - background-color: var(--vp-c-warning-1); + background-color: var(--vp-c-warning-1, var(--lb-c-warning-fg, #d97706)); +} +.tryit-status-warning::before { + content: "\25B2"; /* ▲ */ } .tryit-status-default { - background-color: var(--vp-c-text-3); + background-color: var(--vp-c-text-3, #9ca3af); +} +.tryit-status-default::before { + content: "\2022"; /* • */ } /* ── Form inputs ─────────────────────────────────────────────────────────── */ @@ -86,10 +129,10 @@ .tryit-input { width: 100%; padding: 6px 10px; - border-radius: 6px; - border: 1px solid var(--vp-c-border); - background-color: var(--vp-c-bg); - color: var(--vp-c-text-1); + border-radius: var(--ar-radius-sm, 6px); + border: 1px solid var(--lb-stroke, #e6e7e8); + background-color: var(--lb-bg-1, #fff); + color: var(--lb-fg-1, #1f2937); font-size: 13px; line-height: 1.5; outline: none; @@ -97,7 +140,28 @@ } .tryit-input:focus { - border-color: var(--vp-c-brand-1); + border-color: var(--lb-brand, #00b8b8); +} +/* Required param flagged empty after a failed Try it. */ +.tryit-input--invalid, +.tryit-input--invalid:focus { + border-color: var(--lb-risk-danger, #ff3a3a); + box-shadow: 0 0 0 1px var(--lb-risk-danger, #ff3a3a); +} + +/* Keep the input legible on dark pages: track the site's data-mode toggle, + falling back to the OS preference when the host has not stamped a mode. */ +:root[data-mode="dark"] .tryit-input { + border-color: var(--lb-stroke, #2c3039); + background-color: var(--lb-bg-1, #161b22); + color: var(--lb-fg-1, #e5e7eb); +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode="light"]) .tryit-input { + border-color: var(--lb-stroke, #2c3039); + background-color: var(--lb-bg-1, #161b22); + color: var(--lb-fg-1, #e5e7eb); + } } /* ── Collapsible form content ────────────────────────────────────────────── */ @@ -113,7 +177,23 @@ } .tryit-form-header:hover { - background-color: var(--vp-c-bg-soft); + background-color: var(--lb-bg-2, #f3f5f6); +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode="light"]) .tryit-form-header:hover { + background-color: var(--lb-bg-2, #161a26); + } +} +:root[data-mode="dark"] .tryit-form-header:hover { + background-color: var(--lb-bg-2, #161a26); +} + +/* Respect reduced-motion: disable the collapse animation and input transition. */ +@media (prefers-reduced-motion: reduce) { + .tryit-form-content, + .tryit-input { + transition: none !important; + } } /* ── Panel wrapper ───────────────────────────────────────────────────────── */ diff --git a/packages/tryit/src/utils/request.ts b/packages/tryit/src/utils/request.ts index 5613672ff..688764cc7 100644 --- a/packages/tryit/src/utils/request.ts +++ b/packages/tryit/src/utils/request.ts @@ -33,7 +33,10 @@ export function createDynamicRequest(authConfig: AuthConfig, options: RequestOpt } } - if (import.meta.env.DEV) { + // In dev, route through the same-origin proxy to avoid CORS — but only when + // the caller didn't pass an explicit baseUrl. The API Reference passes its own + // per-environment dev proxy prefix (/api-prod, /api-test), which must win. + if (import.meta.env.DEV && !options.baseUrl) { baseUrl = '/api' } diff --git a/src/components/shell/LanguageSwitcher.tsx b/src/components/shell/LanguageSwitcher.tsx index a4ab7b80b..2a67dc756 100644 --- a/src/components/shell/LanguageSwitcher.tsx +++ b/src/components/shell/LanguageSwitcher.tsx @@ -13,11 +13,15 @@ const LOCALES: { value: Locale; label: string }[] = [ ] function buildUrl(currentLocale: Locale, targetLocale: Locale, currentPath: string): string { + // `currentPath` may include a `?query` (e.g. the API reference's `?page=`) — + // keep it so switching locale preserves the exact view. + const qIdx = currentPath.indexOf('?') + const search = qIdx >= 0 ? currentPath.slice(qIdx) : '' // Strip current locale prefix to get bare path. - // `currentPath` can carry a `.html` suffix (build format:'file' → Astro.url + // The path can carry a `.html` suffix (build format:'file' → Astro.url // pathname is `…/overview.html`); drop it so the switched-locale link points // at the clean URL, not the 404 `.html` file. - let barePath = currentPath.replace(/(?:index)?\.html$/, '') + let barePath = (qIdx >= 0 ? currentPath.slice(0, qIdx) : currentPath).replace(/(?:index)?\.html$/, '') if (currentLocale !== 'en') { const prefix = `/${currentLocale}` if (barePath.startsWith(prefix + '/')) { @@ -30,14 +34,14 @@ function buildUrl(currentLocale: Locale, targetLocale: Locale, currentPath: stri // For English, use bare path (no prefix) if (targetLocale === 'en') { - return barePath + return (barePath === '/' ? '/' : barePath) + search } // For zh-CN or zh-HK, prepend locale if (barePath === '/') { - return `/${targetLocale}` + return `/${targetLocale}${search}` } - return `/${targetLocale}${barePath}` + return `/${targetLocale}${barePath}${search}` } function GlobeIcon() { @@ -62,10 +66,18 @@ function GlobeIcon() { export default function LanguageSwitcher({ currentLocale, currentPath }: Props) { const [open, setOpen] = useState(false) + // `currentPath` is the server-rendered path; on pages that navigate + // client-side (the API reference pushes `/docs/api/<op>` URLs) it goes stale. + // Read the live URL when the menu opens so switching locale keeps the same + // endpoint instead of dropping back to the section root. + const [livePath, setLivePath] = useState(currentPath) const containerRef = useRef<HTMLDivElement>(null) useEffect(() => { if (!open) return + if (typeof window !== 'undefined') { + setLivePath(window.location.pathname + window.location.search) + } function handleMouseDown(e: MouseEvent) { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setOpen(false) @@ -102,7 +114,7 @@ export default function LanguageSwitcher({ currentLocale, currentPath }: Props) {LOCALES.map(({ value, label }) => ( <li key={value} role="option" aria-selected={value === currentLocale}> <a - href={buildUrl(currentLocale, value, currentPath)} + href={buildUrl(currentLocale, value, livePath)} onClick={() => setOpen(false)} className={ value === currentLocale diff --git a/src/components/shell/SearchDialog.tsx b/src/components/shell/SearchDialog.tsx index 2c7df5480..19e70208b 100644 --- a/src/components/shell/SearchDialog.tsx +++ b/src/components/shell/SearchDialog.tsx @@ -3,7 +3,7 @@ import { createPortal } from 'react-dom' import MiniSearch from 'minisearch' import type { Locale } from '@longbridge/openapi-utils' import { t } from '@longbridge/openapi-utils' -import SearchResults, { type SearchHit } from './SearchResults' +import SearchResults, { type SearchHit, type SearchType } from './SearchResults' interface Props { locale: Locale @@ -17,6 +17,8 @@ interface Section { title: string headings: string[] body: string + type: SearchType + slug: string } const DEBOUNCE_MS = 120 @@ -32,13 +34,17 @@ async function buildIndex(locale: Locale): Promise<MiniSearch<Section>> { // as a static file via _assets.conf. A root .json would fall to the catch-all, // be rewritten to `<path>/index.html`, and 404 — the "Search index failed to // load" seen on the deployed site. Mirrors the /assets asset-dir alignment. - const res = await fetch(`/assets/search-index.${locale}.json`) + // `cache: 'no-cache'` forces revalidation: the index URL is not + // content-hashed, so a stale copy (from a prior deploy or a mid-session + // rebuild) would otherwise be served from the HTTP cache until it expires, + // hiding freshly-indexed content. + const res = await fetch(`/assets/search-index.${locale}.json`, { cache: 'no-cache' }) if (!res.ok) throw new Error(`search-index ${locale} ${res.status}`) const { sections } = (await res.json()) as { sections: Section[] } const ms = new MiniSearch<Section>({ - fields: ['title', 'headingsJoined', 'body'], - storeFields: ['url', 'title', 'headings'], + fields: ['title', 'headingsJoined', 'slug', 'body'], + storeFields: ['url', 'title', 'headings', 'type'], tokenize: (text) => { const out: string[] = [] let buf = '' @@ -60,10 +66,10 @@ async function buildIndex(locale: Locale): Promise<MiniSearch<Section>> { // For non-virtual fields, hand back the raw value. Returning a // stringified array here corrupts storeFields — `headings` came back // as "a,b,c" instead of ["a","b","c"] and the UI's .map() blew up. - return (doc as unknown as Record<string, unknown>)[field] as string + return ((doc as unknown as Record<string, unknown>)[field] as string) ?? '' }, searchOptions: { - boost: { title: 3, headingsJoined: 2, body: 1 }, + boost: { title: 3, headingsJoined: 2, slug: 2, body: 1 }, fuzzy: 0.15, prefix: true, }, @@ -206,11 +212,22 @@ export default function SearchDialog({ locale, isOpen, onClose }: Props) { setLoading(true) try { const raw = ms.search(trimmed) - const hits: SearchHit[] = raw.slice(0, MAX_RESULTS).map((r) => ({ + // Collapse to one hit per page: a doc is sliced into many heading + // sections (…#parameters, …#response, …), and without this a single + // page floods every result slot — burying the API / CLI / MCP pages + // for the same topic. Keep the highest-ranked section per base URL + // (the #hash still deep-links to that section). + const byPage = new Map<string, (typeof raw)[number]>() + for (const r of raw) { + const base = (r.url as string).split('#')[0] + if (!byPage.has(base)) byPage.set(base, r) + } + const hits: SearchHit[] = [...byPage.values()].slice(0, MAX_RESULTS).map((r) => ({ id: String(r.id), url: r.url as string, title: r.title as string, headings: r.headings as string[], + type: (r.type as SearchType) ?? 'docs', matchedTerms: r.terms ?? [], })) setResults(hits) diff --git a/src/components/shell/SearchResults.tsx b/src/components/shell/SearchResults.tsx index 0ade13c16..8c1bf6769 100644 --- a/src/components/shell/SearchResults.tsx +++ b/src/components/shell/SearchResults.tsx @@ -1,14 +1,30 @@ import type { Locale } from '@longbridge/openapi-utils' import { t } from '@longbridge/openapi-utils' +export type SearchType = 'api' | 'cli' | 'mcp' | 'docs' + export interface SearchHit { id: string url: string title: string headings: string[] + type: SearchType matchedTerms: string[] } +const TYPE_LABEL: Record<SearchType, Record<Locale, string>> = { + api: { en: 'API', 'zh-CN': 'API', 'zh-HK': 'API' }, + cli: { en: 'CLI', 'zh-CN': 'CLI', 'zh-HK': 'CLI' }, + mcp: { en: 'MCP', 'zh-CN': 'MCP', 'zh-HK': 'MCP' }, + docs: { en: 'Docs', 'zh-CN': '文档', 'zh-HK': '文檔' }, +} +const TYPE_STYLE: Record<SearchType, { background: string; color: string }> = { + api: { background: 'rgba(0,184,184,0.12)', color: '#00807f' }, + cli: { background: 'rgba(90,116,255,0.12)', color: '#4a5fd0' }, + mcp: { background: 'rgba(255,145,40,0.14)', color: '#b45309' }, + docs: { background: 'var(--lb-bg-2)', color: 'var(--lb-fg-2)' }, +} + interface Props { results: SearchHit[] loading: boolean @@ -73,6 +89,12 @@ export default function SearchResults({ </span> ))} </span> + <span + className="ml-auto shrink-0 text-[11px] font-semibold leading-none px-1.5 py-1 rounded" + style={TYPE_STYLE[hit.type]} + > + {TYPE_LABEL[hit.type][locale]} + </span> </button> </li> ))} diff --git a/src/data/locale.en.ts b/src/data/locale.en.ts index 4874ccda1..640aeb632 100644 --- a/src/data/locale.en.ts +++ b/src/data/locale.en.ts @@ -46,16 +46,16 @@ export const locale = { 'footer.about': 'About', 'footer.rights': '© {year} Longbridge. All rights reserved.', // API Reference - 'api.search': 'Search...', + 'api.search': 'Search…', 'api.sections.authorizations': 'Authorization', - 'api.sections.pathParams': 'Path Parameters', - 'api.sections.queryParams': 'Query Parameters', - 'api.sections.body': 'Request Body', + 'api.sections.pathParams': 'Path parameters', + 'api.sections.queryParams': 'Query parameters', + 'api.sections.body': 'Request body', 'api.sections.response': 'Response', 'api.code.request': 'Request', 'api.code.response': 'Response', 'api.copy': 'Copy', - 'api.copied': 'Copied!', + 'api.copied': 'Copied', 'api.intro.title': 'API Reference', 'api.intro.desc': 'Explore all available REST and WebSocket endpoints. Use the sidebar to browse endpoints by category.', 'api.intro.httpTitle': 'REST API', @@ -65,6 +65,6 @@ export const locale = { 'api.intro.hint': 'Click any endpoint in the sidebar to view parameters, code samples, and example responses.', 'api.param.required': 'Required', 'api.param.optional': 'Optional', - 'api.fallback': 'No request body fields defined in this spec.', + 'api.fallback': 'This endpoint takes no request parameters.', 'api.pathCopy': 'Copy path', } as const diff --git a/src/data/locale.zh-CN.ts b/src/data/locale.zh-CN.ts index 8297f5792..1d160cae7 100644 --- a/src/data/locale.zh-CN.ts +++ b/src/data/locale.zh-CN.ts @@ -45,7 +45,7 @@ export const locale = { 'footer.about': '关于我们', 'footer.rights': '© {year} Longbridge. 版权所有。', // API Reference - 'api.search': '搜索...', + 'api.search': '搜索…', 'api.sections.authorizations': '鉴权', 'api.sections.pathParams': '路径参数', 'api.sections.queryParams': '查询参数', @@ -54,7 +54,7 @@ export const locale = { 'api.code.request': '请求', 'api.code.response': '响应', 'api.copy': '复制', - 'api.copied': '已复制!', + 'api.copied': '已复制', 'api.intro.title': 'API 参考', 'api.intro.desc': '浏览所有可用的 REST 和 WebSocket 接口。通过左侧侧边栏按分类查找接口。', 'api.intro.httpTitle': 'REST API', @@ -64,6 +64,6 @@ export const locale = { 'api.intro.hint': '点击侧边栏中的任意接口,查看参数说明、代码示例和响应示例。', 'api.param.required': '必填', 'api.param.optional': '可选', - 'api.fallback': '本规范中未定义请求体字段。', + 'api.fallback': '此接口无需请求参数。', 'api.pathCopy': '复制路径', } as const diff --git a/src/data/locale.zh-HK.ts b/src/data/locale.zh-HK.ts index 8653a6f69..eb3a324fe 100644 --- a/src/data/locale.zh-HK.ts +++ b/src/data/locale.zh-HK.ts @@ -4,8 +4,8 @@ export const locale = { // Nav 'nav.features': '功能', 'nav.pricing': '定價', - 'nav.docs': '文件', - 'nav.searchDocs': '搜尋文件…', + 'nav.docs': '文檔', + 'nav.searchDocs': '搜尋文檔…', 'nav.getStarted': '立即開始', 'nav.dashboard': '控制台', 'nav.connectAi': '連接 AI', @@ -16,7 +16,7 @@ export const locale = { 'nav.theme.dark': '深色', 'nav.theme.system': '跟隨系統', // Search - 'search.placeholder': '搜尋文件…', + 'search.placeholder': '搜尋文檔…', 'search.button': '搜尋', 'search.empty': '無法找到相關結果', // Sidebar (not in legacy JSON — using defaults) @@ -45,25 +45,25 @@ export const locale = { 'footer.about': '關於我們', 'footer.rights': '© {year} Longbridge. 版權所有。', // API Reference - 'api.search': '搜尋...', + 'api.search': '搜尋…', 'api.sections.authorizations': '鑑權', 'api.sections.pathParams': '路徑參數', 'api.sections.queryParams': '查詢參數', 'api.sections.body': '請求體', - 'api.sections.response': '回應', + 'api.sections.response': '響應', 'api.code.request': '請求', - 'api.code.response': '回應', + 'api.code.response': '響應', 'api.copy': '複製', - 'api.copied': '已複製!', + 'api.copied': '已複製', 'api.intro.title': 'API 參考', 'api.intro.desc': '瀏覽所有可用的 REST 和 WebSocket 介面。透過左側側邊欄按分類查找介面。', 'api.intro.httpTitle': 'REST API', - 'api.intro.httpDesc': '無狀態請求/回應模型,適用於下單、帳戶資料及一次性查詢。', + 'api.intro.httpDesc': '無狀態請求/響應模型,適用於下單、帳戶資料及一次性查詢。', 'api.intro.wsTitle': 'WebSocket', 'api.intro.wsDesc': '持久連線,即時推送。適用於行情訂閱和訂單狀態更新。', - 'api.intro.hint': '點選側邊欄中的任意介面,查看參數說明、程式碼範例和回應範例。', + 'api.intro.hint': '點選側邊欄中的任意介面,查看參數說明、程式碼範例和響應範例。', 'api.param.required': '必填', 'api.param.optional': '可選', - 'api.fallback': '本規範中未定義請求體欄位。', + 'api.fallback': '此接口無需請求參數。', 'api.pathCopy': '複製路徑', } as const diff --git a/src/data/nav.en.ts b/src/data/nav.en.ts index 8734807c8..3034fd0ab 100644 --- a/src/data/nav.en.ts +++ b/src/data/nav.en.ts @@ -11,4 +11,5 @@ export const nav: NavItem[] = [ { text: 'CLI', link: '/docs/cli', activeMatch: '^(/en)?/docs/cli' }, { text: 'MCP', link: '/docs/mcp', activeMatch: '^(/en)?/docs/mcp' }, { text: 'Docs', link: '/docs', activeMatch: '^(/en)?/docs(?!/cli)(?!/api)(?!/mcp)' }, + { text: 'Reference', link: '/docs/api', activeMatch: '^(/en)?/docs/api' }, ] diff --git a/src/data/nav.zh-CN.ts b/src/data/nav.zh-CN.ts index a7627028c..ae8d7f56c 100644 --- a/src/data/nav.zh-CN.ts +++ b/src/data/nav.zh-CN.ts @@ -6,4 +6,5 @@ export const nav: NavItem[] = [ { text: 'CLI', link: '/zh-CN/docs/cli', activeMatch: '^/zh-CN/docs/cli' }, { text: 'MCP', link: '/zh-CN/docs/mcp', activeMatch: '^/zh-CN/docs/mcp' }, { text: '文档', link: '/zh-CN/docs', activeMatch: '^/zh-CN/docs(?!/cli)(?!/api)(?!/mcp)' }, + { text: 'API 文档', link: '/zh-CN/docs/api', activeMatch: '^/zh-CN/docs/api' }, ] diff --git a/src/data/nav.zh-HK.ts b/src/data/nav.zh-HK.ts index fee8afe13..10e16eb6a 100644 --- a/src/data/nav.zh-HK.ts +++ b/src/data/nav.zh-HK.ts @@ -5,5 +5,6 @@ export const nav: NavItem[] = [ { text: 'Skill', link: '/zh-HK/skill', activeMatch: '^/zh-HK/skill' }, { text: 'CLI', link: '/zh-HK/docs/cli', activeMatch: '^/zh-HK/docs/cli' }, { text: 'MCP', link: '/zh-HK/docs/mcp', activeMatch: '^/zh-HK/docs/mcp' }, - { text: '文件', link: '/zh-HK/docs', activeMatch: '^/zh-HK/docs(?!/cli)(?!/api)(?!/mcp)' }, + { text: '文檔', link: '/zh-HK/docs', activeMatch: '^/zh-HK/docs(?!/cli)(?!/api)(?!/mcp)' }, + { text: 'API 文檔', link: '/zh-HK/docs/api', activeMatch: '^/zh-HK/docs/api' }, ] diff --git a/src/layouts/ApiReferenceLayout.astro b/src/layouts/ApiReferenceLayout.astro index e429c8450..112edc764 100644 --- a/src/layouts/ApiReferenceLayout.astro +++ b/src/layouts/ApiReferenceLayout.astro @@ -15,6 +15,6 @@ export interface Props { const { title, description = '', locale = 'en' } = Astro.props --- -<BaseLayout title={title} description={description} locale={locale}> +<BaseLayout title={title} description={description} locale={locale} hideFooter> <ApiReference client:load rawYaml={rawYaml} locale={locale} /> </BaseLayout> diff --git a/src/pages/[...slug].md.ts b/src/pages/[...slug].md.ts index 87e58b51a..53a681e23 100644 --- a/src/pages/[...slug].md.ts +++ b/src/pages/[...slug].md.ts @@ -10,6 +10,8 @@ export async function getStaticPaths() { const all = await getCollection('docs') return all .filter((entry) => resolveLocale(entry) === 'en') + // /docs/api.md is served from openapi.yaml by src/pages/docs/api.md.ts. + .filter((entry) => !resolveUrl(entry).endsWith('/docs/api')) .map((entry) => { const url = resolveUrl(entry) // e.g. /docs/trade/grid/list const slug = url === '/' ? undefined : url.replace(/^\//, '') diff --git a/src/pages/[locale]/[...slug].md.ts b/src/pages/[locale]/[...slug].md.ts index f897f2dc4..83589cb64 100644 --- a/src/pages/[locale]/[...slug].md.ts +++ b/src/pages/[locale]/[...slug].md.ts @@ -4,7 +4,13 @@ import { resolveUrl, resolveLocale } from '@longbridge/openapi-utils' export async function getStaticPaths() { const all = await getCollection('docs') - return all.map((entry) => { + return all + // en is the root locale — its markdown is served at /docs/*.md by the + // top-level [...slug].md.ts; emitting /en/docs/*.md here would just duplicate it. + .filter((entry) => resolveLocale(entry) !== 'en') + // /<locale>/docs/api.md is served from openapi.yaml by [locale]/docs/api.md.ts. + .filter((entry) => !resolveUrl(entry).endsWith('/docs/api')) + .map((entry) => { const url = resolveUrl(entry) const locale = resolveLocale(entry) const bareUrl = locale === 'en' ? url : url.replace(new RegExp(`^/${locale}`), '') diff --git a/src/pages/[locale]/docs/api.md.ts b/src/pages/[locale]/docs/api.md.ts new file mode 100644 index 000000000..b0ac72653 --- /dev/null +++ b/src/pages/[locale]/docs/api.md.ts @@ -0,0 +1,13 @@ +import type { APIRoute } from 'astro' +import type { Locale } from '@longbridge/openapi-utils' +import { referenceMarkdown } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../../../openapi.yaml?raw' + +export function getStaticPaths() { + return [{ params: { locale: 'zh-CN' } }, { params: { locale: 'zh-HK' } }] +} + +export const GET: APIRoute = ({ params }) => + new Response(referenceMarkdown(rawYaml, params.locale as Locale), { + headers: { 'Content-Type': 'text/markdown; charset=utf-8' }, + }) diff --git a/src/pages/[locale]/docs/api/[op].astro b/src/pages/[locale]/docs/api/[op].astro new file mode 100644 index 000000000..d7e80d3c3 --- /dev/null +++ b/src/pages/[locale]/docs/api/[op].astro @@ -0,0 +1,27 @@ +--- +// Localized path-based route: /<locale>/docs/api/<operationId>. +import ApiReferenceLayout from '@/layouts/ApiReferenceLayout.astro' +import type { Locale } from '@longbridge/openapi-utils' +import { endpointList, wsCommandList } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../../../../openapi.yaml?raw' + +export function getStaticPaths() { + const ops = endpointList(rawYaml) + const ws = wsCommandList(rawYaml) + return (['zh-CN', 'zh-HK'] as const).flatMap((locale) => [ + ...ops.map((e) => ({ + params: { locale, op: e.operationId }, + props: { summary: e.summary }, + })), + ...ws.map((w) => ({ + params: { locale, op: w.id }, + props: { summary: w.name }, + })), + ]) +} + +const { summary } = Astro.props as { summary: string } +const locale = Astro.params.locale as Locale +--- + +<ApiReferenceLayout title={`${summary} · API Reference`} locale={locale} /> diff --git a/src/pages/[locale]/docs/api/[op].md.ts b/src/pages/[locale]/docs/api/[op].md.ts new file mode 100644 index 000000000..07acab08c --- /dev/null +++ b/src/pages/[locale]/docs/api/[op].md.ts @@ -0,0 +1,22 @@ +import type { APIRoute } from 'astro' +import type { Locale } from '@longbridge/openapi-utils' +import { endpointList, endpointMarkdownById, wsCommandList, wsCommandMarkdownById } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../../../../openapi.yaml?raw' + +export function getStaticPaths() { + const ops = endpointList(rawYaml) + const ws = wsCommandList(rawYaml) + return (['zh-CN', 'zh-HK'] as const).flatMap((locale) => [ + ...ops.map((e) => ({ params: { locale, op: e.operationId } })), + ...ws.map((w) => ({ params: { locale, op: w.id } })), + ]) +} + +export const GET: APIRoute = ({ params }) => { + const op = String(params.op) + const locale = params.locale as Locale + const md = endpointMarkdownById(rawYaml, op, locale) ?? wsCommandMarkdownById(rawYaml, op, locale) + return md + ? new Response(md, { headers: { 'Content-Type': 'text/markdown; charset=utf-8' } }) + : new Response('Not found', { status: 404 }) +} diff --git a/src/pages/assets/search-index.[locale].json.ts b/src/pages/assets/search-index.[locale].json.ts index 94146d5ae..d10da12f2 100644 --- a/src/pages/assets/search-index.[locale].json.ts +++ b/src/pages/assets/search-index.[locale].json.ts @@ -14,6 +14,8 @@ import type { APIRoute } from 'astro' import type { CollectionEntry } from 'astro:content' import { getCollection, render } from 'astro:content' import { resolveUrl, resolveLocale, currentRegion, includedInRegion } from '@longbridge/openapi-utils' +import { parseSpec, pickLocale, epId } from '@longbridge/openapi-api-reference' +import rawApiYaml from '../../../openapi.yaml?raw' export function getStaticPaths() { return [ @@ -23,12 +25,38 @@ export function getStaticPaths() { ] } +/** Result category, so the header search can label API / CLI / MCP / Docs hits. + * (SDK usage lives inline in guide pages, so it folds into 'docs'.) */ +export type SearchType = 'api' | 'cli' | 'mcp' | 'docs' + interface Section { id: string url: string title: string headings: string[] body: string + type: SearchType + /** Searchable slug — the endpoint/page/ws id, or a doc URL's last path + * segment — so typing part of the URL finds the page. */ + slug: string +} + +/** Last meaningful path segment of a URL (drops #hash and query), decoded. */ +function urlSlug(url: string): string { + const path = url.split(/[#?]/)[0] + const seg = path.split('/').filter(Boolean).pop() ?? '' + try { + return decodeURIComponent(seg) + } catch { + return seg + } +} + +/** Category of a docs-collection entry, derived from its URL path. */ +function docType(url: string): SearchType { + if (url.includes('/docs/cli')) return 'cli' + if (url.includes('/docs/mcp')) return 'mcp' + return 'docs' } const MAX_SECTION_BODY = 2000 @@ -78,6 +106,8 @@ export const GET: APIRoute = async ({ params }) => { title: docTitle, headings: path, body, + type: docType(url), + slug: urlSlug(href), }) } @@ -99,11 +129,105 @@ export const GET: APIRoute = async ({ params }) => { if (sawFirstHeading || buf.some((l) => l.trim())) flush() } + // ── API Reference (openapi.yaml): endpoints, static pages, WebSocket cmds ── + // The reference is client-rendered from openapi.yaml, so its content is absent + // from the built HTML — index it here so the header search can find it. + try { + const prefix = locale === 'en' ? '' : `/${locale}` + const { groups, pages, wsGroups } = parseSpec(rawApiYaml) + for (const g of groups) { + const gname = pickLocale(g.name, g.nameZh, g.nameZhHk, locale) + const eps = [...g.endpoints, ...g.subgroups.flatMap((sg) => sg.endpoints)] + for (const ep of eps) { + const title = pickLocale( + ep.operation.summary, + ep.operation['x-summary-zh'], + ep.operation['x-summary-zh-hk'], + locale + ) + const desc = pickLocale( + ep.operation.description, + ep.operation['x-description-zh'], + ep.operation['x-description-zh-hk'], + locale + ) + // Fold parameter and response-field names into the body so an endpoint + // is findable by the fields it takes/returns, not just its prose. + const fieldNames = [ + ...(ep.operation['x-parameters'] ?? ep.operation.parameters ?? []), + ...(ep.operation['x-response-properties'] ?? []), + ] + .map((p) => p.name) + .filter(Boolean) + .join(' ') + const body = `${stripMarkdown(desc || '')} ${fieldNames}`.trim().slice(0, MAX_SECTION_BODY) + sections.push({ + id: `api::${epId(ep)}`, + url: `${prefix}/docs/api/${epId(ep)}`, + title: title || epId(ep), + headings: [gname, title || epId(ep)].filter(Boolean), + body, + type: 'api', + slug: epId(ep), + }) + } + // WebSocket commands merged into this tag's subgroups (path-based URL, same + // canonical form as endpoints). Without this the merged WS commands — which + // is all of them — would be missing from search entirely. + for (const c of g.subgroups.flatMap((sg) => sg.wsCommands ?? [])) { + const title = pickLocale(c.name, c.nameZh, c.nameZhHk, locale) + const desc = pickLocale(c.description, c.descriptionZh, c.descriptionZhHk, locale) + sections.push({ + id: `api-ws::${c.id}`, + url: `${prefix}/docs/api/${c.id}`, + title: title || c.id, + headings: [gname, title || c.id].filter(Boolean), + body: stripMarkdown(desc || '').slice(0, MAX_SECTION_BODY), + type: 'api', + slug: c.id, + }) + } + } + for (const p of pages) { + const title = pickLocale(p.title, p.titleZh, p.titleZhHk, locale) + const content = pickLocale(p.content, p.contentZh, p.contentZhHk, locale) + sections.push({ + id: `api-page::${p.id}`, + url: `${prefix}/docs/api?page=${p.id}`, + title: title || p.id, + headings: [title || p.id], + body: stripMarkdown(content || '').slice(0, MAX_SECTION_BODY), + type: 'api', + slug: p.id, + }) + } + for (const wg of wsGroups) { + const gname = pickLocale(wg.name, wg.nameZh, wg.nameZhHk, locale) + for (const c of wg.commands) { + const title = pickLocale(c.name, c.nameZh, c.nameZhHk, locale) + const desc = pickLocale(c.description, c.descriptionZh, c.descriptionZhHk, locale) + sections.push({ + id: `api-ws::${c.id}`, + url: `${prefix}/docs/api/${c.id}`, + title: title || c.id, + headings: [gname, title || c.id].filter(Boolean), + body: stripMarkdown(desc || '').slice(0, MAX_SECTION_BODY), + type: 'api', + slug: c.id, + }) + } + } + } catch (err) { + console.error('search-index: failed to index API reference', err) + } + return new Response(JSON.stringify({ locale, sections }, null, 0), { headers: { 'Content-Type': 'application/json; charset=utf-8', - // Long cache — index is content-hash-invalidated by build - 'Cache-Control': 'public, max-age=3600', + // The URL is not content-hashed, so it must not be cached hard: a long + // max-age would serve a stale index (missing newly-indexed pages/fields) + // for up to an hour after a deploy. `no-cache` = store but revalidate. + 'Cache-Control': 'no-cache', }, }) } diff --git a/src/pages/docs/api.md.ts b/src/pages/docs/api.md.ts new file mode 100644 index 000000000..db1247bbd --- /dev/null +++ b/src/pages/docs/api.md.ts @@ -0,0 +1,8 @@ +import type { APIRoute } from 'astro' +import { referenceMarkdown } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../../openapi.yaml?raw' + +export const GET: APIRoute = () => + new Response(referenceMarkdown(rawYaml, 'en'), { + headers: { 'Content-Type': 'text/markdown; charset=utf-8' }, + }) diff --git a/src/pages/docs/api/[op].astro b/src/pages/docs/api/[op].astro new file mode 100644 index 000000000..b2251c37e --- /dev/null +++ b/src/pages/docs/api/[op].astro @@ -0,0 +1,25 @@ +--- +// Path-based route for a single endpoint: /docs/api/<operationId>. +// Renders the same interactive reference as /docs/api; the React app reads the +// operationId from the pathname on hydration and focuses that endpoint. +import ApiReferenceLayout from '@/layouts/ApiReferenceLayout.astro' +import { endpointList, wsCommandList } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../../../openapi.yaml?raw' + +export function getStaticPaths() { + return [ + ...endpointList(rawYaml).map((e) => ({ + params: { op: e.operationId }, + props: { summary: e.summary }, + })), + ...wsCommandList(rawYaml).map((w) => ({ + params: { op: w.id }, + props: { summary: w.name }, + })), + ] +} + +const { summary } = Astro.props as { summary: string } +--- + +<ApiReferenceLayout title={`${summary} · API Reference`} locale="en" /> diff --git a/src/pages/docs/api/[op].md.ts b/src/pages/docs/api/[op].md.ts new file mode 100644 index 000000000..691665047 --- /dev/null +++ b/src/pages/docs/api/[op].md.ts @@ -0,0 +1,18 @@ +import type { APIRoute } from 'astro' +import { endpointList, endpointMarkdownById, wsCommandList, wsCommandMarkdownById } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../../../openapi.yaml?raw' + +export function getStaticPaths() { + return [ + ...endpointList(rawYaml).map((e) => ({ params: { op: e.operationId } })), + ...wsCommandList(rawYaml).map((w) => ({ params: { op: w.id } })), + ] +} + +export const GET: APIRoute = ({ params }) => { + const op = String(params.op) + const md = endpointMarkdownById(rawYaml, op, 'en') ?? wsCommandMarkdownById(rawYaml, op, 'en') + return md + ? new Response(md, { headers: { 'Content-Type': 'text/markdown; charset=utf-8' } }) + : new Response('Not found', { status: 404 }) +} diff --git a/src/pages/llms-full.txt.ts b/src/pages/llms-full.txt.ts index 297601dde..a34000849 100644 --- a/src/pages/llms-full.txt.ts +++ b/src/pages/llms-full.txt.ts @@ -1,20 +1,27 @@ import type { APIRoute } from 'astro' import { getCollection } from 'astro:content' import { resolveUrl, resolveLocale } from '@longbridge/openapi-utils' +import { referenceMarkdown } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../openapi.yaml?raw' export const GET: APIRoute = async ({ site }) => { const all = await getCollection('docs') const enEntries = all.filter((e) => resolveLocale(e) === 'en') const header = '# Longbridge Developers\n' - const sections = enEntries.map((entry) => { - const url = resolveUrl(entry) - const title = entry.data.title ?? url - const absUrl = `${site}${url.replace(/^\//, '')}` - const body = entry.body ?? '' - return `# ${title}\nURL: ${absUrl}\n\n${body}` - }) + const sections = enEntries + .filter((entry) => !resolveUrl(entry).endsWith('/docs/api')) + .map((entry) => { + const url = resolveUrl(entry) + const title = entry.data.title ?? url + const absUrl = `${site}${url.replace(/^\//, '')}` + const body = entry.body ?? '' + return `# ${title}\nURL: ${absUrl}\n\n${body}` + }) - const output = header + '\n\n---\n' + sections.join('\n\n---\n') + '\n' + // Full API reference (all endpoints, generated from openapi.yaml). + const apiSection = `URL: ${site}docs/api.md\n\n${referenceMarkdown(rawYaml, 'en')}` + + const output = header + '\n\n---\n' + sections.join('\n\n---\n') + '\n\n---\n' + apiSection + '\n' return new Response(output, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }) } diff --git a/src/pages/llms.txt.ts b/src/pages/llms.txt.ts index 3aab1ec0b..1b73d51e5 100644 --- a/src/pages/llms.txt.ts +++ b/src/pages/llms.txt.ts @@ -1,18 +1,38 @@ import type { APIRoute } from 'astro' import { getCollection } from 'astro:content' import { resolveUrl, resolveLocale } from '@longbridge/openapi-utils' +import { endpointList, wsCommandList } from '@longbridge/openapi-api-reference/markdown' +import rawYaml from '../../openapi.yaml?raw' export const GET: APIRoute = async ({ site }) => { const all = await getCollection('docs') const enEntries = all.filter((e) => resolveLocale(e) === 'en') - const lines = enEntries.map((entry) => { - const url = resolveUrl(entry) - const title = entry.data.title ?? url - return `- [${title}](${site}${url.replace(/^\//, '')})` - }) + const lines = enEntries + .filter((entry) => !resolveUrl(entry).endsWith('/docs/api')) + .map((entry) => { + const url = resolveUrl(entry) + const title = entry.data.title ?? url + return `- [${title}](${site}${url.replace(/^\//, '')})` + }) - const header = '# Longbridge Developers\n\n## Docs\n\n' - const body = header + lines.join('\n') + '\n' + // API Reference — one machine-readable .md per endpoint … + const apiLines = endpointList(rawYaml).map( + (e) => `- [${e.summary}](${site}docs/api/${e.operationId}.md): ${e.method} ${e.path}` + ) + // … and per WebSocket command. + const wsLines = wsCommandList(rawYaml).map( + (w) => `- [${w.name}](${site}docs/api/${w.id}.md): WS ${w.direction}${w.cmd != null ? ` cmd ${w.cmd}` : ''}` + ) + + const body = + '# Longbridge Developers\n\n## Docs\n\n' + + lines.join('\n') + + '\n\n## API Reference\n\nFull reference: ' + + `${site}docs/api.md\n\n` + + apiLines.join('\n') + + '\n\n### WebSocket\n\n' + + wsLines.join('\n') + + '\n' return new Response(body, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }) } diff --git a/src/styles/docs.css b/src/styles/docs.css index febbc413e..9b908e41a 100644 --- a/src/styles/docs.css +++ b/src/styles/docs.css @@ -289,7 +289,9 @@ line-height: 1.7; } html:not([data-mode="dark"]) .docs-content pre.astro-code { - background: var(--lb-bg-2) !important; + /* White code surface with a hairline so it stays legible on the white page. */ + background: var(--lb-bg-1) !important; + border: 1px solid var(--lb-stroke); } .docs-content pre code { background: transparent;