@okx_ai/okx-trade-cli 1.2.3 → 1.2.4-beta.1
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 +1263 -425
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/scripts/postinstall.js +20 -0
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",
|
|
@@ -2391,7 +2392,7 @@ function registerGridTools() {
|
|
|
2391
2392
|
algoOrdType: {
|
|
2392
2393
|
type: "string",
|
|
2393
2394
|
enum: ["grid", "contract_grid", "moon_grid"],
|
|
2394
|
-
description: "grid=Spot, contract_grid=Contract, moon_grid=Moon"
|
|
2395
|
+
description: "Grid bot type. grid=Spot, contract_grid=Contract, moon_grid=Moon. Must match the bot's actual type when filtering by algoId."
|
|
2395
2396
|
},
|
|
2396
2397
|
status: {
|
|
2397
2398
|
type: "string",
|
|
@@ -2403,7 +2404,8 @@ function registerGridTools() {
|
|
|
2403
2404
|
description: "e.g. BTC-USDT"
|
|
2404
2405
|
},
|
|
2405
2406
|
algoId: {
|
|
2406
|
-
type: "string"
|
|
2407
|
+
type: "string",
|
|
2408
|
+
description: "Grid bot algo order ID (returned by grid_create_order or grid_get_orders). This is NOT a normal trade order ID."
|
|
2407
2409
|
},
|
|
2408
2410
|
after: {
|
|
2409
2411
|
type: "string",
|
|
@@ -2451,10 +2453,11 @@ function registerGridTools() {
|
|
|
2451
2453
|
algoOrdType: {
|
|
2452
2454
|
type: "string",
|
|
2453
2455
|
enum: ["grid", "contract_grid", "moon_grid"],
|
|
2454
|
-
description: "grid=Spot, contract_grid=Contract, moon_grid=Moon"
|
|
2456
|
+
description: "Must match the bot's actual type (use the algoOrdType value returned by grid_get_orders). grid=Spot, contract_grid=Contract, moon_grid=Moon."
|
|
2455
2457
|
},
|
|
2456
2458
|
algoId: {
|
|
2457
|
-
type: "string"
|
|
2459
|
+
type: "string",
|
|
2460
|
+
description: "Grid bot algo order ID (returned by grid_create_order or grid_get_orders). This is NOT a normal trade order ID."
|
|
2458
2461
|
}
|
|
2459
2462
|
},
|
|
2460
2463
|
required: ["algoOrdType", "algoId"]
|
|
@@ -2483,10 +2486,11 @@ function registerGridTools() {
|
|
|
2483
2486
|
algoOrdType: {
|
|
2484
2487
|
type: "string",
|
|
2485
2488
|
enum: ["grid", "contract_grid", "moon_grid"],
|
|
2486
|
-
description: "grid=Spot, contract_grid=Contract, moon_grid=Moon"
|
|
2489
|
+
description: "Must match the bot's actual type (use the algoOrdType value returned by grid_get_orders). grid=Spot, contract_grid=Contract, moon_grid=Moon."
|
|
2487
2490
|
},
|
|
2488
2491
|
algoId: {
|
|
2489
|
-
type: "string"
|
|
2492
|
+
type: "string",
|
|
2493
|
+
description: "Grid bot algo order ID (returned by grid_create_order or grid_get_orders). This is NOT a normal trade order ID."
|
|
2490
2494
|
},
|
|
2491
2495
|
type: {
|
|
2492
2496
|
type: "string",
|
|
@@ -2494,7 +2498,8 @@ function registerGridTools() {
|
|
|
2494
2498
|
description: "filled=executed trades (default); live=pending orders"
|
|
2495
2499
|
},
|
|
2496
2500
|
groupId: {
|
|
2497
|
-
type: "string"
|
|
2501
|
+
type: "string",
|
|
2502
|
+
description: "Group ID \u2014 a buy-sell pair shares the same groupId. Use to filter a specific grid level."
|
|
2498
2503
|
},
|
|
2499
2504
|
after: {
|
|
2500
2505
|
type: "string",
|
|
@@ -2629,12 +2634,13 @@ function registerGridTools() {
|
|
|
2629
2634
|
type: "object",
|
|
2630
2635
|
properties: {
|
|
2631
2636
|
algoId: {
|
|
2632
|
-
type: "string"
|
|
2637
|
+
type: "string",
|
|
2638
|
+
description: "Grid bot algo order ID (returned by grid_create_order or grid_get_orders). This is NOT a normal trade order ID."
|
|
2633
2639
|
},
|
|
2634
2640
|
algoOrdType: {
|
|
2635
2641
|
type: "string",
|
|
2636
2642
|
enum: ["grid", "contract_grid", "moon_grid"],
|
|
2637
|
-
description: "grid=Spot, contract_grid=Contract, moon_grid=Moon"
|
|
2643
|
+
description: "Must match the bot's actual type (use the algoOrdType value returned by grid_get_orders). grid=Spot, contract_grid=Contract, moon_grid=Moon."
|
|
2638
2644
|
},
|
|
2639
2645
|
instId: {
|
|
2640
2646
|
type: "string",
|
|
@@ -2766,7 +2772,10 @@ function registerDcaTools() {
|
|
|
2766
2772
|
inputSchema: {
|
|
2767
2773
|
type: "object",
|
|
2768
2774
|
properties: {
|
|
2769
|
-
algoId: {
|
|
2775
|
+
algoId: {
|
|
2776
|
+
type: "string",
|
|
2777
|
+
description: "DCA bot algo order ID (returned by dca_create_order or dca_get_orders). This is NOT a normal trade order ID."
|
|
2778
|
+
}
|
|
2770
2779
|
},
|
|
2771
2780
|
required: ["algoId"]
|
|
2772
2781
|
},
|
|
@@ -2794,7 +2803,10 @@ function registerDcaTools() {
|
|
|
2794
2803
|
enum: ["active", "history"],
|
|
2795
2804
|
description: "active=running (default); history=stopped"
|
|
2796
2805
|
},
|
|
2797
|
-
algoId: {
|
|
2806
|
+
algoId: {
|
|
2807
|
+
type: "string",
|
|
2808
|
+
description: "DCA bot algo order ID (returned by dca_create_order or dca_get_orders). This is NOT a normal trade order ID."
|
|
2809
|
+
},
|
|
2798
2810
|
instId: { type: "string", description: "Filter by instrument, e.g. BTC-USDT-SWAP (optional)" },
|
|
2799
2811
|
after: { type: "string", description: "Pagination: before this algo ID" },
|
|
2800
2812
|
before: { type: "string", description: "Pagination: after this algo ID" },
|
|
@@ -2829,7 +2841,10 @@ function registerDcaTools() {
|
|
|
2829
2841
|
inputSchema: {
|
|
2830
2842
|
type: "object",
|
|
2831
2843
|
properties: {
|
|
2832
|
-
algoId: {
|
|
2844
|
+
algoId: {
|
|
2845
|
+
type: "string",
|
|
2846
|
+
description: "DCA bot algo order ID (returned by dca_create_order or dca_get_orders). This is NOT a normal trade order ID."
|
|
2847
|
+
}
|
|
2833
2848
|
},
|
|
2834
2849
|
required: ["algoId"]
|
|
2835
2850
|
},
|
|
@@ -2852,7 +2867,10 @@ function registerDcaTools() {
|
|
|
2852
2867
|
inputSchema: {
|
|
2853
2868
|
type: "object",
|
|
2854
2869
|
properties: {
|
|
2855
|
-
algoId: {
|
|
2870
|
+
algoId: {
|
|
2871
|
+
type: "string",
|
|
2872
|
+
description: "DCA bot algo order ID (returned by dca_create_order or dca_get_orders). This is NOT a normal trade order ID."
|
|
2873
|
+
},
|
|
2856
2874
|
cycleId: { type: "string", description: "Omit to list all cycles; provide to get orders within a cycle" },
|
|
2857
2875
|
after: { type: "string", description: "Pagination cursor \u2014 applies to cycle-list mode only (when cycleId is omitted)" },
|
|
2858
2876
|
before: { type: "string", description: "Pagination cursor \u2014 applies to cycle-list mode only (when cycleId is omitted)" },
|
|
@@ -3139,639 +3157,921 @@ function registerEarnTools() {
|
|
|
3139
3157
|
}
|
|
3140
3158
|
];
|
|
3141
3159
|
}
|
|
3142
|
-
|
|
3143
|
-
function normalize5(response) {
|
|
3144
|
-
return {
|
|
3145
|
-
endpoint: response.endpoint,
|
|
3146
|
-
requestTime: response.requestTime,
|
|
3147
|
-
data: response.data
|
|
3148
|
-
};
|
|
3149
|
-
}
|
|
3150
|
-
function registerFuturesTools() {
|
|
3160
|
+
function registerOnchainEarnTools() {
|
|
3151
3161
|
return [
|
|
3162
|
+
// -------------------------------------------------------------------------
|
|
3163
|
+
// Get Offers
|
|
3164
|
+
// -------------------------------------------------------------------------
|
|
3152
3165
|
{
|
|
3153
|
-
name: "
|
|
3154
|
-
module: "
|
|
3155
|
-
description: "
|
|
3156
|
-
isWrite:
|
|
3166
|
+
name: "onchain_earn_get_offers",
|
|
3167
|
+
module: "earn.onchain",
|
|
3168
|
+
description: "Get available on-chain earn (staking/DeFi) offers. Returns investment products with APY, terms, and limits. Private endpoint. Rate limit: 3 req/s.",
|
|
3169
|
+
isWrite: false,
|
|
3157
3170
|
inputSchema: {
|
|
3158
3171
|
type: "object",
|
|
3159
3172
|
properties: {
|
|
3160
|
-
|
|
3161
|
-
type: "string",
|
|
3162
|
-
description: "e.g. BTC-USDT-240329"
|
|
3163
|
-
},
|
|
3164
|
-
tdMode: {
|
|
3165
|
-
type: "string",
|
|
3166
|
-
enum: ["cross", "isolated"],
|
|
3167
|
-
description: "cross|isolated margin"
|
|
3168
|
-
},
|
|
3169
|
-
side: {
|
|
3170
|
-
type: "string",
|
|
3171
|
-
enum: ["buy", "sell"],
|
|
3172
|
-
description: "one-way: buy=open long, sell=open short (use reduceOnly=true to close); hedge: combined with posSide"
|
|
3173
|
-
},
|
|
3174
|
-
posSide: {
|
|
3175
|
-
type: "string",
|
|
3176
|
-
enum: ["long", "short", "net"],
|
|
3177
|
-
description: "net=one-way mode (default); long/short=hedge mode only"
|
|
3178
|
-
},
|
|
3179
|
-
ordType: {
|
|
3180
|
-
type: "string",
|
|
3181
|
-
enum: ["market", "limit", "post_only", "fok", "ioc"],
|
|
3182
|
-
description: "market(no px)|limit(px req)|post_only(maker)|fok(all-or-cancel)|ioc(partial fill)"
|
|
3183
|
-
},
|
|
3184
|
-
sz: {
|
|
3185
|
-
type: "string",
|
|
3186
|
-
description: "Number of contracts (NOT USDT amount). Use market_get_instruments to get ctVal for conversion."
|
|
3187
|
-
},
|
|
3188
|
-
px: {
|
|
3189
|
-
type: "string",
|
|
3190
|
-
description: "Required for limit/post_only/fok/ioc"
|
|
3191
|
-
},
|
|
3192
|
-
reduceOnly: {
|
|
3193
|
-
type: "boolean",
|
|
3194
|
-
description: "Close/reduce only, no new position (one-way mode)"
|
|
3195
|
-
},
|
|
3196
|
-
clOrdId: {
|
|
3197
|
-
type: "string",
|
|
3198
|
-
description: "Client order ID (max 32 chars)"
|
|
3199
|
-
},
|
|
3200
|
-
tpTriggerPx: {
|
|
3201
|
-
type: "string",
|
|
3202
|
-
description: "TP trigger price; places TP at tpOrdPx"
|
|
3203
|
-
},
|
|
3204
|
-
tpOrdPx: {
|
|
3173
|
+
productId: {
|
|
3205
3174
|
type: "string",
|
|
3206
|
-
description: "
|
|
3175
|
+
description: "Specific product ID to query. Omit for all offers."
|
|
3207
3176
|
},
|
|
3208
|
-
|
|
3177
|
+
protocolType: {
|
|
3209
3178
|
type: "string",
|
|
3210
|
-
description: "
|
|
3179
|
+
description: "Protocol type filter: staking, defi. Omit for all types."
|
|
3211
3180
|
},
|
|
3212
|
-
|
|
3181
|
+
ccy: {
|
|
3213
3182
|
type: "string",
|
|
3214
|
-
description: "
|
|
3183
|
+
description: "Currency filter, e.g. ETH. Omit for all currencies."
|
|
3215
3184
|
}
|
|
3216
|
-
}
|
|
3217
|
-
required: ["instId", "tdMode", "side", "ordType", "sz"]
|
|
3185
|
+
}
|
|
3218
3186
|
},
|
|
3219
3187
|
handler: async (rawArgs, context) => {
|
|
3220
3188
|
const args = asRecord(rawArgs);
|
|
3221
|
-
const
|
|
3222
|
-
|
|
3223
|
-
const tpOrdPx = readString(args, "tpOrdPx");
|
|
3224
|
-
const slTriggerPx = readString(args, "slTriggerPx");
|
|
3225
|
-
const slOrdPx = readString(args, "slOrdPx");
|
|
3226
|
-
const algoEntry = compactObject({ tpTriggerPx, tpOrdPx, slTriggerPx, slOrdPx });
|
|
3227
|
-
const attachAlgoOrds = Object.keys(algoEntry).length > 0 ? [algoEntry] : void 0;
|
|
3228
|
-
const response = await context.client.privatePost(
|
|
3229
|
-
"/api/v5/trade/order",
|
|
3189
|
+
const response = await context.client.privateGet(
|
|
3190
|
+
"/api/v5/finance/staking-defi/offers",
|
|
3230
3191
|
compactObject({
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
posSide: readString(args, "posSide"),
|
|
3235
|
-
ordType: requireString(args, "ordType"),
|
|
3236
|
-
sz: requireString(args, "sz"),
|
|
3237
|
-
px: readString(args, "px"),
|
|
3238
|
-
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3239
|
-
clOrdId: readString(args, "clOrdId"),
|
|
3240
|
-
tag: context.config.sourceTag,
|
|
3241
|
-
attachAlgoOrds
|
|
3192
|
+
productId: readString(args, "productId"),
|
|
3193
|
+
protocolType: readString(args, "protocolType"),
|
|
3194
|
+
ccy: readString(args, "ccy")
|
|
3242
3195
|
}),
|
|
3243
|
-
privateRateLimit("
|
|
3196
|
+
privateRateLimit("onchain_earn_get_offers", 3)
|
|
3244
3197
|
);
|
|
3245
|
-
return
|
|
3198
|
+
return normalizeResponse(response);
|
|
3246
3199
|
}
|
|
3247
3200
|
},
|
|
3201
|
+
// -------------------------------------------------------------------------
|
|
3202
|
+
// Purchase
|
|
3203
|
+
// -------------------------------------------------------------------------
|
|
3248
3204
|
{
|
|
3249
|
-
name: "
|
|
3250
|
-
module: "
|
|
3251
|
-
description: "
|
|
3205
|
+
name: "onchain_earn_purchase",
|
|
3206
|
+
module: "earn.onchain",
|
|
3207
|
+
description: "Purchase on-chain earn (staking/DeFi) product. [CAUTION] Moves real funds into staking/DeFi product. Not supported in demo/simulated trading mode. Private endpoint. Rate limit: 2 req/s.",
|
|
3252
3208
|
isWrite: true,
|
|
3253
3209
|
inputSchema: {
|
|
3254
3210
|
type: "object",
|
|
3255
3211
|
properties: {
|
|
3256
|
-
|
|
3212
|
+
productId: {
|
|
3257
3213
|
type: "string",
|
|
3258
|
-
description: "
|
|
3214
|
+
description: "Product ID to purchase"
|
|
3259
3215
|
},
|
|
3260
|
-
|
|
3261
|
-
type: "
|
|
3216
|
+
investData: {
|
|
3217
|
+
type: "array",
|
|
3218
|
+
description: "Investment data array: [{ccy, amt}]. Each item specifies currency and amount.",
|
|
3219
|
+
items: {
|
|
3220
|
+
type: "object",
|
|
3221
|
+
properties: {
|
|
3222
|
+
ccy: { type: "string", description: "Currency, e.g. ETH" },
|
|
3223
|
+
amt: { type: "string", description: "Amount to invest" }
|
|
3224
|
+
},
|
|
3225
|
+
required: ["ccy", "amt"]
|
|
3226
|
+
}
|
|
3262
3227
|
},
|
|
3263
|
-
|
|
3228
|
+
term: {
|
|
3264
3229
|
type: "string",
|
|
3265
|
-
description: "
|
|
3230
|
+
description: "Investment term in days. Required for fixed-term products."
|
|
3231
|
+
},
|
|
3232
|
+
tag: {
|
|
3233
|
+
type: "string",
|
|
3234
|
+
description: "Order tag for tracking (optional)."
|
|
3266
3235
|
}
|
|
3267
3236
|
},
|
|
3268
|
-
required: ["
|
|
3237
|
+
required: ["productId", "investData"]
|
|
3269
3238
|
},
|
|
3270
3239
|
handler: async (rawArgs, context) => {
|
|
3240
|
+
assertNotDemo(context.config, "onchain_earn_purchase");
|
|
3271
3241
|
const args = asRecord(rawArgs);
|
|
3272
3242
|
const response = await context.client.privatePost(
|
|
3273
|
-
"/api/v5/
|
|
3243
|
+
"/api/v5/finance/staking-defi/purchase",
|
|
3274
3244
|
compactObject({
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3245
|
+
productId: requireString(args, "productId"),
|
|
3246
|
+
investData: args.investData,
|
|
3247
|
+
term: readString(args, "term"),
|
|
3248
|
+
tag: readString(args, "tag")
|
|
3278
3249
|
}),
|
|
3279
|
-
privateRateLimit("
|
|
3250
|
+
privateRateLimit("onchain_earn_purchase", 2)
|
|
3280
3251
|
);
|
|
3281
|
-
return
|
|
3252
|
+
return normalizeResponse(response);
|
|
3282
3253
|
}
|
|
3283
3254
|
},
|
|
3255
|
+
// -------------------------------------------------------------------------
|
|
3256
|
+
// Redeem
|
|
3257
|
+
// -------------------------------------------------------------------------
|
|
3284
3258
|
{
|
|
3285
|
-
name: "
|
|
3286
|
-
module: "
|
|
3287
|
-
description: "
|
|
3288
|
-
isWrite:
|
|
3259
|
+
name: "onchain_earn_redeem",
|
|
3260
|
+
module: "earn.onchain",
|
|
3261
|
+
description: "Redeem on-chain earn (staking/DeFi) investment. [CAUTION] Withdraws funds from staking/DeFi product. Some products may have lock periods. Not supported in demo mode. Private endpoint. Rate limit: 2 req/s.",
|
|
3262
|
+
isWrite: true,
|
|
3289
3263
|
inputSchema: {
|
|
3290
3264
|
type: "object",
|
|
3291
3265
|
properties: {
|
|
3292
|
-
instId: {
|
|
3293
|
-
type: "string",
|
|
3294
|
-
description: "e.g. BTC-USDT-240329"
|
|
3295
|
-
},
|
|
3296
3266
|
ordId: {
|
|
3297
3267
|
type: "string",
|
|
3298
|
-
description: "
|
|
3268
|
+
description: "Order ID to redeem"
|
|
3299
3269
|
},
|
|
3300
|
-
|
|
3270
|
+
protocolType: {
|
|
3301
3271
|
type: "string",
|
|
3302
|
-
description: "
|
|
3272
|
+
description: "Protocol type: staking, defi"
|
|
3273
|
+
},
|
|
3274
|
+
allowEarlyRedeem: {
|
|
3275
|
+
type: "boolean",
|
|
3276
|
+
description: "Allow early redemption for fixed-term products (may incur penalties). Default false."
|
|
3303
3277
|
}
|
|
3304
3278
|
},
|
|
3305
|
-
required: ["
|
|
3279
|
+
required: ["ordId", "protocolType"]
|
|
3306
3280
|
},
|
|
3307
3281
|
handler: async (rawArgs, context) => {
|
|
3282
|
+
assertNotDemo(context.config, "onchain_earn_redeem");
|
|
3308
3283
|
const args = asRecord(rawArgs);
|
|
3309
|
-
const response = await context.client.
|
|
3310
|
-
"/api/v5/
|
|
3284
|
+
const response = await context.client.privatePost(
|
|
3285
|
+
"/api/v5/finance/staking-defi/redeem",
|
|
3311
3286
|
compactObject({
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3287
|
+
ordId: requireString(args, "ordId"),
|
|
3288
|
+
protocolType: requireString(args, "protocolType"),
|
|
3289
|
+
allowEarlyRedeem: readBoolean(args, "allowEarlyRedeem")
|
|
3315
3290
|
}),
|
|
3316
|
-
privateRateLimit("
|
|
3291
|
+
privateRateLimit("onchain_earn_redeem", 2)
|
|
3317
3292
|
);
|
|
3318
|
-
return
|
|
3293
|
+
return normalizeResponse(response);
|
|
3319
3294
|
}
|
|
3320
3295
|
},
|
|
3296
|
+
// -------------------------------------------------------------------------
|
|
3297
|
+
// Cancel
|
|
3298
|
+
// -------------------------------------------------------------------------
|
|
3321
3299
|
{
|
|
3322
|
-
name: "
|
|
3323
|
-
module: "
|
|
3324
|
-
description: "
|
|
3325
|
-
isWrite:
|
|
3300
|
+
name: "onchain_earn_cancel",
|
|
3301
|
+
module: "earn.onchain",
|
|
3302
|
+
description: "Cancel pending on-chain earn purchase. [CAUTION] Cancels a pending investment order. Not supported in demo mode. Private endpoint. Rate limit: 2 req/s.",
|
|
3303
|
+
isWrite: true,
|
|
3326
3304
|
inputSchema: {
|
|
3327
3305
|
type: "object",
|
|
3328
3306
|
properties: {
|
|
3329
|
-
|
|
3307
|
+
ordId: {
|
|
3330
3308
|
type: "string",
|
|
3331
|
-
|
|
3332
|
-
description: "open=active, history=7d, archive=3mo"
|
|
3309
|
+
description: "Order ID to cancel"
|
|
3333
3310
|
},
|
|
3334
|
-
|
|
3311
|
+
protocolType: {
|
|
3335
3312
|
type: "string",
|
|
3336
|
-
|
|
3337
|
-
|
|
3313
|
+
description: "Protocol type: staking, defi"
|
|
3314
|
+
}
|
|
3315
|
+
},
|
|
3316
|
+
required: ["ordId", "protocolType"]
|
|
3317
|
+
},
|
|
3318
|
+
handler: async (rawArgs, context) => {
|
|
3319
|
+
assertNotDemo(context.config, "onchain_earn_cancel");
|
|
3320
|
+
const args = asRecord(rawArgs);
|
|
3321
|
+
const response = await context.client.privatePost(
|
|
3322
|
+
"/api/v5/finance/staking-defi/cancel",
|
|
3323
|
+
{
|
|
3324
|
+
ordId: requireString(args, "ordId"),
|
|
3325
|
+
protocolType: requireString(args, "protocolType")
|
|
3338
3326
|
},
|
|
3339
|
-
|
|
3340
|
-
type: "string",
|
|
3341
|
-
description: "e.g. BTC-USDT-240329"
|
|
3342
|
-
},
|
|
3343
|
-
ordType: {
|
|
3344
|
-
type: "string",
|
|
3345
|
-
description: "Order type filter"
|
|
3346
|
-
},
|
|
3347
|
-
state: {
|
|
3348
|
-
type: "string",
|
|
3349
|
-
description: "canceled|filled"
|
|
3350
|
-
},
|
|
3351
|
-
after: {
|
|
3352
|
-
type: "string",
|
|
3353
|
-
description: "Pagination: before this order ID"
|
|
3354
|
-
},
|
|
3355
|
-
before: {
|
|
3356
|
-
type: "string",
|
|
3357
|
-
description: "Pagination: after this order ID"
|
|
3358
|
-
},
|
|
3359
|
-
begin: {
|
|
3360
|
-
type: "string",
|
|
3361
|
-
description: "Start time (ms)"
|
|
3362
|
-
},
|
|
3363
|
-
end: {
|
|
3364
|
-
type: "string",
|
|
3365
|
-
description: "End time (ms)"
|
|
3366
|
-
},
|
|
3367
|
-
limit: {
|
|
3368
|
-
type: "number",
|
|
3369
|
-
description: "Max results (default 100)"
|
|
3370
|
-
}
|
|
3371
|
-
}
|
|
3372
|
-
},
|
|
3373
|
-
handler: async (rawArgs, context) => {
|
|
3374
|
-
const args = asRecord(rawArgs);
|
|
3375
|
-
const status = readString(args, "status") ?? "open";
|
|
3376
|
-
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3377
|
-
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3378
|
-
const path4 = status === "archive" ? "/api/v5/trade/orders-history-archive" : status === "history" ? "/api/v5/trade/orders-history" : "/api/v5/trade/orders-pending";
|
|
3379
|
-
const response = await context.client.privateGet(
|
|
3380
|
-
path4,
|
|
3381
|
-
compactObject({
|
|
3382
|
-
instType,
|
|
3383
|
-
instId: readString(args, "instId"),
|
|
3384
|
-
ordType: readString(args, "ordType"),
|
|
3385
|
-
state: readString(args, "state"),
|
|
3386
|
-
after: readString(args, "after"),
|
|
3387
|
-
before: readString(args, "before"),
|
|
3388
|
-
begin: readString(args, "begin"),
|
|
3389
|
-
end: readString(args, "end"),
|
|
3390
|
-
limit: readNumber(args, "limit")
|
|
3391
|
-
}),
|
|
3392
|
-
privateRateLimit("futures_get_orders", 20)
|
|
3327
|
+
privateRateLimit("onchain_earn_cancel", 2)
|
|
3393
3328
|
);
|
|
3394
|
-
return
|
|
3329
|
+
return normalizeResponse(response);
|
|
3395
3330
|
}
|
|
3396
3331
|
},
|
|
3332
|
+
// -------------------------------------------------------------------------
|
|
3333
|
+
// Get Active Orders
|
|
3334
|
+
// -------------------------------------------------------------------------
|
|
3397
3335
|
{
|
|
3398
|
-
name: "
|
|
3399
|
-
module: "
|
|
3400
|
-
description: "Get
|
|
3336
|
+
name: "onchain_earn_get_active_orders",
|
|
3337
|
+
module: "earn.onchain",
|
|
3338
|
+
description: "Get active on-chain earn orders. Returns current staking/DeFi investments. Private endpoint. Rate limit: 3 req/s.",
|
|
3401
3339
|
isWrite: false,
|
|
3402
3340
|
inputSchema: {
|
|
3403
3341
|
type: "object",
|
|
3404
3342
|
properties: {
|
|
3405
|
-
|
|
3343
|
+
productId: {
|
|
3406
3344
|
type: "string",
|
|
3407
|
-
|
|
3408
|
-
description: "FUTURES (default) or SWAP"
|
|
3345
|
+
description: "Filter by product ID. Omit for all."
|
|
3409
3346
|
},
|
|
3410
|
-
|
|
3347
|
+
protocolType: {
|
|
3411
3348
|
type: "string",
|
|
3412
|
-
description: "
|
|
3349
|
+
description: "Filter by protocol type: staking, defi. Omit for all."
|
|
3413
3350
|
},
|
|
3414
|
-
|
|
3415
|
-
type: "string"
|
|
3351
|
+
ccy: {
|
|
3352
|
+
type: "string",
|
|
3353
|
+
description: "Filter by currency, e.g. ETH. Omit for all."
|
|
3354
|
+
},
|
|
3355
|
+
state: {
|
|
3356
|
+
type: "string",
|
|
3357
|
+
description: "Filter by state: 8 (pending), 13 (cancelling), 9 (onchain), 1 (earning), 2 (redeeming). Omit for all."
|
|
3416
3358
|
}
|
|
3417
3359
|
}
|
|
3418
3360
|
},
|
|
3419
3361
|
handler: async (rawArgs, context) => {
|
|
3420
3362
|
const args = asRecord(rawArgs);
|
|
3421
|
-
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3422
|
-
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3423
3363
|
const response = await context.client.privateGet(
|
|
3424
|
-
"/api/v5/
|
|
3364
|
+
"/api/v5/finance/staking-defi/orders-active",
|
|
3425
3365
|
compactObject({
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3366
|
+
productId: readString(args, "productId"),
|
|
3367
|
+
protocolType: readString(args, "protocolType"),
|
|
3368
|
+
ccy: readString(args, "ccy"),
|
|
3369
|
+
state: readString(args, "state")
|
|
3429
3370
|
}),
|
|
3430
|
-
privateRateLimit("
|
|
3371
|
+
privateRateLimit("onchain_earn_get_active_orders", 3)
|
|
3431
3372
|
);
|
|
3432
|
-
return
|
|
3373
|
+
return normalizeResponse(response);
|
|
3433
3374
|
}
|
|
3434
3375
|
},
|
|
3376
|
+
// -------------------------------------------------------------------------
|
|
3377
|
+
// Get Order History
|
|
3378
|
+
// -------------------------------------------------------------------------
|
|
3435
3379
|
{
|
|
3436
|
-
name: "
|
|
3437
|
-
module: "
|
|
3438
|
-
description: "Get
|
|
3380
|
+
name: "onchain_earn_get_order_history",
|
|
3381
|
+
module: "earn.onchain",
|
|
3382
|
+
description: "Get on-chain earn order history. Returns past staking/DeFi investments including redeemed orders. Private endpoint. Rate limit: 3 req/s.",
|
|
3439
3383
|
isWrite: false,
|
|
3440
3384
|
inputSchema: {
|
|
3441
3385
|
type: "object",
|
|
3442
3386
|
properties: {
|
|
3443
|
-
|
|
3444
|
-
type: "boolean",
|
|
3445
|
-
description: "true=up to 3 months; false=last 3 days (default)"
|
|
3446
|
-
},
|
|
3447
|
-
instType: {
|
|
3387
|
+
productId: {
|
|
3448
3388
|
type: "string",
|
|
3449
|
-
|
|
3450
|
-
description: "FUTURES (default) or SWAP"
|
|
3389
|
+
description: "Filter by product ID. Omit for all."
|
|
3451
3390
|
},
|
|
3452
|
-
|
|
3391
|
+
protocolType: {
|
|
3453
3392
|
type: "string",
|
|
3454
|
-
description: "
|
|
3393
|
+
description: "Filter by protocol type: staking, defi. Omit for all."
|
|
3455
3394
|
},
|
|
3456
|
-
|
|
3395
|
+
ccy: {
|
|
3457
3396
|
type: "string",
|
|
3458
|
-
description: "
|
|
3397
|
+
description: "Filter by currency, e.g. ETH. Omit for all."
|
|
3459
3398
|
},
|
|
3460
3399
|
after: {
|
|
3461
3400
|
type: "string",
|
|
3462
|
-
description: "Pagination: before this
|
|
3401
|
+
description: "Pagination: return results before this order ID"
|
|
3463
3402
|
},
|
|
3464
3403
|
before: {
|
|
3465
3404
|
type: "string",
|
|
3466
|
-
description: "Pagination: after this
|
|
3467
|
-
},
|
|
3468
|
-
begin: {
|
|
3469
|
-
type: "string",
|
|
3470
|
-
description: "Start time (ms)"
|
|
3471
|
-
},
|
|
3472
|
-
end: {
|
|
3473
|
-
type: "string",
|
|
3474
|
-
description: "End time (ms)"
|
|
3405
|
+
description: "Pagination: return results after this order ID"
|
|
3475
3406
|
},
|
|
3476
3407
|
limit: {
|
|
3477
|
-
type: "
|
|
3478
|
-
description: "Max results (default 100
|
|
3408
|
+
type: "string",
|
|
3409
|
+
description: "Max results to return (default 100, max 100)"
|
|
3479
3410
|
}
|
|
3480
3411
|
}
|
|
3481
3412
|
},
|
|
3482
3413
|
handler: async (rawArgs, context) => {
|
|
3483
3414
|
const args = asRecord(rawArgs);
|
|
3484
|
-
const archive = readBoolean(args, "archive") ?? false;
|
|
3485
|
-
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3486
|
-
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3487
|
-
const path4 = archive ? "/api/v5/trade/fills-history" : "/api/v5/trade/fills";
|
|
3488
3415
|
const response = await context.client.privateGet(
|
|
3489
|
-
|
|
3416
|
+
"/api/v5/finance/staking-defi/orders-history",
|
|
3490
3417
|
compactObject({
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3418
|
+
productId: readString(args, "productId"),
|
|
3419
|
+
protocolType: readString(args, "protocolType"),
|
|
3420
|
+
ccy: readString(args, "ccy"),
|
|
3494
3421
|
after: readString(args, "after"),
|
|
3495
3422
|
before: readString(args, "before"),
|
|
3496
|
-
|
|
3497
|
-
end: readString(args, "end"),
|
|
3498
|
-
limit: readNumber(args, "limit") ?? (archive ? 20 : void 0)
|
|
3423
|
+
limit: readString(args, "limit")
|
|
3499
3424
|
}),
|
|
3500
|
-
privateRateLimit("
|
|
3425
|
+
privateRateLimit("onchain_earn_get_order_history", 3)
|
|
3501
3426
|
);
|
|
3502
|
-
return
|
|
3427
|
+
return normalizeResponse(response);
|
|
3503
3428
|
}
|
|
3504
3429
|
}
|
|
3505
3430
|
];
|
|
3506
3431
|
}
|
|
3507
|
-
|
|
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() {
|
|
3508
3476
|
return [
|
|
3509
|
-
// -------------------------------------------------------------------------
|
|
3510
|
-
// Get Offers
|
|
3511
|
-
// -------------------------------------------------------------------------
|
|
3512
3477
|
{
|
|
3513
|
-
name: "
|
|
3514
|
-
module: "earn.
|
|
3515
|
-
description: "Get available
|
|
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.",
|
|
3516
3498
|
isWrite: false,
|
|
3517
3499
|
inputSchema: {
|
|
3518
3500
|
type: "object",
|
|
3519
3501
|
properties: {
|
|
3520
|
-
|
|
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: {
|
|
3521
3673
|
type: "string",
|
|
3522
|
-
description: "
|
|
3674
|
+
description: "Filter by state: initial | live | pending_settle | settled | pending_redeem | redeemed | rejected"
|
|
3523
3675
|
},
|
|
3524
|
-
|
|
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
|
+
}
|
|
3707
|
+
function registerAllEarnTools() {
|
|
3708
|
+
return [
|
|
3709
|
+
...registerEarnTools(),
|
|
3710
|
+
...registerOnchainEarnTools(),
|
|
3711
|
+
...registerDcdTools()
|
|
3712
|
+
];
|
|
3713
|
+
}
|
|
3714
|
+
var FUTURES_INST_TYPES = ["FUTURES", "SWAP"];
|
|
3715
|
+
function normalize5(response) {
|
|
3716
|
+
return {
|
|
3717
|
+
endpoint: response.endpoint,
|
|
3718
|
+
requestTime: response.requestTime,
|
|
3719
|
+
data: response.data
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
function registerFuturesTools() {
|
|
3723
|
+
return [
|
|
3724
|
+
{
|
|
3725
|
+
name: "futures_place_order",
|
|
3726
|
+
module: "futures",
|
|
3727
|
+
description: "Place a FUTURES delivery contract order (e.g. instId: BTC-USDT-240329). Optionally attach TP/SL via tpTriggerPx/slTriggerPx. [CAUTION] Executes real trades. Private endpoint. Rate limit: 60 req/s.",
|
|
3728
|
+
isWrite: true,
|
|
3729
|
+
inputSchema: {
|
|
3730
|
+
type: "object",
|
|
3731
|
+
properties: {
|
|
3732
|
+
instId: {
|
|
3525
3733
|
type: "string",
|
|
3526
|
-
description: "
|
|
3734
|
+
description: "e.g. BTC-USDT-240329"
|
|
3527
3735
|
},
|
|
3528
|
-
|
|
3736
|
+
tdMode: {
|
|
3529
3737
|
type: "string",
|
|
3530
|
-
|
|
3738
|
+
enum: ["cross", "isolated"],
|
|
3739
|
+
description: "cross|isolated margin"
|
|
3740
|
+
},
|
|
3741
|
+
side: {
|
|
3742
|
+
type: "string",
|
|
3743
|
+
enum: ["buy", "sell"],
|
|
3744
|
+
description: "one-way: buy=open long, sell=open short (use reduceOnly=true to close); hedge: combined with posSide"
|
|
3745
|
+
},
|
|
3746
|
+
posSide: {
|
|
3747
|
+
type: "string",
|
|
3748
|
+
enum: ["long", "short", "net"],
|
|
3749
|
+
description: "net=one-way mode (default); long/short=hedge mode only"
|
|
3750
|
+
},
|
|
3751
|
+
ordType: {
|
|
3752
|
+
type: "string",
|
|
3753
|
+
enum: ["market", "limit", "post_only", "fok", "ioc"],
|
|
3754
|
+
description: "market(no px)|limit(px req)|post_only(maker)|fok(all-or-cancel)|ioc(partial fill)"
|
|
3755
|
+
},
|
|
3756
|
+
sz: {
|
|
3757
|
+
type: "string",
|
|
3758
|
+
description: "Number of contracts (NOT USDT amount). Use market_get_instruments to get ctVal for conversion."
|
|
3759
|
+
},
|
|
3760
|
+
px: {
|
|
3761
|
+
type: "string",
|
|
3762
|
+
description: "Required for limit/post_only/fok/ioc"
|
|
3763
|
+
},
|
|
3764
|
+
reduceOnly: {
|
|
3765
|
+
type: "boolean",
|
|
3766
|
+
description: "Close/reduce only, no new position (one-way mode)"
|
|
3767
|
+
},
|
|
3768
|
+
clOrdId: {
|
|
3769
|
+
type: "string",
|
|
3770
|
+
description: "Client order ID (max 32 chars)"
|
|
3771
|
+
},
|
|
3772
|
+
tpTriggerPx: {
|
|
3773
|
+
type: "string",
|
|
3774
|
+
description: "TP trigger price; places TP at tpOrdPx"
|
|
3775
|
+
},
|
|
3776
|
+
tpOrdPx: {
|
|
3777
|
+
type: "string",
|
|
3778
|
+
description: "TP order price; -1=market"
|
|
3779
|
+
},
|
|
3780
|
+
slTriggerPx: {
|
|
3781
|
+
type: "string",
|
|
3782
|
+
description: "SL trigger price; places SL at slOrdPx"
|
|
3783
|
+
},
|
|
3784
|
+
slOrdPx: {
|
|
3785
|
+
type: "string",
|
|
3786
|
+
description: "SL order price; -1=market"
|
|
3531
3787
|
}
|
|
3532
|
-
}
|
|
3788
|
+
},
|
|
3789
|
+
required: ["instId", "tdMode", "side", "ordType", "sz"]
|
|
3533
3790
|
},
|
|
3534
3791
|
handler: async (rawArgs, context) => {
|
|
3535
3792
|
const args = asRecord(rawArgs);
|
|
3536
|
-
const
|
|
3537
|
-
|
|
3793
|
+
const reduceOnly = args.reduceOnly;
|
|
3794
|
+
const tpTriggerPx = readString(args, "tpTriggerPx");
|
|
3795
|
+
const tpOrdPx = readString(args, "tpOrdPx");
|
|
3796
|
+
const slTriggerPx = readString(args, "slTriggerPx");
|
|
3797
|
+
const slOrdPx = readString(args, "slOrdPx");
|
|
3798
|
+
const algoEntry = compactObject({ tpTriggerPx, tpOrdPx, slTriggerPx, slOrdPx });
|
|
3799
|
+
const attachAlgoOrds = Object.keys(algoEntry).length > 0 ? [algoEntry] : void 0;
|
|
3800
|
+
const response = await context.client.privatePost(
|
|
3801
|
+
"/api/v5/trade/order",
|
|
3538
3802
|
compactObject({
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3803
|
+
instId: requireString(args, "instId"),
|
|
3804
|
+
tdMode: requireString(args, "tdMode"),
|
|
3805
|
+
side: requireString(args, "side"),
|
|
3806
|
+
posSide: readString(args, "posSide"),
|
|
3807
|
+
ordType: requireString(args, "ordType"),
|
|
3808
|
+
sz: requireString(args, "sz"),
|
|
3809
|
+
px: readString(args, "px"),
|
|
3810
|
+
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3811
|
+
clOrdId: readString(args, "clOrdId"),
|
|
3812
|
+
tag: context.config.sourceTag,
|
|
3813
|
+
attachAlgoOrds
|
|
3542
3814
|
}),
|
|
3543
|
-
privateRateLimit("
|
|
3815
|
+
privateRateLimit("futures_place_order", 60)
|
|
3544
3816
|
);
|
|
3545
|
-
return
|
|
3817
|
+
return normalize5(response);
|
|
3546
3818
|
}
|
|
3547
3819
|
},
|
|
3548
|
-
// -------------------------------------------------------------------------
|
|
3549
|
-
// Purchase
|
|
3550
|
-
// -------------------------------------------------------------------------
|
|
3551
3820
|
{
|
|
3552
|
-
name: "
|
|
3553
|
-
module: "
|
|
3554
|
-
description: "
|
|
3821
|
+
name: "futures_cancel_order",
|
|
3822
|
+
module: "futures",
|
|
3823
|
+
description: "Cancel an unfilled FUTURES delivery order. Private endpoint. Rate limit: 60 req/s.",
|
|
3555
3824
|
isWrite: true,
|
|
3556
3825
|
inputSchema: {
|
|
3557
3826
|
type: "object",
|
|
3558
3827
|
properties: {
|
|
3559
|
-
|
|
3828
|
+
instId: {
|
|
3560
3829
|
type: "string",
|
|
3561
|
-
description: "
|
|
3562
|
-
},
|
|
3563
|
-
investData: {
|
|
3564
|
-
type: "array",
|
|
3565
|
-
description: "Investment data array: [{ccy, amt}]. Each item specifies currency and amount.",
|
|
3566
|
-
items: {
|
|
3567
|
-
type: "object",
|
|
3568
|
-
properties: {
|
|
3569
|
-
ccy: { type: "string", description: "Currency, e.g. ETH" },
|
|
3570
|
-
amt: { type: "string", description: "Amount to invest" }
|
|
3571
|
-
},
|
|
3572
|
-
required: ["ccy", "amt"]
|
|
3573
|
-
}
|
|
3830
|
+
description: "e.g. BTC-USDT-240329"
|
|
3574
3831
|
},
|
|
3575
|
-
|
|
3576
|
-
type: "string"
|
|
3577
|
-
description: "Investment term in days. Required for fixed-term products."
|
|
3832
|
+
ordId: {
|
|
3833
|
+
type: "string"
|
|
3578
3834
|
},
|
|
3579
|
-
|
|
3835
|
+
clOrdId: {
|
|
3580
3836
|
type: "string",
|
|
3581
|
-
description: "
|
|
3837
|
+
description: "Client order ID"
|
|
3582
3838
|
}
|
|
3583
3839
|
},
|
|
3584
|
-
required: ["
|
|
3840
|
+
required: ["instId"]
|
|
3585
3841
|
},
|
|
3586
3842
|
handler: async (rawArgs, context) => {
|
|
3587
|
-
assertNotDemo(context.config, "onchain_earn_purchase");
|
|
3588
3843
|
const args = asRecord(rawArgs);
|
|
3589
3844
|
const response = await context.client.privatePost(
|
|
3590
|
-
"/api/v5/
|
|
3845
|
+
"/api/v5/trade/cancel-order",
|
|
3591
3846
|
compactObject({
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
tag: readString(args, "tag")
|
|
3847
|
+
instId: requireString(args, "instId"),
|
|
3848
|
+
ordId: readString(args, "ordId"),
|
|
3849
|
+
clOrdId: readString(args, "clOrdId")
|
|
3596
3850
|
}),
|
|
3597
|
-
privateRateLimit("
|
|
3851
|
+
privateRateLimit("futures_cancel_order", 60)
|
|
3598
3852
|
);
|
|
3599
|
-
return
|
|
3853
|
+
return normalize5(response);
|
|
3600
3854
|
}
|
|
3601
3855
|
},
|
|
3602
|
-
// -------------------------------------------------------------------------
|
|
3603
|
-
// Redeem
|
|
3604
|
-
// -------------------------------------------------------------------------
|
|
3605
3856
|
{
|
|
3606
|
-
name: "
|
|
3607
|
-
module: "
|
|
3608
|
-
description: "
|
|
3609
|
-
isWrite:
|
|
3857
|
+
name: "futures_get_order",
|
|
3858
|
+
module: "futures",
|
|
3859
|
+
description: "Get details of a single FUTURES delivery order by ordId or clOrdId. Private endpoint. Rate limit: 60 req/s.",
|
|
3860
|
+
isWrite: false,
|
|
3610
3861
|
inputSchema: {
|
|
3611
3862
|
type: "object",
|
|
3612
3863
|
properties: {
|
|
3613
|
-
|
|
3864
|
+
instId: {
|
|
3614
3865
|
type: "string",
|
|
3615
|
-
description: "
|
|
3866
|
+
description: "e.g. BTC-USDT-240329"
|
|
3616
3867
|
},
|
|
3617
|
-
|
|
3868
|
+
ordId: {
|
|
3618
3869
|
type: "string",
|
|
3619
|
-
description: "
|
|
3870
|
+
description: "Provide ordId or clOrdId"
|
|
3620
3871
|
},
|
|
3621
|
-
|
|
3622
|
-
type: "
|
|
3623
|
-
description: "
|
|
3872
|
+
clOrdId: {
|
|
3873
|
+
type: "string",
|
|
3874
|
+
description: "Provide ordId or clOrdId"
|
|
3624
3875
|
}
|
|
3625
3876
|
},
|
|
3626
|
-
required: ["
|
|
3877
|
+
required: ["instId"]
|
|
3627
3878
|
},
|
|
3628
3879
|
handler: async (rawArgs, context) => {
|
|
3629
|
-
assertNotDemo(context.config, "onchain_earn_redeem");
|
|
3630
3880
|
const args = asRecord(rawArgs);
|
|
3631
|
-
const response = await context.client.
|
|
3632
|
-
"/api/v5/
|
|
3881
|
+
const response = await context.client.privateGet(
|
|
3882
|
+
"/api/v5/trade/order",
|
|
3633
3883
|
compactObject({
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3884
|
+
instId: requireString(args, "instId"),
|
|
3885
|
+
ordId: readString(args, "ordId"),
|
|
3886
|
+
clOrdId: readString(args, "clOrdId")
|
|
3637
3887
|
}),
|
|
3638
|
-
privateRateLimit("
|
|
3888
|
+
privateRateLimit("futures_get_order", 60)
|
|
3639
3889
|
);
|
|
3640
|
-
return
|
|
3890
|
+
return normalize5(response);
|
|
3641
3891
|
}
|
|
3642
3892
|
},
|
|
3643
|
-
// -------------------------------------------------------------------------
|
|
3644
|
-
// Cancel
|
|
3645
|
-
// -------------------------------------------------------------------------
|
|
3646
3893
|
{
|
|
3647
|
-
name: "
|
|
3648
|
-
module: "
|
|
3649
|
-
description: "
|
|
3650
|
-
isWrite:
|
|
3894
|
+
name: "futures_get_orders",
|
|
3895
|
+
module: "futures",
|
|
3896
|
+
description: "Query FUTURES open orders, history (last 7 days), or archive (up to 3 months). Private. Rate limit: 20 req/s.",
|
|
3897
|
+
isWrite: false,
|
|
3651
3898
|
inputSchema: {
|
|
3652
3899
|
type: "object",
|
|
3653
3900
|
properties: {
|
|
3654
|
-
|
|
3901
|
+
status: {
|
|
3655
3902
|
type: "string",
|
|
3656
|
-
|
|
3903
|
+
enum: ["open", "history", "archive"],
|
|
3904
|
+
description: "open=active, history=7d, archive=3mo"
|
|
3657
3905
|
},
|
|
3658
|
-
|
|
3906
|
+
instType: {
|
|
3659
3907
|
type: "string",
|
|
3660
|
-
|
|
3908
|
+
enum: [...FUTURES_INST_TYPES],
|
|
3909
|
+
description: "FUTURES (default) or SWAP"
|
|
3910
|
+
},
|
|
3911
|
+
instId: {
|
|
3912
|
+
type: "string",
|
|
3913
|
+
description: "e.g. BTC-USDT-240329"
|
|
3914
|
+
},
|
|
3915
|
+
ordType: {
|
|
3916
|
+
type: "string",
|
|
3917
|
+
description: "Order type filter"
|
|
3918
|
+
},
|
|
3919
|
+
state: {
|
|
3920
|
+
type: "string",
|
|
3921
|
+
description: "canceled|filled"
|
|
3922
|
+
},
|
|
3923
|
+
after: {
|
|
3924
|
+
type: "string",
|
|
3925
|
+
description: "Pagination: before this order ID"
|
|
3926
|
+
},
|
|
3927
|
+
before: {
|
|
3928
|
+
type: "string",
|
|
3929
|
+
description: "Pagination: after this order ID"
|
|
3930
|
+
},
|
|
3931
|
+
begin: {
|
|
3932
|
+
type: "string",
|
|
3933
|
+
description: "Start time (ms)"
|
|
3934
|
+
},
|
|
3935
|
+
end: {
|
|
3936
|
+
type: "string",
|
|
3937
|
+
description: "End time (ms)"
|
|
3938
|
+
},
|
|
3939
|
+
limit: {
|
|
3940
|
+
type: "number",
|
|
3941
|
+
description: "Max results (default 100)"
|
|
3661
3942
|
}
|
|
3662
|
-
}
|
|
3663
|
-
required: ["ordId", "protocolType"]
|
|
3943
|
+
}
|
|
3664
3944
|
},
|
|
3665
3945
|
handler: async (rawArgs, context) => {
|
|
3666
|
-
assertNotDemo(context.config, "onchain_earn_cancel");
|
|
3667
3946
|
const args = asRecord(rawArgs);
|
|
3668
|
-
const
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3947
|
+
const status = readString(args, "status") ?? "open";
|
|
3948
|
+
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3949
|
+
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3950
|
+
const path4 = status === "archive" ? "/api/v5/trade/orders-history-archive" : status === "history" ? "/api/v5/trade/orders-history" : "/api/v5/trade/orders-pending";
|
|
3951
|
+
const response = await context.client.privateGet(
|
|
3952
|
+
path4,
|
|
3953
|
+
compactObject({
|
|
3954
|
+
instType,
|
|
3955
|
+
instId: readString(args, "instId"),
|
|
3956
|
+
ordType: readString(args, "ordType"),
|
|
3957
|
+
state: readString(args, "state"),
|
|
3958
|
+
after: readString(args, "after"),
|
|
3959
|
+
before: readString(args, "before"),
|
|
3960
|
+
begin: readString(args, "begin"),
|
|
3961
|
+
end: readString(args, "end"),
|
|
3962
|
+
limit: readNumber(args, "limit")
|
|
3963
|
+
}),
|
|
3964
|
+
privateRateLimit("futures_get_orders", 20)
|
|
3675
3965
|
);
|
|
3676
|
-
return
|
|
3966
|
+
return normalize5(response);
|
|
3677
3967
|
}
|
|
3678
3968
|
},
|
|
3679
|
-
// -------------------------------------------------------------------------
|
|
3680
|
-
// Get Active Orders
|
|
3681
|
-
// -------------------------------------------------------------------------
|
|
3682
3969
|
{
|
|
3683
|
-
name: "
|
|
3684
|
-
module: "
|
|
3685
|
-
description: "Get
|
|
3970
|
+
name: "futures_get_positions",
|
|
3971
|
+
module: "futures",
|
|
3972
|
+
description: "Get current FUTURES delivery contract positions. Private endpoint. Rate limit: 10 req/s.",
|
|
3686
3973
|
isWrite: false,
|
|
3687
3974
|
inputSchema: {
|
|
3688
3975
|
type: "object",
|
|
3689
3976
|
properties: {
|
|
3690
|
-
|
|
3691
|
-
type: "string",
|
|
3692
|
-
description: "Filter by product ID. Omit for all."
|
|
3693
|
-
},
|
|
3694
|
-
protocolType: {
|
|
3977
|
+
instType: {
|
|
3695
3978
|
type: "string",
|
|
3696
|
-
|
|
3979
|
+
enum: [...FUTURES_INST_TYPES],
|
|
3980
|
+
description: "FUTURES (default) or SWAP"
|
|
3697
3981
|
},
|
|
3698
|
-
|
|
3982
|
+
instId: {
|
|
3699
3983
|
type: "string",
|
|
3700
|
-
description: "
|
|
3984
|
+
description: "e.g. BTC-USDT-240329"
|
|
3701
3985
|
},
|
|
3702
|
-
|
|
3703
|
-
type: "string"
|
|
3704
|
-
description: "Filter by state: 8 (pending), 13 (cancelling), 9 (onchain), 1 (earning), 2 (redeeming). Omit for all."
|
|
3986
|
+
posId: {
|
|
3987
|
+
type: "string"
|
|
3705
3988
|
}
|
|
3706
3989
|
}
|
|
3707
3990
|
},
|
|
3708
3991
|
handler: async (rawArgs, context) => {
|
|
3709
3992
|
const args = asRecord(rawArgs);
|
|
3993
|
+
const instType = readString(args, "instType") ?? "FUTURES";
|
|
3994
|
+
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
3710
3995
|
const response = await context.client.privateGet(
|
|
3711
|
-
"/api/v5/
|
|
3996
|
+
"/api/v5/account/positions",
|
|
3712
3997
|
compactObject({
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
state: readString(args, "state")
|
|
3998
|
+
instType,
|
|
3999
|
+
instId: readString(args, "instId"),
|
|
4000
|
+
posId: readString(args, "posId")
|
|
3717
4001
|
}),
|
|
3718
|
-
privateRateLimit("
|
|
4002
|
+
privateRateLimit("futures_get_positions", 10)
|
|
3719
4003
|
);
|
|
3720
|
-
return
|
|
4004
|
+
return normalize5(response);
|
|
3721
4005
|
}
|
|
3722
4006
|
},
|
|
3723
|
-
// -------------------------------------------------------------------------
|
|
3724
|
-
// Get Order History
|
|
3725
|
-
// -------------------------------------------------------------------------
|
|
3726
4007
|
{
|
|
3727
|
-
name: "
|
|
3728
|
-
module: "
|
|
3729
|
-
description: "Get
|
|
4008
|
+
name: "futures_get_fills",
|
|
4009
|
+
module: "futures",
|
|
4010
|
+
description: "Get FUTURES fill details. archive=false: last 3 days. archive=true: up to 3 months. Private. Rate limit: 20 req/s.",
|
|
3730
4011
|
isWrite: false,
|
|
3731
4012
|
inputSchema: {
|
|
3732
4013
|
type: "object",
|
|
3733
4014
|
properties: {
|
|
3734
|
-
|
|
4015
|
+
archive: {
|
|
4016
|
+
type: "boolean",
|
|
4017
|
+
description: "true=up to 3 months; false=last 3 days (default)"
|
|
4018
|
+
},
|
|
4019
|
+
instType: {
|
|
3735
4020
|
type: "string",
|
|
3736
|
-
|
|
4021
|
+
enum: [...FUTURES_INST_TYPES],
|
|
4022
|
+
description: "FUTURES (default) or SWAP"
|
|
3737
4023
|
},
|
|
3738
|
-
|
|
4024
|
+
instId: {
|
|
3739
4025
|
type: "string",
|
|
3740
|
-
description: "
|
|
4026
|
+
description: "Instrument ID filter"
|
|
3741
4027
|
},
|
|
3742
|
-
|
|
4028
|
+
ordId: {
|
|
3743
4029
|
type: "string",
|
|
3744
|
-
description: "
|
|
4030
|
+
description: "Order ID filter"
|
|
4031
|
+
},
|
|
4032
|
+
after: {
|
|
4033
|
+
type: "string",
|
|
4034
|
+
description: "Pagination: before this bill ID"
|
|
4035
|
+
},
|
|
4036
|
+
before: {
|
|
4037
|
+
type: "string",
|
|
4038
|
+
description: "Pagination: after this bill ID"
|
|
3745
4039
|
},
|
|
3746
|
-
|
|
4040
|
+
begin: {
|
|
3747
4041
|
type: "string",
|
|
3748
|
-
description: "
|
|
4042
|
+
description: "Start time (ms)"
|
|
3749
4043
|
},
|
|
3750
|
-
|
|
4044
|
+
end: {
|
|
3751
4045
|
type: "string",
|
|
3752
|
-
description: "
|
|
4046
|
+
description: "End time (ms)"
|
|
3753
4047
|
},
|
|
3754
4048
|
limit: {
|
|
3755
|
-
type: "
|
|
3756
|
-
description: "Max results
|
|
4049
|
+
type: "number",
|
|
4050
|
+
description: "Max results (default 100 or 20 for archive)"
|
|
3757
4051
|
}
|
|
3758
4052
|
}
|
|
3759
4053
|
},
|
|
3760
4054
|
handler: async (rawArgs, context) => {
|
|
3761
4055
|
const args = asRecord(rawArgs);
|
|
4056
|
+
const archive = readBoolean(args, "archive") ?? false;
|
|
4057
|
+
const instType = readString(args, "instType") ?? "FUTURES";
|
|
4058
|
+
assertEnum(instType, "instType", FUTURES_INST_TYPES);
|
|
4059
|
+
const path4 = archive ? "/api/v5/trade/fills-history" : "/api/v5/trade/fills";
|
|
3762
4060
|
const response = await context.client.privateGet(
|
|
3763
|
-
|
|
4061
|
+
path4,
|
|
3764
4062
|
compactObject({
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
4063
|
+
instType,
|
|
4064
|
+
instId: readString(args, "instId"),
|
|
4065
|
+
ordId: readString(args, "ordId"),
|
|
3768
4066
|
after: readString(args, "after"),
|
|
3769
4067
|
before: readString(args, "before"),
|
|
3770
|
-
|
|
4068
|
+
begin: readString(args, "begin"),
|
|
4069
|
+
end: readString(args, "end"),
|
|
4070
|
+
limit: readNumber(args, "limit") ?? (archive ? 20 : void 0)
|
|
3771
4071
|
}),
|
|
3772
|
-
privateRateLimit("
|
|
4072
|
+
privateRateLimit("futures_get_fills", 20)
|
|
3773
4073
|
);
|
|
3774
|
-
return
|
|
4074
|
+
return normalize5(response);
|
|
3775
4075
|
}
|
|
3776
4076
|
}
|
|
3777
4077
|
];
|
|
@@ -4253,6 +4553,40 @@ function registerMarketTools() {
|
|
|
4253
4553
|
);
|
|
4254
4554
|
return normalize6(response);
|
|
4255
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
|
+
}
|
|
4256
4590
|
}
|
|
4257
4591
|
];
|
|
4258
4592
|
}
|
|
@@ -4772,17 +5106,20 @@ function registerSpotTradeTools() {
|
|
|
4772
5106
|
description: "e.g. BTC-USDT"
|
|
4773
5107
|
},
|
|
4774
5108
|
ordId: {
|
|
4775
|
-
type: "string"
|
|
5109
|
+
type: "string",
|
|
5110
|
+
description: "Order ID"
|
|
4776
5111
|
},
|
|
4777
5112
|
clOrdId: {
|
|
4778
5113
|
type: "string",
|
|
4779
5114
|
description: "Client order ID"
|
|
4780
5115
|
},
|
|
4781
5116
|
newSz: {
|
|
4782
|
-
type: "string"
|
|
5117
|
+
type: "string",
|
|
5118
|
+
description: "New order size in base currency (e.g. BTC amount)"
|
|
4783
5119
|
},
|
|
4784
5120
|
newPx: {
|
|
4785
|
-
type: "string"
|
|
5121
|
+
type: "string",
|
|
5122
|
+
description: "New order price"
|
|
4786
5123
|
},
|
|
4787
5124
|
newClOrdId: {
|
|
4788
5125
|
type: "string",
|
|
@@ -4957,7 +5294,7 @@ function registerSpotTradeTools() {
|
|
|
4957
5294
|
properties: {
|
|
4958
5295
|
instId: { type: "string", description: "e.g. BTC-USDT" },
|
|
4959
5296
|
algoId: { type: "string", description: "Algo order ID" },
|
|
4960
|
-
newSz: { type: "string" },
|
|
5297
|
+
newSz: { type: "string", description: "New order size in base currency (e.g. BTC amount)" },
|
|
4961
5298
|
newTpTriggerPx: { type: "string", description: "New TP trigger price" },
|
|
4962
5299
|
newTpOrdPx: { type: "string", description: "New TP order price; -1=market" },
|
|
4963
5300
|
newSlTriggerPx: { type: "string", description: "New SL trigger price" },
|
|
@@ -5980,8 +6317,7 @@ function allToolSpecs() {
|
|
|
5980
6317
|
...registerAlgoTradeTools(),
|
|
5981
6318
|
...registerAccountTools(),
|
|
5982
6319
|
...registerBotTools(),
|
|
5983
|
-
...
|
|
5984
|
-
...registerOnchainEarnTools(),
|
|
6320
|
+
...registerAllEarnTools(),
|
|
5985
6321
|
...registerAuditTools()
|
|
5986
6322
|
];
|
|
5987
6323
|
}
|
|
@@ -6355,7 +6691,7 @@ function readCliVersion() {
|
|
|
6355
6691
|
return "0.0.0";
|
|
6356
6692
|
}
|
|
6357
6693
|
var CLI_VERSION = readCliVersion();
|
|
6358
|
-
var GIT_HASH = true ? "
|
|
6694
|
+
var GIT_HASH = true ? "1df90cc" : "dev";
|
|
6359
6695
|
var Report = class {
|
|
6360
6696
|
lines = [];
|
|
6361
6697
|
add(key, value) {
|
|
@@ -6582,18 +6918,19 @@ async function checkNetwork(config, client, report) {
|
|
|
6582
6918
|
}
|
|
6583
6919
|
return passed;
|
|
6584
6920
|
}
|
|
6585
|
-
function getAuthHints(msg) {
|
|
6921
|
+
function getAuthHints(msg, baseUrl) {
|
|
6922
|
+
const accountUrl = baseUrl.replace(/\/+$/, "") + "/account/my-api";
|
|
6586
6923
|
if (msg.includes("50111") || msg.includes("Invalid OK-ACCESS-KEY")) {
|
|
6587
|
-
return ["API key is invalid or expired",
|
|
6924
|
+
return ["API key is invalid or expired", `Regenerate at ${accountUrl}`];
|
|
6588
6925
|
}
|
|
6589
6926
|
if (msg.includes("50112") || msg.includes("Invalid Sign")) {
|
|
6590
|
-
return ["Secret key or passphrase may be wrong",
|
|
6927
|
+
return ["Secret key or passphrase may be wrong", `Regenerate API key at ${accountUrl}`];
|
|
6591
6928
|
}
|
|
6592
6929
|
if (msg.includes("50113")) {
|
|
6593
6930
|
return ["Passphrase is incorrect"];
|
|
6594
6931
|
}
|
|
6595
6932
|
if (msg.includes("50100")) {
|
|
6596
|
-
return ["API key lacks required permissions",
|
|
6933
|
+
return ["API key lacks required permissions", `Update permissions at ${accountUrl}`];
|
|
6597
6934
|
}
|
|
6598
6935
|
return ["Check API credentials and permissions"];
|
|
6599
6936
|
}
|
|
@@ -6616,7 +6953,7 @@ async function checkAuth(client, config, report) {
|
|
|
6616
6953
|
} catch (e) {
|
|
6617
6954
|
const ms = Date.now() - t1;
|
|
6618
6955
|
const msg = e instanceof Error ? e.message : String(e);
|
|
6619
|
-
const hints = getAuthHints(msg);
|
|
6956
|
+
const hints = getAuthHints(msg, config.baseUrl);
|
|
6620
6957
|
fail("Account balance", msg, hints);
|
|
6621
6958
|
passed = false;
|
|
6622
6959
|
report.add("auth_api", `FAIL /account/balance ${msg} (${ms}ms)`);
|
|
@@ -6738,6 +7075,10 @@ var HELP_TREE = {
|
|
|
6738
7075
|
"open-interest": {
|
|
6739
7076
|
usage: "okx market open-interest --instType <SWAP|FUTURES|OPTION> [--instId <id>]",
|
|
6740
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)"
|
|
6741
7082
|
}
|
|
6742
7083
|
}
|
|
6743
7084
|
},
|
|
@@ -7005,7 +7346,7 @@ var HELP_TREE = {
|
|
|
7005
7346
|
}
|
|
7006
7347
|
},
|
|
7007
7348
|
earn: {
|
|
7008
|
-
description: "Earn products \u2014 Simple Earn
|
|
7349
|
+
description: "Earn products \u2014 Simple Earn, On-chain Earn, and DCD (Dual Currency Deposit)",
|
|
7009
7350
|
subgroups: {
|
|
7010
7351
|
savings: {
|
|
7011
7352
|
description: "Simple Earn \u2014 flexible savings and lending",
|
|
@@ -7068,6 +7409,51 @@ var HELP_TREE = {
|
|
|
7068
7409
|
description: "Get on-chain earn order history"
|
|
7069
7410
|
}
|
|
7070
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
|
+
}
|
|
7071
7457
|
}
|
|
7072
7458
|
}
|
|
7073
7459
|
},
|
|
@@ -7103,7 +7489,7 @@ var HELP_TREE = {
|
|
|
7103
7489
|
description: "Contract DCA (Martingale) bot \u2014 leveraged recurring buys on futures/swaps",
|
|
7104
7490
|
commands: {
|
|
7105
7491
|
orders: {
|
|
7106
|
-
usage: "okx bot dca orders [--history]",
|
|
7492
|
+
usage: "okx bot dca orders [--algoId <id>] [--instId <id>] [--history]",
|
|
7107
7493
|
description: "List active or historical Contract DCA bot orders"
|
|
7108
7494
|
},
|
|
7109
7495
|
details: {
|
|
@@ -7390,6 +7776,21 @@ var CLI_OPTIONS = {
|
|
|
7390
7776
|
tag: { type: "string" },
|
|
7391
7777
|
allowEarlyRedeem: { type: "boolean", default: false },
|
|
7392
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" },
|
|
7393
7794
|
// diagnostics
|
|
7394
7795
|
verbose: { type: "boolean", default: false }
|
|
7395
7796
|
};
|
|
@@ -7649,6 +8050,22 @@ async function cmdMarketCandles(run, instId, opts) {
|
|
|
7649
8050
|
}))
|
|
7650
8051
|
);
|
|
7651
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
|
+
}
|
|
7652
8069
|
|
|
7653
8070
|
// src/commands/account.ts
|
|
7654
8071
|
import * as fs4 from "fs";
|
|
@@ -9278,7 +9695,9 @@ async function cmdDcaStop(run, opts) {
|
|
|
9278
9695
|
}
|
|
9279
9696
|
async function cmdDcaOrders(run, opts) {
|
|
9280
9697
|
const result = await run("dca_get_orders", {
|
|
9281
|
-
status: opts.history ? "history" : "active"
|
|
9698
|
+
status: opts.history ? "history" : "active",
|
|
9699
|
+
algoId: opts.algoId,
|
|
9700
|
+
instId: opts.instId
|
|
9282
9701
|
});
|
|
9283
9702
|
const orders = getData7(result) ?? [];
|
|
9284
9703
|
if (opts.json) return printJson(orders);
|
|
@@ -9394,10 +9813,362 @@ function cmdOnchainEarnOrderHistory(run, v) {
|
|
|
9394
9813
|
});
|
|
9395
9814
|
}
|
|
9396
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
|
+
|
|
9397
10168
|
// src/index.ts
|
|
9398
10169
|
var _require2 = createRequire2(import.meta.url);
|
|
9399
10170
|
var CLI_VERSION2 = _require2("../package.json").version;
|
|
9400
|
-
var GIT_HASH2 = true ? "
|
|
10171
|
+
var GIT_HASH2 = true ? "1df90cc" : "dev";
|
|
9401
10172
|
function handleConfigCommand(action, rest, json, lang, force) {
|
|
9402
10173
|
if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
|
|
9403
10174
|
if (action === "show") return cmdConfigShow(json);
|
|
@@ -9442,6 +10213,8 @@ function handleMarketPublicCommand(run, action, rest, v, json) {
|
|
|
9442
10213
|
if (action === "price-limit") return cmdMarketPriceLimit(run, rest[0], json);
|
|
9443
10214
|
if (action === "open-interest")
|
|
9444
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 });
|
|
9445
10218
|
}
|
|
9446
10219
|
function handleMarketDataCommand(run, action, rest, v, json) {
|
|
9447
10220
|
const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
|
|
@@ -9828,7 +10601,7 @@ function handleBotGridCommand(run, v, rest, json) {
|
|
|
9828
10601
|
}
|
|
9829
10602
|
function handleBotDcaCommand(run, subAction, v, json) {
|
|
9830
10603
|
if (subAction === "orders")
|
|
9831
|
-
return cmdDcaOrders(run, { history: v.history ?? false, json });
|
|
10604
|
+
return cmdDcaOrders(run, { algoId: v.algoId, instId: v.instId, history: v.history ?? false, json });
|
|
9832
10605
|
if (subAction === "details")
|
|
9833
10606
|
return cmdDcaDetails(run, { algoId: v.algoId, json });
|
|
9834
10607
|
if (subAction === "sub-orders")
|
|
@@ -9864,8 +10637,9 @@ function handleEarnCommand(run, submodule, rest, v, json) {
|
|
|
9864
10637
|
const innerRest = rest.slice(1);
|
|
9865
10638
|
if (submodule === "savings") return handleEarnSavingsCommand(run, action, innerRest, v, json);
|
|
9866
10639
|
if (submodule === "onchain") return handleEarnOnchainCommand(run, action, v, json);
|
|
10640
|
+
if (submodule === "dcd") return handleEarnDcdCommand(run, action, v, json);
|
|
9867
10641
|
process.stderr.write(`Unknown earn sub-module: ${submodule}
|
|
9868
|
-
Valid: savings, onchain
|
|
10642
|
+
Valid: savings, onchain, dcd
|
|
9869
10643
|
`);
|
|
9870
10644
|
process.exitCode = 1;
|
|
9871
10645
|
}
|
|
@@ -9893,6 +10667,70 @@ function handleEarnOnchainCommand(run, action, v, json) {
|
|
|
9893
10667
|
`);
|
|
9894
10668
|
process.exitCode = 1;
|
|
9895
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
|
+
}
|
|
9896
10734
|
function outputResult(result, json) {
|
|
9897
10735
|
if (json) {
|
|
9898
10736
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|