@okx_ai/okx-trade-cli 1.2.4-beta.0 → 1.2.4-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1284,7 +1284,8 @@ var BOT_SUB_MODULE_IDS = [
1284
1284
  var BOT_DEFAULT_SUB_MODULES = ["bot.grid"];
1285
1285
  var EARN_SUB_MODULE_IDS = [
1286
1286
  "earn.savings",
1287
- "earn.onchain"
1287
+ "earn.onchain",
1288
+ "earn.dcd"
1288
1289
  ];
1289
1290
  var MODULES = [
1290
1291
  "market",
@@ -3428,10 +3429,286 @@ function registerOnchainEarnTools() {
3428
3429
  }
3429
3430
  ];
3430
3431
  }
3432
+ var DCD_CODE_BEHAVIORS = {
3433
+ "50001": { retry: true, suggestion: "DCD service is down. Retry in a few minutes." },
3434
+ "50002": { retry: false, suggestion: "Invalid JSON in request body. This is likely a bug \u2014 check request parameters." },
3435
+ "50014": { retry: false, suggestion: "Missing required parameter. Check that all required fields are provided." },
3436
+ "50016": { retry: false, suggestion: "notionalCcy does not match productId option type. Use baseCcy for CALL, quoteCcy for PUT." },
3437
+ "50026": { retry: true, suggestion: "DCD system error. Retry in a few minutes." },
3438
+ "50030": { retry: false, suggestion: "Account not authorized for DCD (earn-auth check failed). Complete required verification in the OKX app first." },
3439
+ "50038": { retry: false, suggestion: "DCD Open API feature is disabled for this account. Contact OKX support to enable it." },
3440
+ "50051": { retry: false, suggestion: "This currency pair is restricted for your country or account type. Do not retry." },
3441
+ "51000": { retry: false, suggestion: "Invalid parameter value or format. Check ordId, quoteId, or clOrdId." },
3442
+ "51728": { retry: false, suggestion: "Available quota exceeded. Reduce the amount and retry." },
3443
+ "51736": { retry: false, suggestion: "Insufficient balance. Top up your account before retrying." },
3444
+ "52905": { retry: false, suggestion: "Quote has expired or was not found. Request a new quote." },
3445
+ "52909": { retry: false, suggestion: "Duplicate client order ID. Use a different clOrdId." },
3446
+ "52917": { retry: false, suggestion: "Amount is below the minimum trade size. Increase the amount." },
3447
+ "52918": { retry: false, suggestion: "Amount exceeds the maximum trade size. Reduce the amount." },
3448
+ "52921": { retry: false, suggestion: "Quote has already been used by another trade." },
3449
+ "52927": { retry: true, suggestion: "No quote returned by liquidity provider. Retry the quote request." },
3450
+ "52928": { retry: false, suggestion: "Amount is not divisible by the required step size. Adjust the amount." },
3451
+ "58004": { retry: false, suggestion: "Account is frozen or blocked. Contact OKX support. Do not retry." },
3452
+ "58102": { retry: true, suggestion: "DCD rate limit exceeded. Back off and retry after a short delay." }
3453
+ };
3454
+ async function withDcdErrors(fn) {
3455
+ try {
3456
+ return await fn();
3457
+ } catch (error) {
3458
+ if (error instanceof OkxApiError && error.code) {
3459
+ const behavior = DCD_CODE_BEHAVIORS[error.code];
3460
+ if (behavior) {
3461
+ if (behavior.retry) {
3462
+ throw new RateLimitError(error.message, behavior.suggestion, error.endpoint, error.traceId);
3463
+ }
3464
+ throw new OkxApiError(error.message, {
3465
+ code: error.code,
3466
+ suggestion: behavior.suggestion,
3467
+ endpoint: error.endpoint,
3468
+ traceId: error.traceId
3469
+ });
3470
+ }
3471
+ }
3472
+ throw error;
3473
+ }
3474
+ }
3475
+ function registerDcdTools() {
3476
+ return [
3477
+ {
3478
+ name: "dcd_get_currency_pairs",
3479
+ module: "earn.dcd",
3480
+ description: "Get available DCD (Dual Currency Deposit) currency pairs. Private endpoint. Rate limit: 5 req/s.",
3481
+ isWrite: false,
3482
+ inputSchema: { type: "object", properties: {} },
3483
+ handler: async (_rawArgs, context) => {
3484
+ return withDcdErrors(async () => {
3485
+ const response = await context.client.privateGet(
3486
+ "/api/v5/finance/sfp/dcd/currency-pair",
3487
+ void 0,
3488
+ privateRateLimit("dcd_get_currency_pairs", 5)
3489
+ );
3490
+ return normalizeResponse(response);
3491
+ });
3492
+ }
3493
+ },
3494
+ {
3495
+ name: "dcd_get_products",
3496
+ module: "earn.dcd",
3497
+ description: "Get active DCD products with yield, trade size, quota, and VIP yield tier information. baseCcy, quoteCcy, and optType are all required. Private endpoint. Rate limit: 5 req/s.",
3498
+ isWrite: false,
3499
+ inputSchema: {
3500
+ type: "object",
3501
+ properties: {
3502
+ baseCcy: { type: "string", description: "Base currency, e.g. BTC" },
3503
+ quoteCcy: { type: "string", description: "Quote currency, e.g. USDT" },
3504
+ optType: { type: "string", description: "Option type: C (Call/\u9AD8\u5356) or P (Put/\u4F4E\u4E70)" }
3505
+ },
3506
+ required: ["baseCcy", "quoteCcy", "optType"]
3507
+ },
3508
+ handler: async (rawArgs, context) => {
3509
+ const args = asRecord(rawArgs);
3510
+ return withDcdErrors(async () => {
3511
+ const response = await context.client.privateGet(
3512
+ "/api/v5/finance/sfp/dcd/products",
3513
+ {
3514
+ baseCcy: requireString(args, "baseCcy"),
3515
+ quoteCcy: requireString(args, "quoteCcy"),
3516
+ optType: requireString(args, "optType")
3517
+ },
3518
+ privateRateLimit("dcd_get_products", 5)
3519
+ );
3520
+ return normalizeResponse(response);
3521
+ });
3522
+ }
3523
+ },
3524
+ {
3525
+ name: "dcd_request_quote",
3526
+ module: "earn.dcd",
3527
+ description: "Request a real-time quote for a DCD product. Check validUntil for expiry time \u2014 execute before expiry. Yield reflects the user's actual VIP tier rate. Private endpoint. Rate limit: 5 req/s.",
3528
+ isWrite: false,
3529
+ // POST for payload, no state change
3530
+ inputSchema: {
3531
+ type: "object",
3532
+ properties: {
3533
+ productId: { type: "string", description: "Product ID, e.g. BTC-USDT-260327-77000-C" },
3534
+ notionalSz: { type: "string", description: "Investment amount" },
3535
+ notionalCcy: { type: "string", description: "Investment currency: baseCcy for CALL (C), quoteCcy for PUT (P)" }
3536
+ },
3537
+ required: ["productId", "notionalSz", "notionalCcy"]
3538
+ },
3539
+ handler: async (rawArgs, context) => {
3540
+ const args = asRecord(rawArgs);
3541
+ return withDcdErrors(async () => {
3542
+ const response = await context.client.privatePost(
3543
+ "/api/v5/finance/sfp/dcd/quote",
3544
+ {
3545
+ productId: requireString(args, "productId"),
3546
+ notionalSz: requireString(args, "notionalSz"),
3547
+ notionalCcy: requireString(args, "notionalCcy")
3548
+ },
3549
+ privateRateLimit("dcd_request_quote", 5)
3550
+ );
3551
+ return normalizeResponse(response);
3552
+ });
3553
+ }
3554
+ },
3555
+ {
3556
+ name: "dcd_execute_quote",
3557
+ module: "earn.dcd",
3558
+ description: "Execute a DCD quote to place a trade. [CAUTION] Moves real funds into DCD product. Quote expires \u2014 call immediately after dcd_request_quote. Not supported in demo/simulated trading mode. Private endpoint. Rate limit: 5 req/s.",
3559
+ isWrite: true,
3560
+ inputSchema: {
3561
+ type: "object",
3562
+ properties: {
3563
+ quoteId: { type: "string", description: "Quote ID from dcd_request_quote" },
3564
+ clOrdId: { type: "string", description: "Client order ID for idempotency (optional)" }
3565
+ },
3566
+ required: ["quoteId"]
3567
+ },
3568
+ handler: async (rawArgs, context) => {
3569
+ assertNotDemo(context.config, "dcd_execute_quote");
3570
+ const args = asRecord(rawArgs);
3571
+ return withDcdErrors(async () => {
3572
+ const response = await context.client.privatePost(
3573
+ "/api/v5/finance/sfp/dcd/trade",
3574
+ compactObject({
3575
+ quoteId: requireString(args, "quoteId"),
3576
+ clOrdId: readString(args, "clOrdId")
3577
+ }),
3578
+ privateRateLimit("dcd_execute_quote", 5)
3579
+ );
3580
+ return normalizeResponse(response);
3581
+ });
3582
+ }
3583
+ },
3584
+ {
3585
+ name: "dcd_request_redeem_quote",
3586
+ module: "earn.dcd",
3587
+ description: "Request an early redemption quote for a live DCD order. Check validUntil for expiry. Private endpoint. Rate limit: 5 req/s.",
3588
+ isWrite: false,
3589
+ inputSchema: {
3590
+ type: "object",
3591
+ properties: {
3592
+ ordId: { type: "string", description: "Order ID to redeem early" }
3593
+ },
3594
+ required: ["ordId"]
3595
+ },
3596
+ handler: async (rawArgs, context) => {
3597
+ const args = asRecord(rawArgs);
3598
+ return withDcdErrors(async () => {
3599
+ const response = await context.client.privatePost(
3600
+ "/api/v5/finance/sfp/dcd/redeem-quote",
3601
+ { ordId: requireString(args, "ordId") },
3602
+ privateRateLimit("dcd_request_redeem_quote", 5)
3603
+ );
3604
+ return normalizeResponse(response);
3605
+ });
3606
+ }
3607
+ },
3608
+ {
3609
+ name: "dcd_execute_redeem",
3610
+ module: "earn.dcd",
3611
+ description: "Execute an early redemption using a valid redeem quote. [CAUTION] Initiates early redemption of a DCD position. Not supported in demo/simulated trading mode. Private endpoint. Rate limit: 5 req/s.",
3612
+ isWrite: true,
3613
+ inputSchema: {
3614
+ type: "object",
3615
+ properties: {
3616
+ ordId: { type: "string", description: "Order ID" },
3617
+ quoteId: { type: "string", description: "Redeem quote ID from dcd_request_redeem_quote" }
3618
+ },
3619
+ required: ["ordId", "quoteId"]
3620
+ },
3621
+ handler: async (rawArgs, context) => {
3622
+ assertNotDemo(context.config, "dcd_execute_redeem");
3623
+ const args = asRecord(rawArgs);
3624
+ return withDcdErrors(async () => {
3625
+ const response = await context.client.privatePost(
3626
+ "/api/v5/finance/sfp/dcd/redeem",
3627
+ {
3628
+ ordId: requireString(args, "ordId"),
3629
+ quoteId: requireString(args, "quoteId")
3630
+ },
3631
+ privateRateLimit("dcd_execute_redeem", 5)
3632
+ );
3633
+ return normalizeResponse(response);
3634
+ });
3635
+ }
3636
+ },
3637
+ {
3638
+ name: "dcd_get_order_state",
3639
+ module: "earn.dcd",
3640
+ description: "Query DCD order state by order ID. Private endpoint. Rate limit: 5 req/s.",
3641
+ isWrite: false,
3642
+ inputSchema: {
3643
+ type: "object",
3644
+ properties: {
3645
+ ordId: { type: "string", description: "Order ID" }
3646
+ },
3647
+ required: ["ordId"]
3648
+ },
3649
+ handler: async (rawArgs, context) => {
3650
+ const args = asRecord(rawArgs);
3651
+ return withDcdErrors(async () => {
3652
+ const response = await context.client.privateGet(
3653
+ "/api/v5/finance/sfp/dcd/order-status",
3654
+ { ordId: requireString(args, "ordId") },
3655
+ privateRateLimit("dcd_get_order_state", 5)
3656
+ );
3657
+ return normalizeResponse(response);
3658
+ });
3659
+ }
3660
+ },
3661
+ {
3662
+ name: "dcd_get_orders",
3663
+ module: "earn.dcd",
3664
+ description: "Get DCD order history with optional filters. Returns up to 100 records per request. Private endpoint. Rate limit: 5 req/s.",
3665
+ isWrite: false,
3666
+ inputSchema: {
3667
+ type: "object",
3668
+ properties: {
3669
+ ordId: { type: "string", description: "Filter by specific order ID (ignores other filters when provided)" },
3670
+ productId: { type: "string", description: "Filter by product ID, e.g. BTC-USDT-260327-77000-C" },
3671
+ uly: { type: "string", description: "Filter by underlying index, e.g. BTC-USD" },
3672
+ state: {
3673
+ type: "string",
3674
+ description: "Filter by state: initial | live | pending_settle | settled | pending_redeem | redeemed | rejected"
3675
+ },
3676
+ beginId: { type: "string", description: "Return records newer than this order ID (pagination)" },
3677
+ endId: { type: "string", description: "Return records older than this order ID (pagination)" },
3678
+ begin: { type: "string", description: "Begin timestamp filter, Unix ms" },
3679
+ end: { type: "string", description: "End timestamp filter, Unix ms" },
3680
+ limit: { type: "number", description: "Results per request, max 100 (default 100)" }
3681
+ }
3682
+ },
3683
+ handler: async (rawArgs, context) => {
3684
+ const args = asRecord(rawArgs);
3685
+ return withDcdErrors(async () => {
3686
+ const response = await context.client.privateGet(
3687
+ "/api/v5/finance/sfp/dcd/order-history",
3688
+ compactObject({
3689
+ ordId: readString(args, "ordId"),
3690
+ productId: readString(args, "productId"),
3691
+ uly: readString(args, "uly"),
3692
+ state: readString(args, "state"),
3693
+ beginId: readString(args, "beginId"),
3694
+ endId: readString(args, "endId"),
3695
+ begin: readString(args, "begin"),
3696
+ end: readString(args, "end"),
3697
+ limit: readNumber(args, "limit")
3698
+ }),
3699
+ privateRateLimit("dcd_get_orders", 5)
3700
+ );
3701
+ return normalizeResponse(response);
3702
+ });
3703
+ }
3704
+ }
3705
+ ];
3706
+ }
3431
3707
  function registerAllEarnTools() {
3432
3708
  return [
3433
3709
  ...registerEarnTools(),
3434
- ...registerOnchainEarnTools()
3710
+ ...registerOnchainEarnTools(),
3711
+ ...registerDcdTools()
3435
3712
  ];
3436
3713
  }
3437
3714
  var FUTURES_INST_TYPES = ["FUTURES", "SWAP"];
@@ -4276,6 +4553,40 @@ function registerMarketTools() {
4276
4553
  );
4277
4554
  return normalize6(response);
4278
4555
  }
4556
+ },
4557
+ {
4558
+ name: "market_get_stock_tokens",
4559
+ module: "market",
4560
+ description: "Get all stock token instruments (instCategory=3). Stock tokens track real-world stock prices on OKX (e.g. AAPL-USDT-SWAP, TSLA-USDT-SWAP). Fetches all instruments of the given type and filters client-side by instCategory=3. Public endpoint. Rate limit: 20 req/s.",
4561
+ isWrite: false,
4562
+ inputSchema: {
4563
+ type: "object",
4564
+ properties: {
4565
+ instType: {
4566
+ type: "string",
4567
+ enum: ["SPOT", "SWAP"],
4568
+ description: "Instrument type. Default: SWAP"
4569
+ },
4570
+ instId: {
4571
+ type: "string",
4572
+ description: "Optional: filter by specific instrument ID, e.g. AAPL-USDT-SWAP"
4573
+ }
4574
+ },
4575
+ required: []
4576
+ },
4577
+ handler: async (rawArgs, context) => {
4578
+ const args = asRecord(rawArgs);
4579
+ const instType = readString(args, "instType") ?? "SWAP";
4580
+ const instId = readString(args, "instId");
4581
+ const response = await context.client.publicGet(
4582
+ "/api/v5/public/instruments",
4583
+ compactObject({ instType, instId }),
4584
+ publicRateLimit("market_get_stock_tokens", 20)
4585
+ );
4586
+ const data = response.data;
4587
+ const filtered = Array.isArray(data) ? data.filter((item) => item.instCategory === "3") : data;
4588
+ return normalize6({ ...response, data: filtered });
4589
+ }
4279
4590
  }
4280
4591
  ];
4281
4592
  }
@@ -6380,7 +6691,7 @@ function readCliVersion() {
6380
6691
  return "0.0.0";
6381
6692
  }
6382
6693
  var CLI_VERSION = readCliVersion();
6383
- var GIT_HASH = true ? "ecce5a7" : "dev";
6694
+ var GIT_HASH = true ? "1df90cc" : "dev";
6384
6695
  var Report = class {
6385
6696
  lines = [];
6386
6697
  add(key, value) {
@@ -6607,18 +6918,19 @@ async function checkNetwork(config, client, report) {
6607
6918
  }
6608
6919
  return passed;
6609
6920
  }
6610
- function getAuthHints(msg) {
6921
+ function getAuthHints(msg, baseUrl) {
6922
+ const accountUrl = baseUrl.replace(/\/+$/, "") + "/account/my-api";
6611
6923
  if (msg.includes("50111") || msg.includes("Invalid OK-ACCESS-KEY")) {
6612
- return ["API key is invalid or expired", "Regenerate at https://www.okx.com/account/my-api"];
6924
+ return ["API key is invalid or expired", `Regenerate at ${accountUrl}`];
6613
6925
  }
6614
6926
  if (msg.includes("50112") || msg.includes("Invalid Sign")) {
6615
- return ["Secret key or passphrase may be wrong", "Regenerate API key at https://www.okx.com/account/my-api"];
6927
+ return ["Secret key or passphrase may be wrong", `Regenerate API key at ${accountUrl}`];
6616
6928
  }
6617
6929
  if (msg.includes("50113")) {
6618
6930
  return ["Passphrase is incorrect"];
6619
6931
  }
6620
6932
  if (msg.includes("50100")) {
6621
- return ["API key lacks required permissions", "Update permissions at https://www.okx.com/account/my-api"];
6933
+ return ["API key lacks required permissions", `Update permissions at ${accountUrl}`];
6622
6934
  }
6623
6935
  return ["Check API credentials and permissions"];
6624
6936
  }
@@ -6641,7 +6953,7 @@ async function checkAuth(client, config, report) {
6641
6953
  } catch (e) {
6642
6954
  const ms = Date.now() - t1;
6643
6955
  const msg = e instanceof Error ? e.message : String(e);
6644
- const hints = getAuthHints(msg);
6956
+ const hints = getAuthHints(msg, config.baseUrl);
6645
6957
  fail("Account balance", msg, hints);
6646
6958
  passed = false;
6647
6959
  report.add("auth_api", `FAIL /account/balance ${msg} (${ms}ms)`);
@@ -6763,6 +7075,10 @@ var HELP_TREE = {
6763
7075
  "open-interest": {
6764
7076
  usage: "okx market open-interest --instType <SWAP|FUTURES|OPTION> [--instId <id>]",
6765
7077
  description: "Get open interest for instruments"
7078
+ },
7079
+ "stock-tokens": {
7080
+ usage: "okx market stock-tokens [--instType <SPOT|SWAP>] [--instId <id>]",
7081
+ description: "List all stock token instruments (instCategory=3, e.g. AAPL-USDT-SWAP)"
6766
7082
  }
6767
7083
  }
6768
7084
  },
@@ -7030,7 +7346,7 @@ var HELP_TREE = {
7030
7346
  }
7031
7347
  },
7032
7348
  earn: {
7033
- description: "Earn products \u2014 Simple Earn (savings/lending) and On-chain Earn (staking/DeFi)",
7349
+ description: "Earn products \u2014 Simple Earn, On-chain Earn, and DCD (Dual Currency Deposit)",
7034
7350
  subgroups: {
7035
7351
  savings: {
7036
7352
  description: "Simple Earn \u2014 flexible savings and lending",
@@ -7093,6 +7409,51 @@ var HELP_TREE = {
7093
7409
  description: "Get on-chain earn order history"
7094
7410
  }
7095
7411
  }
7412
+ },
7413
+ dcd: {
7414
+ description: "DCD (Dual Currency Deposit) \u2014 structured products with fixed yield",
7415
+ commands: {
7416
+ pairs: {
7417
+ usage: "okx earn dcd pairs",
7418
+ description: "List available DCD currency pairs"
7419
+ },
7420
+ products: {
7421
+ usage: "okx earn dcd products --baseCcy <ccy> --quoteCcy <ccy> --optType <C|P>\n [--minYield <n>] [--strikeNear <price>]\n [--termDays <n>] [--minTermDays <n>] [--maxTermDays <n>]\n [--expDate <YYYY-MM-DD|YYYY-MM-DDTHH:mm>]",
7422
+ description: "List active DCD products (baseCcy, quoteCcy, optType required). Client-side filters: minYield (e.g. 0.05=5%), strikeNear (\xB110%), term range, expDate"
7423
+ },
7424
+ quote: {
7425
+ usage: "okx earn dcd quote --productId <id> --sz <n> --notionalCcy <ccy>",
7426
+ description: "Request a real-time DCD quote (TTL: 30 seconds)"
7427
+ },
7428
+ buy: {
7429
+ usage: "okx earn dcd buy --quoteId <id> [--clOrdId <id>]",
7430
+ description: "[CAUTION] Execute a DCD quote to place a trade. Auto-queries order state after placement"
7431
+ },
7432
+ "quote-and-buy": {
7433
+ usage: "okx earn dcd quote-and-buy --productId <id> --sz <n> --notionalCcy <ccy> [--clOrdId <id>]",
7434
+ description: "[CAUTION] Request quote and execute immediately in one step (no confirmation \u2014 for AI agent use)"
7435
+ },
7436
+ "redeem-quote": {
7437
+ usage: "okx earn dcd redeem-quote --ordId <id>",
7438
+ description: "Request an early redemption quote for a live DCD order (TTL: 15 seconds)"
7439
+ },
7440
+ redeem: {
7441
+ usage: "okx earn dcd redeem --ordId <id> --quoteId <id>",
7442
+ description: "[CAUTION] Execute early redemption of a DCD position"
7443
+ },
7444
+ "redeem-execute": {
7445
+ usage: "okx earn dcd redeem-execute --ordId <id>",
7446
+ description: "[CAUTION] Re-quote and execute early redemption in one step (recommended for AI agent use)"
7447
+ },
7448
+ order: {
7449
+ usage: "okx earn dcd order --ordId <id>",
7450
+ description: "Query current state of a DCD order"
7451
+ },
7452
+ orders: {
7453
+ usage: "okx earn dcd orders [--ordId <id>] [--productId <id>] [--uly <uly>] [--state <state>] [--limit <n>]",
7454
+ description: "Get DCD order history. State: initial|live|pending_settle|settled|pending_redeem|redeemed|rejected"
7455
+ }
7456
+ }
7096
7457
  }
7097
7458
  }
7098
7459
  },
@@ -7415,6 +7776,21 @@ var CLI_OPTIONS = {
7415
7776
  tag: { type: "string" },
7416
7777
  allowEarlyRedeem: { type: "boolean", default: false },
7417
7778
  state: { type: "string" },
7779
+ // dcd
7780
+ quoteId: { type: "string" },
7781
+ notionalCcy: { type: "string" },
7782
+ optType: { type: "string" },
7783
+ baseCcy: { type: "string" },
7784
+ beginId: { type: "string" },
7785
+ endId: { type: "string" },
7786
+ begin: { type: "string" },
7787
+ end: { type: "string" },
7788
+ minYield: { type: "string" },
7789
+ strikeNear: { type: "string" },
7790
+ termDays: { type: "string" },
7791
+ minTermDays: { type: "string" },
7792
+ maxTermDays: { type: "string" },
7793
+ expDate: { type: "string" },
7418
7794
  // diagnostics
7419
7795
  verbose: { type: "boolean", default: false }
7420
7796
  };
@@ -7674,6 +8050,22 @@ async function cmdMarketCandles(run, instId, opts) {
7674
8050
  }))
7675
8051
  );
7676
8052
  }
8053
+ async function cmdMarketStockTokens(run, opts) {
8054
+ const result = await run("market_get_stock_tokens", { instType: opts.instType, instId: opts.instId });
8055
+ const items = getData(result);
8056
+ if (opts.json) return printJson(items);
8057
+ printTable(
8058
+ (items ?? []).slice(0, 50).map((t) => ({
8059
+ instId: t["instId"],
8060
+ instCategory: t["instCategory"],
8061
+ ctVal: t["ctVal"],
8062
+ lotSz: t["lotSz"],
8063
+ minSz: t["minSz"],
8064
+ tickSz: t["tickSz"],
8065
+ state: t["state"]
8066
+ }))
8067
+ );
8068
+ }
7677
8069
 
7678
8070
  // src/commands/account.ts
7679
8071
  import * as fs4 from "fs";
@@ -9421,10 +9813,362 @@ function cmdOnchainEarnOrderHistory(run, v) {
9421
9813
  });
9422
9814
  }
9423
9815
 
9816
+ // src/commands/dcd.ts
9817
+ function extractArray(result) {
9818
+ if (result && typeof result === "object") {
9819
+ const data = result["data"];
9820
+ if (Array.isArray(data)) return data;
9821
+ }
9822
+ return [];
9823
+ }
9824
+ function extractProducts(result) {
9825
+ if (result && typeof result === "object") {
9826
+ const data = result["data"];
9827
+ if (data && typeof data === "object" && !Array.isArray(data)) {
9828
+ const products = data["products"];
9829
+ if (Array.isArray(products)) return products;
9830
+ }
9831
+ }
9832
+ return [];
9833
+ }
9834
+ async function cmdDcdPairs(run, json) {
9835
+ const result = await run("dcd_get_currency_pairs", {});
9836
+ const data = extractArray(result);
9837
+ if (json) {
9838
+ printJson(data);
9839
+ return;
9840
+ }
9841
+ if (!data.length) {
9842
+ process.stdout.write("No currency pairs available\n");
9843
+ return;
9844
+ }
9845
+ printTable(data.map((r) => ({
9846
+ baseCcy: r["baseCcy"],
9847
+ quoteCcy: r["quoteCcy"],
9848
+ optType: r["optType"]
9849
+ })));
9850
+ }
9851
+ function filterByYield(data, minYield) {
9852
+ return data.filter((r) => {
9853
+ const y = parseFloat(r["annualizedYield"]);
9854
+ return !isNaN(y) && y >= minYield;
9855
+ });
9856
+ }
9857
+ function filterByStrike(data, ref) {
9858
+ return data.filter((r) => {
9859
+ const strike = parseFloat(r["strike"]);
9860
+ return !isNaN(strike) && Math.abs(strike - ref) / ref <= 0.1;
9861
+ });
9862
+ }
9863
+ function filterByTerm(data, termDays, minTermDays, maxTermDays) {
9864
+ const MS_PER_DAY = 864e5;
9865
+ return data.filter((r) => {
9866
+ const exp = Number(r["expTime"]);
9867
+ const start = Number(r["interestAccrualTime"]);
9868
+ if (!exp || !start) return false;
9869
+ const days = Math.round((exp - start) / MS_PER_DAY);
9870
+ if (termDays !== void 0 && days !== termDays) return false;
9871
+ if (minTermDays !== void 0 && days < minTermDays) return false;
9872
+ if (maxTermDays !== void 0 && days > maxTermDays) return false;
9873
+ return true;
9874
+ });
9875
+ }
9876
+ function filterByExpDate(data, expDate) {
9877
+ const hasTime = expDate.includes("T") || expDate.includes(" ");
9878
+ const precision = hasTime ? 13 : 10;
9879
+ const target = new Date(expDate).toISOString().slice(0, precision);
9880
+ return data.filter((r) => {
9881
+ const exp = Number(r["expTime"]);
9882
+ if (!exp) return false;
9883
+ return new Date(exp).toISOString().slice(0, precision) === target;
9884
+ });
9885
+ }
9886
+ function applyProductFilters(data, opts) {
9887
+ if (opts.minYield !== void 0) data = filterByYield(data, opts.minYield);
9888
+ if (opts.strikeNear !== void 0) data = filterByStrike(data, opts.strikeNear);
9889
+ if (opts.termDays !== void 0 || opts.minTermDays !== void 0 || opts.maxTermDays !== void 0) {
9890
+ data = filterByTerm(data, opts.termDays, opts.minTermDays, opts.maxTermDays);
9891
+ }
9892
+ if (opts.expDate !== void 0) data = filterByExpDate(data, opts.expDate);
9893
+ return data;
9894
+ }
9895
+ async function cmdDcdProducts(run, opts) {
9896
+ const result = await run("dcd_get_products", {
9897
+ baseCcy: opts.baseCcy,
9898
+ quoteCcy: opts.quoteCcy,
9899
+ optType: opts.optType
9900
+ });
9901
+ const data = applyProductFilters(extractProducts(result), opts);
9902
+ if (opts.json) {
9903
+ printJson(data);
9904
+ return;
9905
+ }
9906
+ if (!data.length) {
9907
+ process.stdout.write("No products matched\n");
9908
+ return;
9909
+ }
9910
+ printTable(data.map((r) => ({
9911
+ productId: r["productId"],
9912
+ baseCcy: r["baseCcy"],
9913
+ quoteCcy: r["quoteCcy"],
9914
+ optType: r["optType"],
9915
+ strike: r["strike"],
9916
+ // products endpoint returns decimal (e.g. 0.3423 = 34.23%) — multiply by 100
9917
+ annualizedYield: r["annualizedYield"] ? `${(parseFloat(r["annualizedYield"]) * 100).toFixed(2)}%` : "\u2014",
9918
+ minSize: r["minSize"],
9919
+ expTime: r["expTime"] ? new Date(Number(r["expTime"])).toLocaleDateString() : ""
9920
+ })));
9921
+ }
9922
+ async function cmdDcdQuote(run, opts) {
9923
+ const result = await run("dcd_request_quote", {
9924
+ productId: opts.productId,
9925
+ notionalSz: opts.notionalSz,
9926
+ notionalCcy: opts.notionalCcy
9927
+ });
9928
+ const data = extractArray(result);
9929
+ if (opts.json) {
9930
+ printJson(data);
9931
+ return;
9932
+ }
9933
+ const r = data[0];
9934
+ if (!r) {
9935
+ process.stdout.write("No quote returned\n");
9936
+ return;
9937
+ }
9938
+ printKv({
9939
+ quoteId: r["quoteId"],
9940
+ productId: r["productId"],
9941
+ notionalSz: r["notionalSz"],
9942
+ notionalCcy: r["notionalCcy"],
9943
+ // quote endpoint returns percentage directly (e.g. 18.34 = 18.34%) — no ×100 needed
9944
+ annualizedYield: r["annualizedYield"] ? `${r["annualizedYield"]}%` : "\u2014",
9945
+ absYield: r["absYield"],
9946
+ idxPx: r["idxPx"],
9947
+ validUntil: r["validUntil"] ? new Date(Number(r["validUntil"])).toLocaleString() : "\u2014"
9948
+ });
9949
+ process.stdout.write("\nQuote expires soon. Use 'earn dcd buy --quoteId <id>' to execute.\n");
9950
+ }
9951
+ async function cmdDcdBuy(run, opts) {
9952
+ const result = await run("dcd_execute_quote", {
9953
+ quoteId: opts.quoteId,
9954
+ clOrdId: opts.clOrdId
9955
+ });
9956
+ const data = extractArray(result);
9957
+ const r = data[0];
9958
+ if (!r) {
9959
+ process.stdout.write("No response data\n");
9960
+ return;
9961
+ }
9962
+ const ordId = r["ordId"];
9963
+ let stateRow;
9964
+ if (ordId) {
9965
+ try {
9966
+ const stateResult = await run("dcd_get_order_state", { ordId });
9967
+ stateRow = extractArray(stateResult)[0];
9968
+ } catch {
9969
+ }
9970
+ }
9971
+ if (opts.json) {
9972
+ printJson({ order: r, state: stateRow ?? null });
9973
+ return;
9974
+ }
9975
+ process.stdout.write("Order placed:\n");
9976
+ printKv({ ordId: r["ordId"], quoteId: r["quoteId"], state: r["state"] });
9977
+ if (stateRow) {
9978
+ process.stdout.write("\nOrder state:\n");
9979
+ printKv({
9980
+ ordId: stateRow["ordId"],
9981
+ state: stateRow["state"]
9982
+ });
9983
+ }
9984
+ }
9985
+ async function cmdDcdRedeemQuote(run, opts) {
9986
+ const result = await run("dcd_request_redeem_quote", { ordId: opts.ordId });
9987
+ const data = extractArray(result);
9988
+ if (opts.json) {
9989
+ printJson(data);
9990
+ return;
9991
+ }
9992
+ const r = data[0];
9993
+ if (!r) {
9994
+ process.stdout.write("No redeem quote returned\n");
9995
+ return;
9996
+ }
9997
+ const redeemSzRaw = r["redeemSz"];
9998
+ const redeemSzDisplay = redeemSzRaw ? `${parseFloat(redeemSzRaw).toFixed(8)} ${r["redeemCcy"]}` : "\u2014";
9999
+ const termRateRaw = r["termRate"];
10000
+ const termRateDisplay = termRateRaw ? `${termRateRaw}%` : "\u2014";
10001
+ const validUntil = r["validUntil"] ? new Date(Number(r["validUntil"])).toLocaleString() : "\u2014";
10002
+ printKv({
10003
+ ordId: r["ordId"],
10004
+ quoteId: r["quoteId"],
10005
+ redeemSz: redeemSzDisplay,
10006
+ termRate: termRateDisplay,
10007
+ validUntil
10008
+ });
10009
+ process.stdout.write("\nQuote expires soon. Use 'earn dcd redeem-execute --ordId <id>' to execute.\n");
10010
+ }
10011
+ async function cmdDcdRedeem(run, opts) {
10012
+ const result = await run("dcd_execute_redeem", { ordId: opts.ordId, quoteId: opts.quoteId });
10013
+ const data = extractArray(result);
10014
+ if (opts.json) {
10015
+ printJson(data);
10016
+ return;
10017
+ }
10018
+ const r = data[0];
10019
+ if (!r) {
10020
+ process.stdout.write("No response data\n");
10021
+ return;
10022
+ }
10023
+ printKv({ ordId: r["ordId"], state: r["state"] });
10024
+ }
10025
+ async function cmdDcdRedeemExecute(run, opts) {
10026
+ const quoteResult = await run("dcd_request_redeem_quote", { ordId: opts.ordId });
10027
+ const quoteData = extractArray(quoteResult);
10028
+ const q = quoteData[0];
10029
+ if (!q) {
10030
+ process.stdout.write("Failed to get redeem quote\n");
10031
+ return;
10032
+ }
10033
+ const redeemResult = await run("dcd_execute_redeem", {
10034
+ ordId: opts.ordId,
10035
+ quoteId: q["quoteId"]
10036
+ });
10037
+ const redeemData = extractArray(redeemResult);
10038
+ const r = redeemData[0];
10039
+ if (!r) {
10040
+ process.stdout.write("No response data\n");
10041
+ return;
10042
+ }
10043
+ if (opts.json) {
10044
+ printJson({ quote: q, redeem: r });
10045
+ return;
10046
+ }
10047
+ printKv({
10048
+ ordId: r["ordId"],
10049
+ state: r["state"],
10050
+ redeemSz: q["redeemSz"] ? `${parseFloat(q["redeemSz"]).toFixed(8)} ${q["redeemCcy"]}` : "\u2014",
10051
+ termRate: q["termRate"] ? `${q["termRate"]}%` : "\u2014",
10052
+ validUntil: q["validUntil"] ? new Date(Number(q["validUntil"])).toLocaleString() : "\u2014"
10053
+ });
10054
+ }
10055
+ async function cmdDcdOrderState(run, opts) {
10056
+ const result = await run("dcd_get_order_state", { ordId: opts.ordId });
10057
+ const data = extractArray(result);
10058
+ if (opts.json) {
10059
+ printJson(data);
10060
+ return;
10061
+ }
10062
+ const r = data[0];
10063
+ if (!r) {
10064
+ process.stdout.write("Order not found\n");
10065
+ return;
10066
+ }
10067
+ printKv({
10068
+ ordId: r["ordId"],
10069
+ state: r["state"],
10070
+ productId: r["productId"],
10071
+ strike: r["strike"],
10072
+ notionalSz: r["notionalSz"],
10073
+ settleTime: r["settleTime"] ? new Date(Number(r["settleTime"])).toLocaleDateString() : ""
10074
+ });
10075
+ }
10076
+ async function cmdDcdOrders(run, opts) {
10077
+ const result = await run("dcd_get_orders", {
10078
+ ordId: opts.ordId,
10079
+ productId: opts.productId,
10080
+ uly: opts.uly,
10081
+ state: opts.state,
10082
+ beginId: opts.beginId,
10083
+ endId: opts.endId,
10084
+ begin: opts.begin,
10085
+ end: opts.end,
10086
+ limit: opts.limit
10087
+ });
10088
+ const data = extractArray(result);
10089
+ if (opts.json) {
10090
+ printJson(data);
10091
+ return;
10092
+ }
10093
+ if (!data.length) {
10094
+ process.stdout.write("No orders found\n");
10095
+ return;
10096
+ }
10097
+ printTable(data.map((r) => ({
10098
+ ordId: r["ordId"],
10099
+ productId: r["productId"],
10100
+ state: r["state"],
10101
+ baseCcy: r["baseCcy"],
10102
+ quoteCcy: r["quoteCcy"],
10103
+ strike: r["strike"],
10104
+ notionalSz: r["notionalSz"],
10105
+ annualizedYield: r["annualizedYield"],
10106
+ yieldSz: r["yieldSz"],
10107
+ settleTime: r["settleTime"] ? new Date(Number(r["settleTime"])).toLocaleDateString() : "",
10108
+ // scheduled settlement time
10109
+ settledTime: r["settledTime"] ? new Date(Number(r["settledTime"])).toLocaleDateString() : ""
10110
+ // actual settled time (non-empty only after settlement)
10111
+ })));
10112
+ }
10113
+ async function cmdDcdQuoteAndBuy(run, opts) {
10114
+ const quoteResult = await run("dcd_request_quote", {
10115
+ productId: opts.productId,
10116
+ notionalSz: opts.notionalSz,
10117
+ notionalCcy: opts.notionalCcy
10118
+ });
10119
+ const quoteData = extractArray(quoteResult);
10120
+ const q = quoteData[0];
10121
+ if (!q) {
10122
+ process.stdout.write("No quote returned\n");
10123
+ return;
10124
+ }
10125
+ const buyResult = await run("dcd_execute_quote", {
10126
+ quoteId: q["quoteId"],
10127
+ clOrdId: opts.clOrdId
10128
+ });
10129
+ const buyData = extractArray(buyResult);
10130
+ const r = buyData[0];
10131
+ if (!r) {
10132
+ process.stdout.write("No order response\n");
10133
+ return;
10134
+ }
10135
+ const ordId = r["ordId"];
10136
+ let stateRow;
10137
+ if (ordId) {
10138
+ try {
10139
+ const stateResult = await run("dcd_get_order_state", { ordId });
10140
+ stateRow = extractArray(stateResult)[0];
10141
+ } catch {
10142
+ }
10143
+ }
10144
+ if (opts.json) {
10145
+ printJson({ quote: q, order: r, state: stateRow ?? null });
10146
+ return;
10147
+ }
10148
+ process.stdout.write("Quote:\n");
10149
+ printKv({
10150
+ quoteId: q["quoteId"],
10151
+ // quote endpoint returns percentage directly (e.g. 18.34 = 18.34%) — no ×100 needed
10152
+ annualizedYield: q["annualizedYield"] ? `${q["annualizedYield"]}%` : "\u2014",
10153
+ absYield: q["absYield"],
10154
+ notionalSz: q["notionalSz"],
10155
+ notionalCcy: q["notionalCcy"]
10156
+ });
10157
+ process.stdout.write("\nOrder placed:\n");
10158
+ printKv({ ordId: r["ordId"], quoteId: r["quoteId"], state: r["state"] ?? r["status"] });
10159
+ if (stateRow) {
10160
+ process.stdout.write("\nOrder state:\n");
10161
+ printKv({
10162
+ ordId: stateRow["ordId"],
10163
+ state: stateRow["state"]
10164
+ });
10165
+ }
10166
+ }
10167
+
9424
10168
  // src/index.ts
9425
10169
  var _require2 = createRequire2(import.meta.url);
9426
10170
  var CLI_VERSION2 = _require2("../package.json").version;
9427
- var GIT_HASH2 = true ? "ecce5a7" : "dev";
10171
+ var GIT_HASH2 = true ? "1df90cc" : "dev";
9428
10172
  function handleConfigCommand(action, rest, json, lang, force) {
9429
10173
  if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
9430
10174
  if (action === "show") return cmdConfigShow(json);
@@ -9469,6 +10213,8 @@ function handleMarketPublicCommand(run, action, rest, v, json) {
9469
10213
  if (action === "price-limit") return cmdMarketPriceLimit(run, rest[0], json);
9470
10214
  if (action === "open-interest")
9471
10215
  return cmdMarketOpenInterest(run, { instType: v.instType, instId: v.instId, json });
10216
+ if (action === "stock-tokens")
10217
+ return cmdMarketStockTokens(run, { instType: v.instType, instId: v.instId, json });
9472
10218
  }
9473
10219
  function handleMarketDataCommand(run, action, rest, v, json) {
9474
10220
  const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
@@ -9891,8 +10637,9 @@ function handleEarnCommand(run, submodule, rest, v, json) {
9891
10637
  const innerRest = rest.slice(1);
9892
10638
  if (submodule === "savings") return handleEarnSavingsCommand(run, action, innerRest, v, json);
9893
10639
  if (submodule === "onchain") return handleEarnOnchainCommand(run, action, v, json);
10640
+ if (submodule === "dcd") return handleEarnDcdCommand(run, action, v, json);
9894
10641
  process.stderr.write(`Unknown earn sub-module: ${submodule}
9895
- Valid: savings, onchain
10642
+ Valid: savings, onchain, dcd
9896
10643
  `);
9897
10644
  process.exitCode = 1;
9898
10645
  }
@@ -9920,6 +10667,70 @@ function handleEarnOnchainCommand(run, action, v, json) {
9920
10667
  `);
9921
10668
  process.exitCode = 1;
9922
10669
  }
10670
+ function parseDcdOpts(v) {
10671
+ return {
10672
+ limit: v.limit !== void 0 ? Number(v.limit) : void 0,
10673
+ minYield: v.minYield !== void 0 ? parseFloat(v.minYield) : void 0,
10674
+ strikeNear: v.strikeNear !== void 0 ? parseFloat(v.strikeNear) : void 0,
10675
+ termDays: v.termDays !== void 0 ? parseInt(v.termDays, 10) : void 0,
10676
+ minTermDays: v.minTermDays !== void 0 ? parseInt(v.minTermDays, 10) : void 0,
10677
+ maxTermDays: v.maxTermDays !== void 0 ? parseInt(v.maxTermDays, 10) : void 0
10678
+ };
10679
+ }
10680
+ function handleEarnDcdCommand(run, action, v, json) {
10681
+ const { limit, minYield, strikeNear, termDays, minTermDays, maxTermDays } = parseDcdOpts(v);
10682
+ if (action === "pairs") return cmdDcdPairs(run, json);
10683
+ if (action === "products")
10684
+ return cmdDcdProducts(run, {
10685
+ baseCcy: v.baseCcy,
10686
+ quoteCcy: v.quoteCcy,
10687
+ optType: v.optType,
10688
+ minYield,
10689
+ strikeNear,
10690
+ termDays,
10691
+ minTermDays,
10692
+ maxTermDays,
10693
+ expDate: v.expDate,
10694
+ json
10695
+ });
10696
+ if (action === "quote")
10697
+ return cmdDcdQuote(run, { productId: v.productId, notionalSz: v.sz, notionalCcy: v.notionalCcy, json });
10698
+ if (action === "buy")
10699
+ return cmdDcdBuy(run, { quoteId: v.quoteId, clOrdId: v.clOrdId, json });
10700
+ if (action === "quote-and-buy")
10701
+ return cmdDcdQuoteAndBuy(run, {
10702
+ productId: v.productId,
10703
+ notionalSz: v.sz,
10704
+ notionalCcy: v.notionalCcy,
10705
+ clOrdId: v.clOrdId,
10706
+ json
10707
+ });
10708
+ if (action === "redeem-quote")
10709
+ return cmdDcdRedeemQuote(run, { ordId: v.ordId, json });
10710
+ if (action === "redeem")
10711
+ return cmdDcdRedeem(run, { ordId: v.ordId, quoteId: v.quoteId, json });
10712
+ if (action === "redeem-execute")
10713
+ return cmdDcdRedeemExecute(run, { ordId: v.ordId, json });
10714
+ if (action === "order")
10715
+ return cmdDcdOrderState(run, { ordId: v.ordId, json });
10716
+ if (action === "orders")
10717
+ return cmdDcdOrders(run, {
10718
+ ordId: v.ordId,
10719
+ productId: v.productId,
10720
+ uly: v.uly,
10721
+ state: v.state,
10722
+ beginId: v.beginId,
10723
+ endId: v.endId,
10724
+ begin: v.begin,
10725
+ end: v.end,
10726
+ limit,
10727
+ json
10728
+ });
10729
+ process.stderr.write(`Unknown earn dcd command: ${action}
10730
+ Valid: pairs, products, quote, buy, quote-and-buy, redeem-quote, redeem, redeem-execute, order, orders
10731
+ `);
10732
+ process.exitCode = 1;
10733
+ }
9923
10734
  function outputResult(result, json) {
9924
10735
  if (json) {
9925
10736
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");