From 4bdf4e38d32eb4fde4e337023d006fe6760f33cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Thu, 8 Oct 2026 17:45:09 +0800 Subject: [PATCH] docs(forex): add currency-exchange channel docs (SDK reference + CLI) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hand-written docs under docs/{en,zh-CN,zh-HK}/docs/: - forex/ — SDK-style API reference (overview + quote + submit + query), mirroring the trade channel format: 7-language request examples, request/response field tables, and error codes 603001-603010. - cli/forex/ — terminal usage for the three-step quote -> submit -> query flow. All three locales (en / zh-CN / zh-HK) kept in sync. --- docs/en/docs/cli/forex/_category_.json | 1 + docs/en/docs/cli/forex/exchange.mdx | 71 ++++++ docs/en/docs/forex/_category_.json | 8 + docs/en/docs/forex/overview.mdx | 57 +++++ docs/en/docs/forex/query.mdx | 269 ++++++++++++++++++++ docs/en/docs/forex/quote.mdx | 286 ++++++++++++++++++++++ docs/en/docs/forex/submit.mdx | 270 ++++++++++++++++++++ docs/zh-CN/docs/cli/forex/_category_.json | 1 + docs/zh-CN/docs/cli/forex/exchange.mdx | 71 ++++++ docs/zh-CN/docs/forex/_category_.json | 8 + docs/zh-CN/docs/forex/overview.mdx | 50 ++++ docs/zh-CN/docs/forex/query.mdx | 266 ++++++++++++++++++++ docs/zh-CN/docs/forex/quote.mdx | 284 +++++++++++++++++++++ docs/zh-CN/docs/forex/submit.mdx | 262 ++++++++++++++++++++ docs/zh-HK/docs/cli/forex/_category_.json | 1 + docs/zh-HK/docs/cli/forex/exchange.mdx | 71 ++++++ docs/zh-HK/docs/forex/_category_.json | 8 + docs/zh-HK/docs/forex/overview.mdx | 50 ++++ docs/zh-HK/docs/forex/query.mdx | 266 ++++++++++++++++++++ docs/zh-HK/docs/forex/quote.mdx | 284 +++++++++++++++++++++ docs/zh-HK/docs/forex/submit.mdx | 262 ++++++++++++++++++++ 21 files changed, 2846 insertions(+) create mode 100644 docs/en/docs/cli/forex/_category_.json create mode 100644 docs/en/docs/cli/forex/exchange.mdx create mode 100644 docs/en/docs/forex/_category_.json create mode 100644 docs/en/docs/forex/overview.mdx create mode 100644 docs/en/docs/forex/query.mdx create mode 100644 docs/en/docs/forex/quote.mdx create mode 100644 docs/en/docs/forex/submit.mdx create mode 100644 docs/zh-CN/docs/cli/forex/_category_.json create mode 100644 docs/zh-CN/docs/cli/forex/exchange.mdx create mode 100644 docs/zh-CN/docs/forex/_category_.json create mode 100644 docs/zh-CN/docs/forex/overview.mdx create mode 100644 docs/zh-CN/docs/forex/query.mdx create mode 100644 docs/zh-CN/docs/forex/quote.mdx create mode 100644 docs/zh-CN/docs/forex/submit.mdx create mode 100644 docs/zh-HK/docs/cli/forex/_category_.json create mode 100644 docs/zh-HK/docs/cli/forex/exchange.mdx create mode 100644 docs/zh-HK/docs/forex/_category_.json create mode 100644 docs/zh-HK/docs/forex/overview.mdx create mode 100644 docs/zh-HK/docs/forex/query.mdx create mode 100644 docs/zh-HK/docs/forex/quote.mdx create mode 100644 docs/zh-HK/docs/forex/submit.mdx diff --git a/docs/en/docs/cli/forex/_category_.json b/docs/en/docs/cli/forex/_category_.json new file mode 100644 index 00000000..45ab470d --- /dev/null +++ b/docs/en/docs/cli/forex/_category_.json @@ -0,0 +1 @@ +{ "label": "Forex", "icon": "arrow-left-right", "position": 12, "collapsed": true } diff --git a/docs/en/docs/cli/forex/exchange.mdx b/docs/en/docs/cli/forex/exchange.mdx new file mode 100644 index 00000000..e6f79288 --- /dev/null +++ b/docs/en/docs/cli/forex/exchange.mdx @@ -0,0 +1,71 @@ +--- +title: 'currency exchange' +sidebar_label: 'currency exchange' +sidebar_position: 1 +--- + +# longbridge forex + +Convert cash between two currencies inside your account, right from the terminal. The flow is three steps and asynchronous: **quote** a rate, **submit** the conversion against that quote, then **query** the order until it settles. + +These commands act on your account, so they require login; `submit-order` moves real money and prompts for confirmation unless you pass `--yes`. + +## Basic Usage + +```bash +# Lock a rate and get a quote_id (give --amount or --target-amount) +longbridge forex quote USD HKD --amount 1000 +``` + +``` +| Field | Value | +|-----------|------------------| +| quote_id | Q20260925000123 | +| rate | 7.7812 | +| ccy_pair | USD/HKD | +| expire_at | 1790000000000 | +``` + +## Examples + +### Quote a rate + +```bash +# By the convert-out amount (the USD you spend) +longbridge forex quote USD HKD --amount 1000 +# …or by the convert-in amount (the HKD you want to receive) +longbridge forex quote USD HKD --target-amount 7800 +``` + +`rate` is quoted on the standard currency pair (`1 BASE = rate QUOTE`, e.g. `USD/HKD`), independent of the direction you ask for. The quote is bound to you and valid for a short window (`expire_at` is a Unix-millisecond timestamp). + +### Submit a conversion + +```bash +# Submit against the quote_id, with your own unique client order id +longbridge forex submit-order Q20260925000123 my-order-0001 +# Skip the confirmation prompt (e.g. for scripting) +longbridge forex submit-order Q20260925000123 my-order-0001 --yes +``` + +Submitting only means the order was **accepted** — the conversion completes asynchronously, so the command re-queries and prints the current order state. Reusing the same `client_order_id` never places a second order; on a timeout, query first and only re-quote + resubmit with the **same** id. + +### Query an order + +```bash +longbridge forex order my-order-0001 +``` + +``` +| Field | Value | +|-------------|---------| +| state | 1 | +| rate | 7.7812 | +| from_amount | 1000 | +| to_amount | 7781.2 | +| fail_reason | | +``` + +`state` is `0` processing (keep polling), `1` success, or `2` failed (frozen funds are returned). Poll no more often than every 2 seconds; most orders settle within a minute. + +> Add `--format json` to any command for the full machine-readable payload. diff --git a/docs/en/docs/forex/_category_.json b/docs/en/docs/forex/_category_.json new file mode 100644 index 00000000..3fee7225 --- /dev/null +++ b/docs/en/docs/forex/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Forex", + "collapsible": true, + "collapsed": true, + "position": 4.6, + "link": null, + "icon": "arrow-left-right" +} diff --git a/docs/en/docs/forex/overview.mdx b/docs/en/docs/forex/overview.mdx new file mode 100644 index 00000000..921b3bbb --- /dev/null +++ b/docs/en/docs/forex/overview.mdx @@ -0,0 +1,57 @@ +--- +id: forex_overview +title: Overview +slug: forex-overview +sidebar_position: 1 +--- + +# Forex Overview + +The forex (currency exchange) channel lets you convert a balance from one +currency into another. Conversion is asynchronous and follows three steps: + +1. **Get Quote** — call [Get Quote](./quote) to lock in an execution rate and + obtain a `quote_id`. The quote is bound to you and expires after a short + window. +2. **Submit Order** — call [Submit Order](./submit) with that `quote_id` and + your own `client_order_id` to place the conversion. Acceptance is + acknowledged synchronously, but the conversion itself is processed + asynchronously. +3. **Query Order** — poll [Query Order](./query) with the same + `client_order_id` until the order reaches a terminal state (success or + failed). + + + + + + + + + + + + + + + + + + + + +
TypeIntroduction
ForexGet Quote
Submit Order
Query Order
+ +## Error Codes + +| Code | Description | +| ------ | --------------------------------------------------------------------------- | +| 603001 | Parameter error | +| 603002 | Unsupported currency pair | +| 603004 | Quote not found | +| 603005 | Quote expired (re-quote) | +| 603006 | Quote already used (re-quote) | +| 603007 | Insufficient convertible balance | +| 603008 | Unauthorized / no exchange permission / outside service hours | +| 603009 | Order not found | +| 603010 | Service temporarily unavailable, retry later | diff --git a/docs/en/docs/forex/query.mdx b/docs/en/docs/forex/query.mdx new file mode 100644 index 00000000..2ee5c864 --- /dev/null +++ b/docs/en/docs/forex/query.mdx @@ -0,0 +1,269 @@ +--- +slug: query +sidebar_position: 4 +title: Query Order +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +This API is used to query a currency-exchange order by your `client_order_id`. +Poll it until the order reaches a terminal state. + + +longbridge forex order my-order-0001 + + + + +## Request + + + + + + +
HTTP MethodGET
HTTP URL/v1/forex/order
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| --------------- | ------ | -------- | --------------- | +| client_order_id | string | YES | Your order id | + +Poll no more often than every 2 seconds. Most orders reach a terminal state +within 1 minute; it can take longer outside service hours or under manual +review. + +### Request Example + + + + +```python +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Query order +resp = ctx.order("my-order-0001") +print(resp) +``` + + + + +```python +import asyncio +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Query order + resp = await ctx.order("my-order-0001") + print(resp) + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + const resp = await ctx.order('my-order-0001') + console.log(resp) +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +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); + ForexContext ctx = ForexContext.create(config)) { + ForexOrderDetail resp = ctx.order("my-order-0001").get(); + System.out.println(resp); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::ForexContext, Config}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + let resp = ctx.order("my-order-0001").await?; + println!("{:?}", resp); + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.order("my-order-0001", [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "state: " << res->state << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + resp, err := fctx.Order(context.Background(), "my-order-0001") + if err != nil { + log.Fatal(err) + } + fmt.Println("state:", resp.State) +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "state": 1, + "rate": "7.7812", + "from_amount": "1000", + "to_amount": "7781.2", + "fail_reason": "" + } +} +``` + +The `data` fields are: + +| Name | Type | Description | +| ----------- | ------ | ------------------------------------------------------------------------------------------------------------------- | +| state | int32 | `0` processing (incl. manual review — keep querying); `1` success; `2` failed (frozen funds returned) | +| rate | string | Execution rate (standard currency-pair terms); empty when not filled | +| from_amount | string | Convert-out amount (actual once filled) | +| to_amount | string | Convert-in amount (actual once filled) | +| fail_reason | string | Failure reason | + +### Response Status + +| Status | Description | Schema | +| ------ | -------------------------------------------------------- | ------ | +| 200 | The order was returned successfully. | None | +| 400 | The query failed with an error in the request parameter. | None | + + diff --git a/docs/en/docs/forex/quote.mdx b/docs/en/docs/forex/quote.mdx new file mode 100644 index 00000000..75f455af --- /dev/null +++ b/docs/en/docs/forex/quote.mdx @@ -0,0 +1,286 @@ +--- +slug: quote +sidebar_position: 2 +title: Get Quote +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +This API is used to get a currency-exchange quote. The returned quote locks in +an execution rate for a short window; submit within the window to fill at that +rate. + + +longbridge forex quote USD HKD --amount 1000 + + + + +## Request + + + + + + +
HTTP MethodPOST
HTTP URL/v1/forex/quote
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| ------------- | ------ | ----------- | ------------------------------------------------------------------------------------------- | +| from | string | YES | Convert-out currency, ISO 4217, example: `USD` | +| to | string | YES | Convert-in currency, example: `HKD` | +| amount | string | Conditional | Convert-out amount. One of `amount` / `target_amount` is required. At most 2 decimals, minimum `0.01` | +| target_amount | string | Conditional | Convert-in amount. One of `amount` / `target_amount` is required. | + +Request example: + +```json +{ "from": "USD", "to": "HKD", "amount": "1000.00" } +``` + +The quote is bound to you and can only be submitted by you. + +### Request Example + + + + +```python +from decimal import Decimal +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Get quote +resp = ctx.quote("USD", "HKD", amount=Decimal("1000.00")) +print(resp) +``` + + + + +```python +import asyncio +from decimal import Decimal +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Get quote + resp = await ctx.quote("USD", "HKD", amount=Decimal("1000.00")) + print(resp) + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + const resp = await ctx.quote('USD', 'HKD', '1000.00') + console.log(resp) +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +import java.math.BigDecimal; +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); + ForexContext ctx = ForexContext.create(config)) { + ForexQuote resp = ctx.quote("USD", "HKD", new BigDecimal("1000.00"), null).get(); + System.out.println(resp); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::{ForexContext, GetForexQuoteOptions}, Config}; +use rust_decimal::Decimal; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + let resp = ctx.quote("USD", "HKD", Some(GetForexQuoteOptions::new().amount(Decimal::from(1000)))).await?; + println!("{:?}", resp); + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.quote("USD", "HKD", Decimal(1000.0), std::nullopt, [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "rate: " << res->rate << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" + "github.com/shopspring/decimal" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + amount := decimal.NewFromFloat(1000) + resp, err := fctx.Quote(context.Background(), &forex.GetForexQuote{ + From: "USD", + To: "HKD", + Amount: &amount, + }) + if err != nil { + log.Fatal(err) + } + fmt.Println("rate:", resp.Rate) +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "ok": true, + "quote_id": "Q20260925000123", + "rate": "7.7812", + "expire_at": 1790000000000, + "ccy_pair": "USD/HKD" + } +} +``` + +The `data` fields are: + +| Name | Type | Description | +| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| quote_id | string | Quote id, used when submitting the order | +| rate | string | Customer execution rate (standard currency-pair terms; 1 BASE = rate QUOTE). In-window orders fill at this rate | +| expire_at | int64 | Quote expiry, Unix MILLISECOND timestamp | +| ccy_pair | string | Standard pair for the rate, format BASE/QUOTE; independent of the request `from`/`to` direction (both HKD→USD and USD→HKD return USD/HKD) | + +### Response Status + +| Status | Description | Schema | +| ------ | ------------------------------------------------------------ | ------ | +| 200 | The quote was returned successfully. | None | +| 400 | The request was rejected with an incorrect request parameter. | None | + + diff --git a/docs/en/docs/forex/submit.mdx b/docs/en/docs/forex/submit.mdx new file mode 100644 index 00000000..a4bb122f --- /dev/null +++ b/docs/en/docs/forex/submit.mdx @@ -0,0 +1,270 @@ +--- +slug: submit +sidebar_position: 3 +title: Submit Order +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +This API is used to submit a currency-exchange order from a quote. Acceptance is +acknowledged synchronously; the conversion itself is processed asynchronously, +so query the result with your `client_order_id`. + + +longbridge forex submit-order Q20260925000123 my-order-0001 + + + + +## Request + + + + + + +
HTTP MethodPOST
HTTP URL/v1/forex/order
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| --------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------- | +| quote_id | string | YES | The quote id from Get Quote; the currency and amount are taken from the quote | +| client_order_id | string | YES | Your own order id, 1~64 chars of letters/digits/`_`/`-`, must be UNIQUE across all of your accounts | + +### Idempotency & retries + +The same `client_order_id` yields at most one order (the first created order is +returned). To avoid duplicate conversions on network timeouts or `5xx` errors, +do **not** change `client_order_id`: + +- First **query** with the `client_order_id`. +- Only if no order is found, **re-quote** and resubmit with the **same** + `client_order_id`. +- Whatever `quote_id` a repeated `client_order_id` carries, the first created + order is returned. +- Each `quote_id` can be submitted successfully only once (reuse returns + `603006` — re-quote). + +### Request Example + + + + +```python +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Submit order +ctx.submit_order("Q20260925000123", "my-order-0001") +``` + + + + +```python +import asyncio +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Submit order + await ctx.submit_order("Q20260925000123", "my-order-0001") + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + await ctx.submitOrder('Q20260925000123', 'my-order-0001') +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +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); + ForexContext ctx = ForexContext.create(config)) { + ctx.submitOrder("Q20260925000123", "my-order-0001").get(); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::{ForexContext, SubmitForexOrderOptions}, Config}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + ctx.submit_order( + SubmitForexOrderOptions::new("Q20260925000123", "my-order-0001") + ).await?; + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.submit_order("Q20260925000123", "my-order-0001", [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "accepted" << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + err = fctx.SubmitOrder(context.Background(), &forex.SubmitForexOrder{ + QuoteID: "Q20260925000123", + ClientOrderID: "my-order-0001", + }) + if err != nil { + log.Fatal(err) + } + fmt.Println("accepted") +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "ok": true + } +} +``` + +`ok` indicates whether the order was accepted. The conversion is processed +asynchronously — query the result with your `client_order_id`. + +### Response Status + +| Status | Description | Schema | +| ------ | ------------------------------------------------------------ | ------ | +| 200 | The submission was successful and the order was accepted. | None | +| 400 | The submit was rejected with an incorrect request parameter. | None | + + diff --git a/docs/zh-CN/docs/cli/forex/_category_.json b/docs/zh-CN/docs/cli/forex/_category_.json new file mode 100644 index 00000000..740b6ae2 --- /dev/null +++ b/docs/zh-CN/docs/cli/forex/_category_.json @@ -0,0 +1 @@ +{ "label": "货币兑换", "icon": "arrow-left-right", "position": 12, "collapsed": true } diff --git a/docs/zh-CN/docs/cli/forex/exchange.mdx b/docs/zh-CN/docs/cli/forex/exchange.mdx new file mode 100644 index 00000000..cbea8c99 --- /dev/null +++ b/docs/zh-CN/docs/cli/forex/exchange.mdx @@ -0,0 +1,71 @@ +--- +title: '货币兑换' +sidebar_label: '货币兑换' +sidebar_position: 1 +--- + +# longbridge forex + +在终端里直接把账户内的一种货币兑换成另一种。流程分三步且为异步:先**询价**拿到汇率,再用该报价**下单**,然后**查询**订单直到进入终态。 + +这些命令作用于你的账户,需要登录;`submit-order` 会动用真实资金,未加 `--yes` 时会提示确认。 + +## 基本用法 + +```bash +# 锁定汇率并拿到 quote_id(--amount 与 --target-amount 二选一) +longbridge forex quote USD HKD --amount 1000 +``` + +``` +| Field | Value | +|-----------|------------------| +| quote_id | Q20260925000123 | +| rate | 7.7812 | +| ccy_pair | USD/HKD | +| expire_at | 1790000000000 | +``` + +## 示例 + +### 询价 + +```bash +# 按兑出金额(你花出的 USD) +longbridge forex quote USD HKD --amount 1000 +# …或按兑入金额(你想收到的 HKD) +longbridge forex quote USD HKD --target-amount 7800 +``` + +`rate` 按标准币种对表示(`1 BASE = rate QUOTE`,如 `USD/HKD`),与你请求的兑换方向无关。报价与你绑定,且仅在有效期内可用(`expire_at` 为 Unix 毫秒时间戳)。 + +### 提交兑换 + +```bash +# 用 quote_id 下单,并带上你自定义的唯一订单号 +longbridge forex submit-order Q20260925000123 my-order-0001 +# 跳过确认(例如用于脚本) +longbridge forex submit-order Q20260925000123 my-order-0001 --yes +``` + +下单成功只代表订单**已受理**——兑换为异步完成,命令会重新查询并打印当前订单状态。相同 `client_order_id` 不会产生第二笔订单;超时请先查询,确有必要再用**同一个**订单号重新询价并提交。 + +### 查询订单 + +```bash +longbridge forex order my-order-0001 +``` + +``` +| Field | Value | +|-------------|---------| +| state | 1 | +| rate | 7.7812 | +| from_amount | 1000 | +| to_amount | 7781.2 | +| fail_reason | | +``` + +`state` 为 `0` 处理中(请继续查询)、`1` 兑换成功、`2` 兑换失败(冻结资金已退回)。建议轮询间隔不低于 2 秒;多数订单在 1 分钟内进入终态。 + +> 任意命令加 `--format json` 可获取完整的机器可读返回。 diff --git a/docs/zh-CN/docs/forex/_category_.json b/docs/zh-CN/docs/forex/_category_.json new file mode 100644 index 00000000..7d3d1aec --- /dev/null +++ b/docs/zh-CN/docs/forex/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "外汇兑换", + "collapsible": true, + "collapsed": true, + "position": 4.6, + "link": null, + "icon": "arrow-left-right" +} diff --git a/docs/zh-CN/docs/forex/overview.mdx b/docs/zh-CN/docs/forex/overview.mdx new file mode 100644 index 00000000..4da3601b --- /dev/null +++ b/docs/zh-CN/docs/forex/overview.mdx @@ -0,0 +1,50 @@ +--- +id: forex_overview +sidebar_label: 概览 +title: 概览 +slug: forex-overview +sidebar_position: 1 +--- + +# 外汇兑换接口总览 + +外汇兑换渠道用于将一种货币的余额兑换为另一种货币。兑换是异步的,分为三个步骤: + +1. **获取报价** — 调用[获取报价](./quote)锁定执行汇率并获得 `quote_id`。报价与您绑定,并在一个较短的时间窗口后过期。 +2. **提交兑换订单** — 使用该 `quote_id` 和您自己的 `client_order_id` 调用[提交兑换订单](./submit)发起兑换。是否受理会同步返回,但兑换本身为异步处理。 +3. **查询兑换订单** — 使用相同的 `client_order_id` 轮询[查询兑换订单](./query),直到订单到达终态(成功或失败)。 + + + + + + + + + + + + + + + + + + + + +
类型功能简介
外汇兑换获取报价
提交兑换订单
查询兑换订单
+ +## 错误码 + +| 错误码 | 说明 | +| ------ | ------------------------------------------------ | +| 603001 | 参数错误 | +| 603002 | 不支持的货币对 | +| 603004 | 报价不存在 | +| 603005 | 报价已过期(请重新报价) | +| 603006 | 报价已被使用(请重新报价) | +| 603007 | 可兑换余额不足 | +| 603008 | 未授权 / 无兑换权限 / 非服务时段 | +| 603009 | 订单不存在 | +| 603010 | 服务暂时不可用,请稍后重试 | diff --git a/docs/zh-CN/docs/forex/query.mdx b/docs/zh-CN/docs/forex/query.mdx new file mode 100644 index 00000000..a20a1779 --- /dev/null +++ b/docs/zh-CN/docs/forex/query.mdx @@ -0,0 +1,266 @@ +--- +slug: query +sidebar_position: 4 +title: 查询兑换订单 +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +该接口用于通过 `client_order_id` 查询外汇兑换订单。请轮询查询,直到订单到达终态。 + + +longbridge forex order my-order-0001 + + + + +## Request + + + + + + +
HTTP MethodGET
HTTP URL/v1/forex/order
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| --------------- | ------ | -------- | --------------- | +| client_order_id | string | YES | 您的订单 id | + +轮询间隔不要短于 2 秒。大多数订单会在 1 分钟内到达终态;在非服务时段或人工审核时可能更久。 + +### Request Example + + + + +```python +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Query order +resp = ctx.order("my-order-0001") +print(resp) +``` + + + + +```python +import asyncio +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Query order + resp = await ctx.order("my-order-0001") + print(resp) + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + const resp = await ctx.order('my-order-0001') + console.log(resp) +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +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); + ForexContext ctx = ForexContext.create(config)) { + ForexOrderDetail resp = ctx.order("my-order-0001").get(); + System.out.println(resp); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::ForexContext, Config}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + let resp = ctx.order("my-order-0001").await?; + println!("{:?}", resp); + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.order("my-order-0001", [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "state: " << res->state << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + resp, err := fctx.Order(context.Background(), "my-order-0001") + if err != nil { + log.Fatal(err) + } + fmt.Println("state:", resp.State) +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "state": 1, + "rate": "7.7812", + "from_amount": "1000", + "to_amount": "7781.2", + "fail_reason": "" + } +} +``` + +`data` 字段说明: + +| Name | Type | Description | +| ----------- | ------ | --------------------------------------------------------------------- | +| state | int32 | `0` 处理中(含人工审核,请继续查询);`1` 成功;`2` 失败(冻结资金已退回) | +| rate | string | 执行汇率(标准货币对表示);未成交时为空 | +| from_amount | string | 转出金额(成交后为实际值) | +| to_amount | string | 转入金额(成交后为实际值) | +| fail_reason | string | 失败原因 | + +### Response Status + +| Status | Description | Schema | +| ------ | ---------------------- | ------ | +| 200 | 订单查询成功。 | None | +| 400 | 请求参数错误,查询失败。 | None | + + diff --git a/docs/zh-CN/docs/forex/quote.mdx b/docs/zh-CN/docs/forex/quote.mdx new file mode 100644 index 00000000..89a9effc --- /dev/null +++ b/docs/zh-CN/docs/forex/quote.mdx @@ -0,0 +1,284 @@ +--- +slug: quote +sidebar_position: 2 +title: 获取报价 +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +该接口用于获取外汇兑换报价。返回的报价会锁定一个短时间窗口内的执行汇率,在窗口内提交即可按该汇率成交。 + + +longbridge forex quote USD HKD --amount 1000 + + + + +## Request + + + + + + +
HTTP MethodPOST
HTTP URL/v1/forex/quote
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| ------------- | ------ | -------- | ------------------------------------------------------------------------- | +| from | string | YES | 转出货币,ISO 4217,例如:`USD` | +| to | string | YES | 转入货币,例如:`HKD` | +| amount | string | 条件必填 | 转出金额。`amount` / `target_amount` 二者必填其一。最多 2 位小数,最小 `0.01` | +| target_amount | string | 条件必填 | 转入金额。`amount` / `target_amount` 二者必填其一。 | + +请求示例: + +```json +{ "from": "USD", "to": "HKD", "amount": "1000.00" } +``` + +报价与您绑定,只能由您本人提交。 + +### Request Example + + + + +```python +from decimal import Decimal +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Get quote +resp = ctx.quote("USD", "HKD", amount=Decimal("1000.00")) +print(resp) +``` + + + + +```python +import asyncio +from decimal import Decimal +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Get quote + resp = await ctx.quote("USD", "HKD", amount=Decimal("1000.00")) + print(resp) + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + const resp = await ctx.quote('USD', 'HKD', '1000.00') + console.log(resp) +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +import java.math.BigDecimal; +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); + ForexContext ctx = ForexContext.create(config)) { + ForexQuote resp = ctx.quote("USD", "HKD", new BigDecimal("1000.00"), null).get(); + System.out.println(resp); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::{ForexContext, GetForexQuoteOptions}, Config}; +use rust_decimal::Decimal; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + let resp = ctx.quote("USD", "HKD", Some(GetForexQuoteOptions::new().amount(Decimal::from(1000)))).await?; + println!("{:?}", resp); + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.quote("USD", "HKD", Decimal(1000.0), std::nullopt, [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "rate: " << res->rate << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" + "github.com/shopspring/decimal" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + amount := decimal.NewFromFloat(1000) + resp, err := fctx.Quote(context.Background(), &forex.GetForexQuote{ + From: "USD", + To: "HKD", + Amount: &amount, + }) + if err != nil { + log.Fatal(err) + } + fmt.Println("rate:", resp.Rate) +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "ok": true, + "quote_id": "Q20260925000123", + "rate": "7.7812", + "expire_at": 1790000000000, + "ccy_pair": "USD/HKD" + } +} +``` + +`data` 字段说明: + +| Name | Type | Description | +| --------- | ------ | --------------------------------------------------------------------------------------- | +| quote_id | string | 报价 id,提交订单时使用 | +| rate | string | 客户执行汇率(标准货币对表示;1 BASE = rate QUOTE)。在窗口内的订单按此汇率成交 | +| expire_at | int64 | 报价过期时间,Unix 毫秒时间戳 | +| ccy_pair | string | 汇率对应的标准货币对,格式为 BASE/QUOTE;与请求的 `from`/`to` 方向无关(HKD→USD 与 USD→HKD 均返回 USD/HKD) | + +### Response Status + +| Status | Description | Schema | +| ------ | ------------------------ | ------ | +| 200 | 报价获取成功。 | None | +| 400 | 请求参数错误,请求被拒绝。 | None | + + diff --git a/docs/zh-CN/docs/forex/submit.mdx b/docs/zh-CN/docs/forex/submit.mdx new file mode 100644 index 00000000..0a219cb9 --- /dev/null +++ b/docs/zh-CN/docs/forex/submit.mdx @@ -0,0 +1,262 @@ +--- +slug: submit +sidebar_position: 3 +title: 提交兑换订单 +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +该接口用于基于报价提交外汇兑换订单。是否受理会同步返回;兑换本身为异步处理,请使用 `client_order_id` 查询结果。 + + +longbridge forex submit-order Q20260925000123 my-order-0001 + + + + +## Request + + + + + + +
HTTP MethodPOST
HTTP URL/v1/forex/order
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| --------------- | ------ | -------- | --------------------------------------------------------------------------- | +| quote_id | string | YES | 来自获取报价的报价 id;币种和金额取自该报价 | +| client_order_id | string | YES | 您自己的订单 id,1~64 个字符,由字母/数字/`_`/`-` 组成,在您名下所有账户中必须唯一 | + +### 幂等与重试 + +相同的 `client_order_id` 最多只会生成一笔订单(返回最先创建的订单)。为避免在网络超时或 `5xx` 错误时产生重复兑换,请**不要**更换 `client_order_id`: + +- 先用该 `client_order_id` **查询**。 +- 仅当查询不到订单时,再**重新报价**并使用**相同的** `client_order_id` 重新提交。 +- 无论重复的 `client_order_id` 携带哪个 `quote_id`,都会返回最先创建的订单。 +- 每个 `quote_id` 只能成功提交一次(重复使用将返回 `603006`,请重新报价)。 + +### Request Example + + + + +```python +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Submit order +ctx.submit_order("Q20260925000123", "my-order-0001") +``` + + + + +```python +import asyncio +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Submit order + await ctx.submit_order("Q20260925000123", "my-order-0001") + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + await ctx.submitOrder('Q20260925000123', 'my-order-0001') +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +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); + ForexContext ctx = ForexContext.create(config)) { + ctx.submitOrder("Q20260925000123", "my-order-0001").get(); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::{ForexContext, SubmitForexOrderOptions}, Config}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + ctx.submit_order( + SubmitForexOrderOptions::new("Q20260925000123", "my-order-0001") + ).await?; + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.submit_order("Q20260925000123", "my-order-0001", [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "accepted" << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + err = fctx.SubmitOrder(context.Background(), &forex.SubmitForexOrder{ + QuoteID: "Q20260925000123", + ClientOrderID: "my-order-0001", + }) + if err != nil { + log.Fatal(err) + } + fmt.Println("accepted") +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "ok": true + } +} +``` + +`ok` 表示订单是否受理。兑换为异步处理 — 请使用 `client_order_id` 查询结果。 + +### Response Status + +| Status | Description | Schema | +| ------ | ---------------------- | ------ | +| 200 | 提交成功,订单已受理。 | None | +| 400 | 请求参数错误,提交被拒绝。 | None | + + diff --git a/docs/zh-HK/docs/cli/forex/_category_.json b/docs/zh-HK/docs/cli/forex/_category_.json new file mode 100644 index 00000000..a24b4df2 --- /dev/null +++ b/docs/zh-HK/docs/cli/forex/_category_.json @@ -0,0 +1 @@ +{ "label": "貨幣兌換", "icon": "arrow-left-right", "position": 12, "collapsed": true } diff --git a/docs/zh-HK/docs/cli/forex/exchange.mdx b/docs/zh-HK/docs/cli/forex/exchange.mdx new file mode 100644 index 00000000..dc738a18 --- /dev/null +++ b/docs/zh-HK/docs/cli/forex/exchange.mdx @@ -0,0 +1,71 @@ +--- +title: '貨幣兌換' +sidebar_label: '貨幣兌換' +sidebar_position: 1 +--- + +# longbridge forex + +在終端裡直接把賬戶內的一種貨幣兌換成另一種。流程分三步且為非同步:先**詢價**取得匯率,再用該報價**下單**,然後**查詢**訂單直至進入終態。 + +這些命令作用於你的賬戶,需要登入;`submit-order` 會動用真實資金,未加 `--yes` 時會提示確認。 + +## 基本用法 + +```bash +# 鎖定匯率並取得 quote_id(--amount 與 --target-amount 二選一) +longbridge forex quote USD HKD --amount 1000 +``` + +``` +| Field | Value | +|-----------|------------------| +| quote_id | Q20260925000123 | +| rate | 7.7812 | +| ccy_pair | USD/HKD | +| expire_at | 1790000000000 | +``` + +## 範例 + +### 詢價 + +```bash +# 按兌出金額(你花出的 USD) +longbridge forex quote USD HKD --amount 1000 +# …或按兌入金額(你想收到的 HKD) +longbridge forex quote USD HKD --target-amount 7800 +``` + +`rate` 按標準幣種對表示(`1 BASE = rate QUOTE`,如 `USD/HKD`),與你請求的兌換方向無關。報價與你綁定,且僅在有效期內可用(`expire_at` 為 Unix 毫秒時間戳)。 + +### 提交兌換 + +```bash +# 用 quote_id 下單,並帶上你自定義的唯一訂單號 +longbridge forex submit-order Q20260925000123 my-order-0001 +# 跳過確認(例如用於腳本) +longbridge forex submit-order Q20260925000123 my-order-0001 --yes +``` + +下單成功只代表訂單**已受理**——兌換為非同步完成,命令會重新查詢並打印當前訂單狀態。相同 `client_order_id` 不會產生第二筆訂單;逾時請先查詢,確有必要再用**同一個**訂單號重新詢價並提交。 + +### 查詢訂單 + +```bash +longbridge forex order my-order-0001 +``` + +``` +| Field | Value | +|-------------|---------| +| state | 1 | +| rate | 7.7812 | +| from_amount | 1000 | +| to_amount | 7781.2 | +| fail_reason | | +``` + +`state` 為 `0` 處理中(請繼續查詢)、`1` 兌換成功、`2` 兌換失敗(凍結資金已退回)。建議輪詢間隔不低於 2 秒;多數訂單在 1 分鐘內進入終態。 + +> 任意命令加 `--format json` 可取得完整的機器可讀返回。 diff --git a/docs/zh-HK/docs/forex/_category_.json b/docs/zh-HK/docs/forex/_category_.json new file mode 100644 index 00000000..9f69cbad --- /dev/null +++ b/docs/zh-HK/docs/forex/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "外匯兌換", + "collapsible": true, + "collapsed": true, + "position": 4.6, + "link": null, + "icon": "arrow-left-right" +} diff --git a/docs/zh-HK/docs/forex/overview.mdx b/docs/zh-HK/docs/forex/overview.mdx new file mode 100644 index 00000000..a243091c --- /dev/null +++ b/docs/zh-HK/docs/forex/overview.mdx @@ -0,0 +1,50 @@ +--- +id: forex_overview +sidebar_label: 概覽 +title: 概覽 +slug: forex-overview +sidebar_position: 1 +--- + +# 外匯兌換接口總覽 + +外匯兌換渠道用於將一種貨幣的餘額兌換為另一種貨幣。兌換是異步的,分為三個步驟: + +1. **獲取報價** — 調用[獲取報價](./quote)鎖定執行匯率並獲得 `quote_id`。報價與您綁定,並在一個較短的時間窗口後過期。 +2. **提交兌換訂單** — 使用該 `quote_id` 和您自己的 `client_order_id` 調用[提交兌換訂單](./submit)發起兌換。是否受理會同步返回,但兌換本身為異步處理。 +3. **查詢兌換訂單** — 使用相同的 `client_order_id` 輪詢[查詢兌換訂單](./query),直到訂單到達終態(成功或失敗)。 + + + + + + + + + + + + + + + + + + + + +
類型功能簡介
外匯兌換獲取報價
提交兌換訂單
查詢兌換訂單
+ +## 錯誤碼 + +| 錯誤碼 | 說明 | +| ------ | ------------------------------------------------ | +| 603001 | 參數錯誤 | +| 603002 | 不支援的貨幣對 | +| 603004 | 報價不存在 | +| 603005 | 報價已過期(請重新報價) | +| 603006 | 報價已被使用(請重新報價) | +| 603007 | 可兌換餘額不足 | +| 603008 | 未授權 / 無兌換權限 / 非服務時段 | +| 603009 | 訂單不存在 | +| 603010 | 服務暫時不可用,請稍後重試 | diff --git a/docs/zh-HK/docs/forex/query.mdx b/docs/zh-HK/docs/forex/query.mdx new file mode 100644 index 00000000..74b687e8 --- /dev/null +++ b/docs/zh-HK/docs/forex/query.mdx @@ -0,0 +1,266 @@ +--- +slug: query +sidebar_position: 4 +title: 查詢兌換訂單 +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +該接口用於通過 `client_order_id` 查詢外匯兌換訂單。請輪詢查詢,直到訂單到達終態。 + + +longbridge forex order my-order-0001 + + + + +## Request + + + + + + +
HTTP MethodGET
HTTP URL/v1/forex/order
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| --------------- | ------ | -------- | --------------- | +| client_order_id | string | YES | 您的訂單 id | + +輪詢間隔不要短於 2 秒。大多數訂單會在 1 分鐘內到達終態;在非服務時段或人工審核時可能更久。 + +### Request Example + + + + +```python +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Query order +resp = ctx.order("my-order-0001") +print(resp) +``` + + + + +```python +import asyncio +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Query order + resp = await ctx.order("my-order-0001") + print(resp) + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + const resp = await ctx.order('my-order-0001') + console.log(resp) +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +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); + ForexContext ctx = ForexContext.create(config)) { + ForexOrderDetail resp = ctx.order("my-order-0001").get(); + System.out.println(resp); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::ForexContext, Config}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + let resp = ctx.order("my-order-0001").await?; + println!("{:?}", resp); + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.order("my-order-0001", [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "state: " << res->state << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + resp, err := fctx.Order(context.Background(), "my-order-0001") + if err != nil { + log.Fatal(err) + } + fmt.Println("state:", resp.State) +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "state": 1, + "rate": "7.7812", + "from_amount": "1000", + "to_amount": "7781.2", + "fail_reason": "" + } +} +``` + +`data` 欄位說明: + +| Name | Type | Description | +| ----------- | ------ | --------------------------------------------------------------------- | +| state | int32 | `0` 處理中(含人工審核,請繼續查詢);`1` 成功;`2` 失敗(凍結資金已退回) | +| rate | string | 執行匯率(標準貨幣對表示);未成交時為空 | +| from_amount | string | 轉出金額(成交後為實際值) | +| to_amount | string | 轉入金額(成交後為實際值) | +| fail_reason | string | 失敗原因 | + +### Response Status + +| Status | Description | Schema | +| ------ | ---------------------- | ------ | +| 200 | 訂單查詢成功。 | None | +| 400 | 請求參數錯誤,查詢失敗。 | None | + + diff --git a/docs/zh-HK/docs/forex/quote.mdx b/docs/zh-HK/docs/forex/quote.mdx new file mode 100644 index 00000000..2b753c37 --- /dev/null +++ b/docs/zh-HK/docs/forex/quote.mdx @@ -0,0 +1,284 @@ +--- +slug: quote +sidebar_position: 2 +title: 獲取報價 +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +該接口用於獲取外匯兌換報價。返回的報價會鎖定一個短時間窗口內的執行匯率,在窗口內提交即可按該匯率成交。 + + +longbridge forex quote USD HKD --amount 1000 + + + + +## Request + + + + + + +
HTTP MethodPOST
HTTP URL/v1/forex/quote
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| ------------- | ------ | -------- | ------------------------------------------------------------------------- | +| from | string | YES | 轉出貨幣,ISO 4217,例如:`USD` | +| to | string | YES | 轉入貨幣,例如:`HKD` | +| amount | string | 條件必填 | 轉出金額。`amount` / `target_amount` 二者必填其一。最多 2 位小數,最小 `0.01` | +| target_amount | string | 條件必填 | 轉入金額。`amount` / `target_amount` 二者必填其一。 | + +請求示例: + +```json +{ "from": "USD", "to": "HKD", "amount": "1000.00" } +``` + +報價與您綁定,只能由您本人提交。 + +### Request Example + + + + +```python +from decimal import Decimal +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Get quote +resp = ctx.quote("USD", "HKD", amount=Decimal("1000.00")) +print(resp) +``` + + + + +```python +import asyncio +from decimal import Decimal +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Get quote + resp = await ctx.quote("USD", "HKD", amount=Decimal("1000.00")) + print(resp) + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + const resp = await ctx.quote('USD', 'HKD', '1000.00') + console.log(resp) +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +import java.math.BigDecimal; +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); + ForexContext ctx = ForexContext.create(config)) { + ForexQuote resp = ctx.quote("USD", "HKD", new BigDecimal("1000.00"), null).get(); + System.out.println(resp); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::{ForexContext, GetForexQuoteOptions}, Config}; +use rust_decimal::Decimal; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + let resp = ctx.quote("USD", "HKD", Some(GetForexQuoteOptions::new().amount(Decimal::from(1000)))).await?; + println!("{:?}", resp); + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.quote("USD", "HKD", Decimal(1000.0), std::nullopt, [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "rate: " << res->rate << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" + "github.com/shopspring/decimal" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + amount := decimal.NewFromFloat(1000) + resp, err := fctx.Quote(context.Background(), &forex.GetForexQuote{ + From: "USD", + To: "HKD", + Amount: &amount, + }) + if err != nil { + log.Fatal(err) + } + fmt.Println("rate:", resp.Rate) +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "ok": true, + "quote_id": "Q20260925000123", + "rate": "7.7812", + "expire_at": 1790000000000, + "ccy_pair": "USD/HKD" + } +} +``` + +`data` 欄位說明: + +| Name | Type | Description | +| --------- | ------ | --------------------------------------------------------------------------------------- | +| quote_id | string | 報價 id,提交訂單時使用 | +| rate | string | 客戶執行匯率(標準貨幣對表示;1 BASE = rate QUOTE)。在窗口內的訂單按此匯率成交 | +| expire_at | int64 | 報價過期時間,Unix 毫秒時間戳 | +| ccy_pair | string | 匯率對應的標準貨幣對,格式為 BASE/QUOTE;與請求的 `from`/`to` 方向無關(HKD→USD 與 USD→HKD 均返回 USD/HKD) | + +### Response Status + +| Status | Description | Schema | +| ------ | ------------------------ | ------ | +| 200 | 報價獲取成功。 | None | +| 400 | 請求參數錯誤,請求被拒絕。 | None | + + diff --git a/docs/zh-HK/docs/forex/submit.mdx b/docs/zh-HK/docs/forex/submit.mdx new file mode 100644 index 00000000..36879699 --- /dev/null +++ b/docs/zh-HK/docs/forex/submit.mdx @@ -0,0 +1,262 @@ +--- +slug: submit +sidebar_position: 3 +title: 提交兌換訂單 +language_tabs: false +toc_footers: [] +includes: [] +search: true +highlight_theme: '' +headingLevel: 2 +--- + +該接口用於基於報價提交外匯兌換訂單。是否受理會同步返回;兌換本身為異步處理,請使用 `client_order_id` 查詢結果。 + + +longbridge forex submit-order Q20260925000123 my-order-0001 + + + + +## Request + + + + + + +
HTTP MethodPOST
HTTP URL/v1/forex/order
+ +### Parameters + +> Content-Type: application/json; charset=utf-8 + +| Name | Type | Required | Description | +| --------------- | ------ | -------- | --------------------------------------------------------------------------- | +| quote_id | string | YES | 來自獲取報價的報價 id;幣種和金額取自該報價 | +| client_order_id | string | YES | 您自己的訂單 id,1~64 個字符,由字母/數字/`_`/`-` 組成,在您名下所有賬戶中必須唯一 | + +### 冪等與重試 + +相同的 `client_order_id` 最多只會生成一筆訂單(返回最先創建的訂單)。為避免在網絡超時或 `5xx` 錯誤時產生重複兌換,請**不要**更換 `client_order_id`: + +- 先用該 `client_order_id` **查詢**。 +- 僅當查詢不到訂單時,再**重新報價**並使用**相同的** `client_order_id` 重新提交。 +- 無論重複的 `client_order_id` 攜帶哪個 `quote_id`,都會返回最先創建的訂單。 +- 每個 `quote_id` 只能成功提交一次(重複使用將返回 `603006`,請重新報價)。 + +### Request Example + + + + +```python +from longbridge.openapi import ForexContext, Config, OAuthBuilder + +oauth = OAuthBuilder("your-client-id").build(lambda url: print("Visit:", url)) +config = Config.from_oauth(oauth) + +# Create a context for forex APIs +ctx = ForexContext(config) + +# Submit order +ctx.submit_order("Q20260925000123", "my-order-0001") +``` + + + + +```python +import asyncio +from longbridge.openapi import AsyncForexContext, Config, OAuthBuilder + +async def main() -> None: + oauth = await OAuthBuilder("your-client-id").build_async(lambda url: print("Visit:", url)) + config = Config.from_oauth(oauth) + + # Create a context for forex APIs + ctx = AsyncForexContext.create(config) + + # Submit order + await ctx.submit_order("Q20260925000123", "my-order-0001") + +if __name__ == "__main__": + asyncio.run(main()) +``` + + + + +```javascript +const { Config, ForexContext, 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 = new ForexContext(config) + await ctx.submitOrder('Q20260925000123', 'my-order-0001') +} +main().catch(console.error) +``` + + + + +```java +import com.longbridge.*; +import com.longbridge.forex.*; +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); + ForexContext ctx = ForexContext.create(config)) { + ctx.submitOrder("Q20260925000123", "my-order-0001").get(); + } + } +} +``` + + + + +```rust +use std::sync::Arc; +use longbridge::{oauth::OAuthBuilder, forex::{ForexContext, SubmitForexOrderOptions}, Config}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let oauth = OAuthBuilder::new("your-client-id").build(|url| println!("Open this URL to authorize: {url}")).await?; + let config = Arc::new(Config::from_oauth(oauth)); + let ctx = ForexContext::new(config); + ctx.submit_order( + SubmitForexOrderOptions::new("Q20260925000123", "my-order-0001") + ).await?; + Ok(()) +} +``` + + + + +```cpp +#include +#include + +#ifdef WIN32 +#include +#endif + +using namespace longbridge; +using namespace longbridge::forex; + +static void +run(const OAuth& oauth) +{ + Config config = Config::from_oauth(oauth); + ForexContext ctx = ForexContext::create(config); + + ctx.submit_order("Q20260925000123", "my-order-0001", [](auto res) { + if (!res) { std::cout << "failed" << std::endl; return; } + std::cout << "accepted" << std::endl; + }); +} + +int main(int argc, char const* argv[]) { +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + const std::string client_id = "your-client-id"; + OAuthBuilder(client_id).build( + [](const std::string& url) { + std::cout << "Open this URL to authorize: " << url << std::endl; + }, + [](auto res) { + if (!res) { + std::cout << "authorization failed: " << *res.status().message() << std::endl; + return; + } + run(*res); + }); + + std::cin.get(); + return 0; +} +``` + + + + +```go +package main + +import ( + "context" + "fmt" + "log" + + "github.com/longbridge/openapi-go/config" + "github.com/longbridge/openapi-go/forex" + "github.com/longbridge/openapi-go/oauth" +) + +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) + } + fctx, err := forex.NewFromCfg(conf) + if err != nil { + log.Fatal(err) + } + defer fctx.Close() + err = fctx.SubmitOrder(context.Background(), &forex.SubmitForexOrder{ + QuoteID: "Q20260925000123", + ClientOrderID: "my-order-0001", + }) + if err != nil { + log.Fatal(err) + } + fmt.Println("accepted") +} +``` + + + + +## Response + +### Response Headers + +- Content-Type: application/json + +### Response Example + +```json +{ + "code": 0, + "message": "success", + "data": { + "ok": true + } +} +``` + +`ok` 表示訂單是否受理。兌換為異步處理 — 請使用 `client_order_id` 查詢結果。 + +### Response Status + +| Status | Description | Schema | +| ------ | ---------------------- | ------ | +| 200 | 提交成功,訂單已受理。 | None | +| 400 | 請求參數錯誤,提交被拒絕。 | None | + +