@okx_ai/okx-trade-cli 1.3.2-beta.7 → 1.3.3-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 CHANGED
@@ -1988,6 +1988,16 @@ function readBoolean(args, key) {
1988
1988
  }
1989
1989
  return value;
1990
1990
  }
1991
+ function readStringArray(args, key) {
1992
+ const value = args[key];
1993
+ if (value === void 0 || value === null) {
1994
+ return void 0;
1995
+ }
1996
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
1997
+ throw new ValidationError(`Parameter "${key}" must be an array of strings.`);
1998
+ }
1999
+ return value;
2000
+ }
1991
2001
  function requireString(args, key) {
1992
2002
  const value = readString(args, key);
1993
2003
  if (!value || value.length === 0) {
@@ -2028,12 +2038,170 @@ function validateSwapInstId(instId) {
2028
2038
  );
2029
2039
  }
2030
2040
  }
2041
+ var TP_ORD_KIND_SCHEMA = {
2042
+ type: "string",
2043
+ enum: ["condition", "limit"],
2044
+ description: "condition(default)=trigger-based TP; limit=immediate limit order (no trigger phase)"
2045
+ };
2046
+ var TP_TRIGGER_PX_TYPE_SCHEMA = {
2047
+ type: "string",
2048
+ enum: ["last", "index", "mark"],
2049
+ description: "TP trigger price source: last(default)|index|mark"
2050
+ };
2051
+ var SL_TRIGGER_PX_TYPE_SCHEMA = {
2052
+ type: "string",
2053
+ enum: ["last", "index", "mark"],
2054
+ description: "SL trigger price source: last(default)|index|mark"
2055
+ };
2056
+ var STP_MODE_SCHEMA = {
2057
+ type: "string",
2058
+ enum: ["cancel_maker", "cancel_taker", "cancel_both"],
2059
+ description: "Self-trade prevention: cancel_maker|cancel_taker|cancel_both"
2060
+ };
2061
+ var CXL_ON_CLOSE_POS_SCHEMA = {
2062
+ type: "boolean",
2063
+ description: "Auto-cancel TP/SL when associated position closes (algo only)"
2064
+ };
2065
+ var PHASE1_PLACE_FLAGS_SCHEMA = {
2066
+ tpOrdKind: TP_ORD_KIND_SCHEMA,
2067
+ tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
2068
+ slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
2069
+ stpMode: STP_MODE_SCHEMA
2070
+ };
2071
+ var PHASE1_ALGO_FLAGS_SCHEMA = {
2072
+ ...PHASE1_PLACE_FLAGS_SCHEMA,
2073
+ cxlOnClosePos: CXL_ON_CLOSE_POS_SCHEMA
2074
+ };
2075
+ var TRIGGER_FLAGS_SCHEMA = {
2076
+ triggerPx: {
2077
+ type: "string",
2078
+ description: "Activation price; order submits when market hits this level (trigger only)"
2079
+ },
2080
+ orderPx: {
2081
+ type: "string",
2082
+ description: "Order price submitted when trigger fires; -1=market (trigger only)"
2083
+ },
2084
+ advanceOrdType: {
2085
+ type: "string",
2086
+ enum: ["fok", "ioc"],
2087
+ description: "Execution qualifier for triggered order: fok=fill-or-kill, ioc=immediate-or-cancel (trigger only)"
2088
+ },
2089
+ triggerPxType: {
2090
+ type: "string",
2091
+ enum: ["last", "index", "mark"],
2092
+ description: "Price type used to evaluate trigger: last(default)|index|mark (trigger only)"
2093
+ }
2094
+ };
2095
+ var CHASE_FLAGS_SCHEMA = {
2096
+ chaseType: {
2097
+ type: "string",
2098
+ enum: ["distance", "ratio"],
2099
+ description: "Chase unit: distance=price ticks, ratio=proportion of price (chase only, default distance)"
2100
+ },
2101
+ chaseVal: {
2102
+ type: "string",
2103
+ description: "Chase amount matching chaseType (e.g. 0.5 ticks for distance, 0.001 for ratio) (chase only)"
2104
+ },
2105
+ maxChaseType: {
2106
+ type: "string",
2107
+ enum: ["distance", "ratio"],
2108
+ description: "Upper-bound unit for chase (chase only)"
2109
+ },
2110
+ maxChaseVal: {
2111
+ type: "string",
2112
+ description: "Upper-bound value for chase (chase only)"
2113
+ }
2114
+ };
2115
+ var ICEBERG_TWAP_FLAGS_SCHEMA = {
2116
+ pxVar: {
2117
+ type: "string",
2118
+ description: "Price variance % [0.0001, 0.01]; provide pxVar OR pxSpread (iceberg/twap only)"
2119
+ },
2120
+ pxSpread: {
2121
+ type: "string",
2122
+ description: "Price variance constant >= 0; provide pxVar OR pxSpread (iceberg/twap only)"
2123
+ },
2124
+ szLimit: {
2125
+ type: "string",
2126
+ description: "Average per-child-order size (iceberg/twap only)"
2127
+ },
2128
+ pxLimit: {
2129
+ type: "string",
2130
+ description: "Order price ceiling >= 0 (iceberg/twap only)"
2131
+ },
2132
+ timeInterval: {
2133
+ type: "string",
2134
+ description: "Seconds between child orders (iceberg/twap only)"
2135
+ }
2136
+ };
2137
+ function buildTriggerOrdTypeBody(args) {
2138
+ return compactObject({
2139
+ triggerPx: readString(args, "triggerPx"),
2140
+ orderPx: readString(args, "orderPx"),
2141
+ advanceOrdType: readString(args, "advanceOrdType"),
2142
+ triggerPxType: readString(args, "triggerPxType"),
2143
+ attachAlgoOrds: buildAttachAlgoOrds(args)
2144
+ });
2145
+ }
2146
+ function buildChaseOrdTypeBody(args) {
2147
+ return compactObject({
2148
+ chaseType: readString(args, "chaseType"),
2149
+ chaseVal: readString(args, "chaseVal"),
2150
+ maxChaseType: readString(args, "maxChaseType"),
2151
+ maxChaseVal: readString(args, "maxChaseVal")
2152
+ });
2153
+ }
2154
+ function buildIcebergTwapOrdTypeBody(args) {
2155
+ return compactObject({
2156
+ pxVar: readString(args, "pxVar"),
2157
+ pxSpread: readString(args, "pxSpread"),
2158
+ szLimit: readString(args, "szLimit"),
2159
+ pxLimit: readString(args, "pxLimit"),
2160
+ timeInterval: readString(args, "timeInterval")
2161
+ });
2162
+ }
2163
+ function buildAlgoConditionalCommonFields(args) {
2164
+ return {
2165
+ tpTriggerPx: readString(args, "tpTriggerPx"),
2166
+ tpOrdPx: readString(args, "tpOrdPx"),
2167
+ tpOrdKind: readString(args, "tpOrdKind"),
2168
+ tpTriggerPxType: readString(args, "tpTriggerPxType"),
2169
+ tpTriggerRatio: readString(args, "tpTriggerRatio"),
2170
+ slTriggerPx: readString(args, "slTriggerPx"),
2171
+ slOrdPx: readString(args, "slOrdPx"),
2172
+ slTriggerPxType: readString(args, "slTriggerPxType"),
2173
+ slTriggerRatio: readString(args, "slTriggerRatio"),
2174
+ closeFraction: readString(args, "closeFraction"),
2175
+ activePx: readString(args, "activePx")
2176
+ };
2177
+ }
2031
2178
  function buildAttachAlgoOrds(source) {
2179
+ const tpLevels = source["tpLevels"];
2180
+ if (Array.isArray(tpLevels) && tpLevels.length > 0) {
2181
+ return tpLevels.map(
2182
+ (level) => compactObject(level)
2183
+ );
2184
+ }
2032
2185
  const tpTriggerPx = readString(source, "tpTriggerPx");
2033
2186
  const tpOrdPx = readString(source, "tpOrdPx");
2034
2187
  const slTriggerPx = readString(source, "slTriggerPx");
2035
2188
  const slOrdPx = readString(source, "slOrdPx");
2036
- const entry = compactObject({ tpTriggerPx, tpOrdPx, slTriggerPx, slOrdPx });
2189
+ const tpOrdKind = readString(source, "tpOrdKind");
2190
+ const tpTriggerPxType = readString(source, "tpTriggerPxType");
2191
+ const slTriggerPxType = readString(source, "slTriggerPxType");
2192
+ const tpTriggerRatio = readString(source, "tpTriggerRatio");
2193
+ const slTriggerRatio = readString(source, "slTriggerRatio");
2194
+ const entry = compactObject({
2195
+ tpTriggerPx,
2196
+ tpOrdPx,
2197
+ slTriggerPx,
2198
+ slOrdPx,
2199
+ tpOrdKind,
2200
+ tpTriggerPxType,
2201
+ slTriggerPxType,
2202
+ tpTriggerRatio,
2203
+ slTriggerRatio
2204
+ });
2037
2205
  return Object.keys(entry).length > 0 ? [entry] : void 0;
2038
2206
  }
2039
2207
  var OKX_CANDLE_BARS = [
@@ -3108,7 +3276,7 @@ function registerAlgoTradeTools() {
3108
3276
  {
3109
3277
  name: "swap_place_algo_order",
3110
3278
  module: "swap",
3111
- description: "Place a SWAP/FUTURES algo order: TP/SL (conditional/oco) or trailing stop (move_order_stop). [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously \u2014 first trigger cancels the other. move_order_stop: provide callbackRatio (e.g. '0.01'=1%) OR callbackSpread, and optionally activePx. Set tpOrdPx='-1' or slOrdPx='-1' for market execution.",
3279
+ description: "Place a SWAP/FUTURES algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously \u2014 first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3112
3280
  isWrite: true,
3113
3281
  inputSchema: {
3114
3282
  type: "object",
@@ -3134,8 +3302,8 @@ function registerAlgoTradeTools() {
3134
3302
  },
3135
3303
  ordType: {
3136
3304
  type: "string",
3137
- enum: ["conditional", "oco", "move_order_stop"],
3138
- description: "conditional=single TP/SL or both; oco=TP+SL pair (first trigger cancels other); move_order_stop=trailing stop"
3305
+ enum: ["conditional", "oco", "move_order_stop", "trigger", "chase", "iceberg", "twap"],
3306
+ description: "conditional=single TP/SL or both; oco=TP+SL pair; move_order_stop=trailing stop; trigger=pending order; chase=follow best bid/ask; iceberg=split order; twap=time-weighted split"
3139
3307
  },
3140
3308
  sz: {
3141
3309
  type: "string",
@@ -3149,11 +3317,8 @@ function registerAlgoTradeTools() {
3149
3317
  type: "string",
3150
3318
  description: "TP order price; -1=market (conditional/oco only)"
3151
3319
  },
3152
- tpTriggerPxType: {
3153
- type: "string",
3154
- enum: ["last", "index", "mark"],
3155
- description: "last(default)|index|mark (conditional/oco only)"
3156
- },
3320
+ tpOrdKind: TP_ORD_KIND_SCHEMA,
3321
+ tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
3157
3322
  slTriggerPx: {
3158
3323
  type: "string",
3159
3324
  description: "SL trigger price (conditional/oco only)"
@@ -3162,11 +3327,9 @@ function registerAlgoTradeTools() {
3162
3327
  type: "string",
3163
3328
  description: "SL order price; -1=market (recommended) (conditional/oco only)"
3164
3329
  },
3165
- slTriggerPxType: {
3166
- type: "string",
3167
- enum: ["last", "index", "mark"],
3168
- description: "last(default)|index|mark (conditional/oco only)"
3169
- },
3330
+ slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
3331
+ stpMode: STP_MODE_SCHEMA,
3332
+ cxlOnClosePos: CXL_ON_CLOSE_POS_SCHEMA,
3170
3333
  callbackRatio: {
3171
3334
  type: "string",
3172
3335
  description: "Callback ratio (e.g. '0.01'=1%); provide either ratio or spread (move_order_stop only)"
@@ -3179,6 +3342,9 @@ function registerAlgoTradeTools() {
3179
3342
  type: "string",
3180
3343
  description: "Activation price; tracking starts after market reaches this level (move_order_stop only)"
3181
3344
  },
3345
+ ...TRIGGER_FLAGS_SCHEMA,
3346
+ ...CHASE_FLAGS_SCHEMA,
3347
+ ...ICEBERG_TWAP_FLAGS_SCHEMA,
3182
3348
  tgtCcy: {
3183
3349
  type: "string",
3184
3350
  enum: ["base_ccy", "quote_ccy", "margin"],
@@ -3198,6 +3364,8 @@ function registerAlgoTradeTools() {
3198
3364
  handler: async (rawArgs, context) => {
3199
3365
  const args = asRecord(rawArgs);
3200
3366
  const reduceOnly = args.reduceOnly;
3367
+ const cxlOnClosePos = args.cxlOnClosePos;
3368
+ const ordType = requireString(args, "ordType");
3201
3369
  const resolved = await resolveQuoteCcySz(
3202
3370
  requireString(args, "instId"),
3203
3371
  requireString(args, "sz"),
@@ -3206,29 +3374,44 @@ function registerAlgoTradeTools() {
3206
3374
  context.client,
3207
3375
  readString(args, "tdMode")
3208
3376
  );
3377
+ const base = compactObject({
3378
+ instId: requireString(args, "instId"),
3379
+ tdMode: requireString(args, "tdMode"),
3380
+ side: requireString(args, "side"),
3381
+ posSide: readString(args, "posSide"),
3382
+ ordType,
3383
+ sz: resolved.sz,
3384
+ tgtCcy: resolved.tgtCcy,
3385
+ stpMode: readString(args, "stpMode"),
3386
+ cxlOnClosePos: typeof cxlOnClosePos === "boolean" ? String(cxlOnClosePos) : void 0,
3387
+ reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
3388
+ clOrdId: readString(args, "clOrdId"),
3389
+ // Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
3390
+ pxAmendType: readString(args, "pxAmendType"),
3391
+ tag: context.config.sourceTag
3392
+ });
3393
+ switch (ordType) {
3394
+ case "trigger":
3395
+ Object.assign(base, buildTriggerOrdTypeBody(args));
3396
+ break;
3397
+ case "chase":
3398
+ Object.assign(base, buildChaseOrdTypeBody(args));
3399
+ break;
3400
+ case "iceberg":
3401
+ case "twap":
3402
+ Object.assign(base, buildIcebergTwapOrdTypeBody(args));
3403
+ break;
3404
+ default:
3405
+ Object.assign(base, compactObject({
3406
+ ...buildAlgoConditionalCommonFields(args),
3407
+ callBackRatio: readString(args, "callbackRatio"),
3408
+ callBackSpread: readString(args, "callbackSpread")
3409
+ }));
3410
+ break;
3411
+ }
3209
3412
  const response = await context.client.privatePost(
3210
3413
  "/api/v5/trade/order-algo",
3211
- compactObject({
3212
- instId: requireString(args, "instId"),
3213
- tdMode: requireString(args, "tdMode"),
3214
- side: requireString(args, "side"),
3215
- posSide: readString(args, "posSide"),
3216
- ordType: requireString(args, "ordType"),
3217
- sz: resolved.sz,
3218
- tgtCcy: resolved.tgtCcy,
3219
- tpTriggerPx: readString(args, "tpTriggerPx"),
3220
- tpOrdPx: readString(args, "tpOrdPx"),
3221
- tpTriggerPxType: readString(args, "tpTriggerPxType"),
3222
- slTriggerPx: readString(args, "slTriggerPx"),
3223
- slOrdPx: readString(args, "slOrdPx"),
3224
- slTriggerPxType: readString(args, "slTriggerPxType"),
3225
- callBackRatio: readString(args, "callbackRatio"),
3226
- callBackSpread: readString(args, "callbackSpread"),
3227
- activePx: readString(args, "activePx"),
3228
- reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
3229
- clOrdId: readString(args, "clOrdId"),
3230
- tag: context.config.sourceTag
3231
- }),
3414
+ base,
3232
3415
  privateRateLimit("swap_place_algo_order", 20)
3233
3416
  );
3234
3417
  const result = normalizeResponse(response);
@@ -3453,7 +3636,7 @@ function registerFuturesAlgoTools() {
3453
3636
  {
3454
3637
  name: "futures_place_algo_order",
3455
3638
  module: "futures",
3456
- description: "Place a FUTURES delivery algo order: TP/SL (conditional/oco) or trailing stop (move_order_stop). [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously \u2014 first trigger cancels the other. move_order_stop: provide callbackRatio (e.g. '0.01'=1%) OR callbackSpread, and optionally activePx. Set tpOrdPx='-1' or slOrdPx='-1' for market execution.",
3639
+ description: "Place a FUTURES delivery algo order. [CAUTION] Executes real trades. conditional: single TP, single SL, or both on one order. oco: TP+SL simultaneously \u2014 first trigger cancels the other. move_order_stop: trailing stop (callbackRatio or callbackSpread). trigger: pending order activated when triggerPx is hit (provide triggerPx + orderPx). chase: smart-follow best bid/ask. iceberg: split large order into child orders at intervals. twap: time-weighted average price order splitting.",
3457
3640
  isWrite: true,
3458
3641
  inputSchema: {
3459
3642
  type: "object",
@@ -3479,8 +3662,8 @@ function registerFuturesAlgoTools() {
3479
3662
  },
3480
3663
  ordType: {
3481
3664
  type: "string",
3482
- enum: ["conditional", "oco", "move_order_stop"],
3483
- description: "conditional=single TP/SL or both; oco=TP+SL pair; move_order_stop=trailing stop"
3665
+ enum: ["conditional", "oco", "move_order_stop", "trigger", "chase", "iceberg", "twap"],
3666
+ description: "conditional=single TP/SL or both; oco=TP+SL pair; move_order_stop=trailing stop; trigger=pending order; chase=follow best bid/ask; iceberg=split order; twap=time-weighted split"
3484
3667
  },
3485
3668
  sz: {
3486
3669
  type: "string",
@@ -3494,11 +3677,8 @@ function registerFuturesAlgoTools() {
3494
3677
  type: "string",
3495
3678
  description: "TP order price; -1=market (conditional/oco only)"
3496
3679
  },
3497
- tpTriggerPxType: {
3498
- type: "string",
3499
- enum: ["last", "index", "mark"],
3500
- description: "last(default)|index|mark (conditional/oco only)"
3501
- },
3680
+ tpOrdKind: TP_ORD_KIND_SCHEMA,
3681
+ tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
3502
3682
  slTriggerPx: {
3503
3683
  type: "string",
3504
3684
  description: "SL trigger price (conditional/oco only)"
@@ -3507,11 +3687,9 @@ function registerFuturesAlgoTools() {
3507
3687
  type: "string",
3508
3688
  description: "SL order price; -1=market (conditional/oco only)"
3509
3689
  },
3510
- slTriggerPxType: {
3511
- type: "string",
3512
- enum: ["last", "index", "mark"],
3513
- description: "last(default)|index|mark (conditional/oco only)"
3514
- },
3690
+ slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
3691
+ stpMode: STP_MODE_SCHEMA,
3692
+ cxlOnClosePos: CXL_ON_CLOSE_POS_SCHEMA,
3515
3693
  callbackRatio: {
3516
3694
  type: "string",
3517
3695
  description: "Callback ratio (e.g. '0.01'=1%); provide either ratio or spread (move_order_stop only)"
@@ -3524,6 +3702,9 @@ function registerFuturesAlgoTools() {
3524
3702
  type: "string",
3525
3703
  description: "Activation price; tracking starts after market reaches this level (move_order_stop only)"
3526
3704
  },
3705
+ ...TRIGGER_FLAGS_SCHEMA,
3706
+ ...CHASE_FLAGS_SCHEMA,
3707
+ ...ICEBERG_TWAP_FLAGS_SCHEMA,
3527
3708
  tgtCcy: {
3528
3709
  type: "string",
3529
3710
  enum: ["base_ccy", "quote_ccy", "margin"],
@@ -3543,6 +3724,8 @@ function registerFuturesAlgoTools() {
3543
3724
  handler: async (rawArgs, context) => {
3544
3725
  const args = asRecord(rawArgs);
3545
3726
  const reduceOnly = args.reduceOnly;
3727
+ const cxlOnClosePos = args.cxlOnClosePos;
3728
+ const ordType = requireString(args, "ordType");
3546
3729
  const resolved = await resolveQuoteCcySz(
3547
3730
  requireString(args, "instId"),
3548
3731
  requireString(args, "sz"),
@@ -3551,29 +3734,44 @@ function registerFuturesAlgoTools() {
3551
3734
  context.client,
3552
3735
  readString(args, "tdMode")
3553
3736
  );
3737
+ const base = compactObject({
3738
+ instId: requireString(args, "instId"),
3739
+ tdMode: requireString(args, "tdMode"),
3740
+ side: requireString(args, "side"),
3741
+ posSide: readString(args, "posSide"),
3742
+ ordType,
3743
+ sz: resolved.sz,
3744
+ tgtCcy: resolved.tgtCcy,
3745
+ stpMode: readString(args, "stpMode"),
3746
+ cxlOnClosePos: typeof cxlOnClosePos === "boolean" ? String(cxlOnClosePos) : void 0,
3747
+ reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
3748
+ clOrdId: readString(args, "clOrdId"),
3749
+ // Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
3750
+ pxAmendType: readString(args, "pxAmendType"),
3751
+ tag: context.config.sourceTag
3752
+ });
3753
+ switch (ordType) {
3754
+ case "trigger":
3755
+ Object.assign(base, buildTriggerOrdTypeBody(args));
3756
+ break;
3757
+ case "chase":
3758
+ Object.assign(base, buildChaseOrdTypeBody(args));
3759
+ break;
3760
+ case "iceberg":
3761
+ case "twap":
3762
+ Object.assign(base, buildIcebergTwapOrdTypeBody(args));
3763
+ break;
3764
+ default:
3765
+ Object.assign(base, compactObject({
3766
+ ...buildAlgoConditionalCommonFields(args),
3767
+ callBackRatio: readString(args, "callbackRatio"),
3768
+ callBackSpread: readString(args, "callbackSpread")
3769
+ }));
3770
+ break;
3771
+ }
3554
3772
  const response = await context.client.privatePost(
3555
3773
  "/api/v5/trade/order-algo",
3556
- compactObject({
3557
- instId: requireString(args, "instId"),
3558
- tdMode: requireString(args, "tdMode"),
3559
- side: requireString(args, "side"),
3560
- posSide: readString(args, "posSide"),
3561
- ordType: requireString(args, "ordType"),
3562
- sz: resolved.sz,
3563
- tgtCcy: resolved.tgtCcy,
3564
- tpTriggerPx: readString(args, "tpTriggerPx"),
3565
- tpOrdPx: readString(args, "tpOrdPx"),
3566
- tpTriggerPxType: readString(args, "tpTriggerPxType"),
3567
- slTriggerPx: readString(args, "slTriggerPx"),
3568
- slOrdPx: readString(args, "slOrdPx"),
3569
- slTriggerPxType: readString(args, "slTriggerPxType"),
3570
- callBackRatio: readString(args, "callbackRatio"),
3571
- callBackSpread: readString(args, "callbackSpread"),
3572
- activePx: readString(args, "activePx"),
3573
- reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
3574
- clOrdId: readString(args, "clOrdId"),
3575
- tag: context.config.sourceTag
3576
- }),
3774
+ base,
3577
3775
  privateRateLimit("futures_place_algo_order", 20)
3578
3776
  );
3579
3777
  const result = normalizeResponse(response);
@@ -6156,6 +6354,23 @@ function resolveOutcome(value) {
6156
6354
  }
6157
6355
  return resolved;
6158
6356
  }
6357
+ async function withConcurrency(items, maxConcurrency, fn) {
6358
+ const results = new Array(items.length);
6359
+ let nextIndex = 0;
6360
+ async function runNext() {
6361
+ while (nextIndex < items.length) {
6362
+ const idx = nextIndex++;
6363
+ try {
6364
+ results[idx] = { status: "fulfilled", value: await fn(items[idx]) };
6365
+ } catch (err) {
6366
+ results[idx] = { status: "rejected", reason: err };
6367
+ }
6368
+ }
6369
+ }
6370
+ const workers = Array.from({ length: Math.min(maxConcurrency, items.length) }, runNext);
6371
+ await Promise.all(workers);
6372
+ return results;
6373
+ }
6159
6374
  function filterBrowseCandidates(allSeries, underlyingFilter) {
6160
6375
  const isHumanReadable = (id) => /^(BTC|ETH|TRX|EOS|SOL|IOTA|KISHU|SUSHI|BTG|XTZ|SOLVU)-/.test(id);
6161
6376
  const seen = /* @__PURE__ */ new Set();
@@ -6296,6 +6511,7 @@ function handlePlaceOrderError(base, rawArgs, endpoint) {
6296
6511
  }
6297
6512
  throw new OkxApiError(`[${sCode}] ${sMsg}`, { code: sCode, endpoint });
6298
6513
  }
6514
+ var MAX_CONCURRENT_MARKET_FETCHES = 8;
6299
6515
  var OUTCOME_SCHEMA = {
6300
6516
  type: "string",
6301
6517
  enum: ["UP", "YES", "DOWN", "NO"],
@@ -6335,10 +6551,15 @@ function registerEventContractTools() {
6335
6551
  const normalizedSeries = normalizeResponse(seriesResp);
6336
6552
  const allSeries = Array.isArray(normalizedSeries["data"]) ? normalizedSeries["data"] : [];
6337
6553
  const candidates = filterBrowseCandidates(allSeries, underlyingFilter);
6338
- const marketResults = await Promise.all(
6339
- candidates.map((s) => fetchActiveContractsForSeries(context.client, s))
6554
+ const settled = await withConcurrency(
6555
+ candidates,
6556
+ MAX_CONCURRENT_MARKET_FETCHES,
6557
+ (s) => fetchActiveContractsForSeries(context.client, s)
6340
6558
  );
6341
- const results = marketResults.filter(Boolean);
6559
+ const fulfilled = settled.filter(
6560
+ (r) => r.status === "fulfilled" && r.value !== null
6561
+ );
6562
+ const results = fulfilled.map((r) => r.value);
6342
6563
  return {
6343
6564
  data: results,
6344
6565
  total: results.reduce((n, r) => n + (r?.contracts?.length ?? 0), 0)
@@ -6758,379 +6979,1106 @@ function registerEventContractTools() {
6758
6979
  }
6759
6980
  ];
6760
6981
  }
6982
+ var SMARTMONEY_RPS = 5;
6761
6983
  var PATH_LEADERBOARD = "/api/v5/orbit/public/leaderboard";
6762
6984
  var PATH_POSITION_CURRENT = "/api/v5/orbit/public/position-current";
6985
+ var PATH_POSITION_HISTORY = "/api/v5/orbit/public/position-history";
6763
6986
  var PATH_TRADE_RECORDS = "/api/v5/orbit/public/trade-records";
6987
+ var PATH_TOP_TRADER_SEARCH = "/api/v5/orbit/top-trader-search";
6764
6988
  var PATH_OVERVIEW = "/api/v5/journal/smartmoney/overview";
6765
- var PATH_SIGNAL = "/api/v5/journal/smartmoney/signal";
6766
6989
  var PATH_SIGNAL_HISTORY = "/api/v5/journal/smartmoney/signal-history";
6990
+ var PERIOD_DAYS = ["3", "7", "30", "90"];
6767
6991
  var SIGNAL_POOL_FILTER_PROPS = {
6768
- sortType: {
6992
+ sortBy: {
6769
6993
  type: "string",
6770
- description: "Pool ranking: pnl|pnlRatio (default pnl)"
6994
+ enum: ["pnl", "pnlRatio"],
6995
+ default: "pnl",
6996
+ description: "Ranking key used to pick traders into the aggregation pool. `pnl` = absolute USD profit (favors high-AUM whales); `pnlRatio` = percentage return (favors small-but-efficient traders)."
6771
6997
  },
6772
6998
  period: {
6773
6999
  type: "string",
6774
- description: "Win-rate window days: 3|7|30|90 (default 90). Not snapshot range."
7000
+ enum: PERIOD_DAYS,
7001
+ default: "7",
7002
+ description: 'Lookback window in days. Pass as a quoted string: `"3"` / `"7"` / `"30"` / `"90"` (NOT integer 7). Drives capability metrics (avgLongWinRate / avgShortWinRate) and the `winRateTier` filter. Does NOT affect signal fields (which always use the latest snapshot).'
6775
7003
  },
6776
- pnl: {
7004
+ pnlTier: {
6777
7005
  type: "string",
6778
- description: "Top N% by PnL: PNL_ANY|PNL_TOP50|PNL_TOP20|PNL_TOP5 (default PNL_ANY)"
7006
+ enum: ["PNL_ANY", "PNL_TOP50", "PNL_TOP20", "PNL_TOP5"],
7007
+ default: "PNL_ANY",
7008
+ description: "PnL percentile gate applied on top of `sortBy`. ANY = no filter; TOP50 = PnL \u2265 P50 (median); TOP20 = \u2265 P80; TOP5 = \u2265 P95. PnL distribution is long-tailed \u2014 use percentile, not absolute thresholds."
6779
7009
  },
6780
- winRatio: {
7010
+ winRateTier: {
6781
7011
  type: "string",
6782
- description: "Min win-rate: WR_ANY|WR_GE_50|WR_GE_80 (default WR_ANY)"
7012
+ enum: ["WR_ANY", "WR_GE_50", "WR_GE_80"],
7013
+ default: "WR_ANY",
7014
+ description: "Minimum win-rate gate (fixed thresholds). ANY = no filter; WR_GE_50 = \u2265 50%; WR_GE_80 = \u2265 80%."
6783
7015
  },
6784
- maxRetreat: {
7016
+ maxDrawdownTier: {
6785
7017
  type: "string",
6786
- description: "Max drawdown: MR_ANY|MR_LE_20|MR_LE_50 (default MR_ANY)"
7018
+ enum: ["MR_ANY", "MR_LE_20", "MR_LE_50"],
7019
+ default: "MR_ANY",
7020
+ description: "Maximum-drawdown gate (fixed thresholds; smaller drawdown = lower risk). ANY = no filter; MR_LE_20 = drawdown \u2264 20%; MR_LE_50 = \u2264 50%."
6787
7021
  },
6788
- asset: {
7022
+ aumTier: {
6789
7023
  type: "string",
6790
- description: "Top N% by AUM: AUM_ANY|AUM_TOP50|AUM_TOP20|AUM_TOP5 (default AUM_ANY)"
7024
+ enum: ["AUM_ANY", "AUM_TOP50", "AUM_TOP20", "AUM_TOP5"],
7025
+ default: "AUM_ANY",
7026
+ description: "AUM (Assets Under Management) percentile gate. ANY = no filter; TOP50 = AUM \u2265 P50; TOP20 = \u2265 P80; TOP5 = \u2265 P95. AUM is long-tailed \u2014 use percentile, not absolute USD."
6791
7027
  }
6792
7028
  };
6793
7029
  var LEADERBOARD_POOL_FILTER_PROPS = {
6794
- sortType: {
7030
+ sortBy: {
6795
7031
  type: "string",
6796
- description: "pnl or pnl_ratio"
7032
+ enum: ["pnl", "pnlRatio"],
7033
+ default: "pnl",
7034
+ description: "Leaderboard sort key. `pnl` = absolute USD profit; `pnlRatio` = percentage return."
6797
7035
  },
6798
7036
  period: {
6799
7037
  type: "string",
6800
- description: "3|7|30|90 days, empty=all"
7038
+ enum: PERIOD_DAYS,
7039
+ default: "90",
7040
+ description: 'Performance lookback window in days. Pass as a quoted string: `"3"` / `"7"` / `"30"` / `"90"` (NOT integer 90). Default `"90"` (matches leaderboard UI). Filters AND ranks traders by their PnL over that window.'
6801
7041
  },
6802
- pnl: {
7042
+ minPnl: {
6803
7043
  type: "string",
6804
- description: "Min PnL USD"
7044
+ description: 'Minimum absolute PnL in USD. Pass as a quoted numeric string, e.g. `"10000"` (NOT integer 10000) \u2192 traders with PnL \u2265 $10,000. Numeric threshold \u2014 distinct from the signal-side `pnlTier` percentile enum.'
6805
7045
  },
6806
- winRatio: {
7046
+ minWinRate: {
6807
7047
  type: "string",
6808
- description: "Min ratio (0.8=80%)"
7048
+ description: 'Minimum win-rate as decimal in 0~1 range. Pass as a quoted numeric string, e.g. `"0.8"` (NOT number 0.8) \u2192 traders with win-rate \u2265 80%. Numeric threshold \u2014 distinct from the signal-side `winRateTier` enum.'
6809
7049
  },
6810
- maxRetreat: {
7050
+ maxDrawdown: {
6811
7051
  type: "string",
6812
- description: "Max DD (0.1=10%)"
7052
+ description: 'Maximum drawdown as decimal. Pass as a quoted numeric string, e.g. `"0.1"` (NOT number 0.1) \u2192 traders with drawdown \u2264 10%. Lower = lower risk. Numeric threshold \u2014 distinct from the signal-side `maxDrawdownTier` enum.'
6813
7053
  },
6814
- asset: {
7054
+ minAum: {
6815
7055
  type: "string",
6816
- description: "Min AUM USD"
7056
+ description: 'Minimum AUM (Assets Under Management) in USD. Pass as a quoted numeric string, e.g. `"1000"` (NOT integer 1000) \u2192 traders with AUM \u2265 $1,000. Numeric threshold \u2014 distinct from the signal-side `aumTier` percentile enum.'
6817
7057
  }
6818
7058
  };
6819
- var POOL_FILTER_KEYS = ["sortType", "period", "pnl", "winRatio", "maxRetreat", "asset"];
7059
+ var LEADERBOARD_FILTER_UPSTREAM_NAMES = {
7060
+ sortBy: "sortBy",
7061
+ period: "period",
7062
+ minPnl: "pnl",
7063
+ minWinRate: "winRate",
7064
+ maxDrawdown: "maxDrawdown",
7065
+ minAum: "asset"
7066
+ };
6820
7067
  function readPoolFilters(args) {
6821
7068
  const result = {};
6822
- for (const key of POOL_FILTER_KEYS) {
7069
+ for (const [publicKey, upstreamKey] of Object.entries(LEADERBOARD_FILTER_UPSTREAM_NAMES)) {
7070
+ const val = readString(args, publicKey);
7071
+ if (val !== void 0 && val !== "") result[upstreamKey] = val;
7072
+ }
7073
+ return result;
7074
+ }
7075
+ function readSignalPoolFilters(args) {
7076
+ const result = {};
7077
+ for (const key of Object.keys(SIGNAL_POOL_FILTER_PROPS)) {
6823
7078
  const val = readString(args, key);
6824
7079
  if (val) result[key] = val;
6825
7080
  }
6826
7081
  return result;
6827
7082
  }
6828
- function extractLeaderboardData(data) {
6829
- if (Array.isArray(data)) return data;
7083
+ function extractLeaderboardEnvelope(data) {
7084
+ if (Array.isArray(data)) return { items: data };
6830
7085
  if (data && typeof data === "object") {
6831
- const inner = data.data;
6832
- if (Array.isArray(inner)) return inner;
7086
+ const obj = data;
7087
+ const inner = obj.data;
7088
+ const updateTime = typeof obj.updateTime === "string" ? obj.updateTime : void 0;
7089
+ if (Array.isArray(inner)) return { items: inner, updateTime };
7090
+ }
7091
+ return { items: [] };
7092
+ }
7093
+ function deriveDirection(posSide, pos) {
7094
+ if (posSide === "long") return "long";
7095
+ if (posSide === "short") return "short";
7096
+ if (posSide === "both" && typeof pos === "string" && pos !== "") {
7097
+ const n = Number(pos);
7098
+ if (Number.isFinite(n) && n !== 0) return n > 0 ? "long" : "short";
7099
+ }
7100
+ return void 0;
7101
+ }
7102
+ function extractPositionData(data) {
7103
+ if (!Array.isArray(data) || data.length === 0) return [];
7104
+ const first = data[0];
7105
+ if (first && typeof first === "object") {
7106
+ const posData = first.posData;
7107
+ if (Array.isArray(posData)) {
7108
+ return posData.map((row) => {
7109
+ if (!row || typeof row !== "object") return row;
7110
+ const r = row;
7111
+ const direction = deriveDirection(r.posSide, r.pos);
7112
+ return direction ? { ...r, direction } : r;
7113
+ });
7114
+ }
6833
7115
  }
6834
7116
  return [];
6835
7117
  }
7118
+ function buildPagination(data, effectiveLimit, cursorField) {
7119
+ const hasMore = data.length >= effectiveLimit;
7120
+ const last = data.length > 0 ? data[data.length - 1] : void 0;
7121
+ const nextAfter = hasMore && last && typeof last === "object" && last !== null ? last[cursorField] : void 0;
7122
+ return nextAfter !== void 0 ? { hasMore, nextAfter } : { hasMore };
7123
+ }
7124
+ function extractBaseCcy(instId) {
7125
+ if (!instId) return void 0;
7126
+ const idx = instId.indexOf("-");
7127
+ const base = idx === -1 ? instId : instId.slice(0, idx);
7128
+ return base || void 0;
7129
+ }
7130
+ function actionableError(message, hint) {
7131
+ return new ValidationError(`${message} ${hint}`);
7132
+ }
7133
+ function readArrayAsCsv(args, key) {
7134
+ const arr = readStringArray(args, key);
7135
+ if (!arr || arr.length === 0) return void 0;
7136
+ return arr.join(",");
7137
+ }
7138
+ function envelope(dataSchema, extras = {}) {
7139
+ return {
7140
+ type: "object",
7141
+ properties: {
7142
+ endpoint: {
7143
+ type: "string",
7144
+ description: "Upstream API path that produced this payload (debug/audit aid)."
7145
+ },
7146
+ requestTime: { type: "string", description: "ISO-8601 timestamp when the request was issued." },
7147
+ data: dataSchema,
7148
+ ...extras
7149
+ },
7150
+ required: ["endpoint", "data"]
7151
+ };
7152
+ }
7153
+ var PAGINATION_PROP = {
7154
+ type: "object",
7155
+ description: "Cursor pagination metadata. Only emitted on list tools that support paging.",
7156
+ properties: {
7157
+ hasMore: {
7158
+ type: "boolean",
7159
+ description: "True when `data.length` reached the requested `limit` (more pages likely). False guarantees there are no more results after this page."
7160
+ },
7161
+ nextAfter: {
7162
+ type: "string",
7163
+ description: "Cursor to pass back as `after` on the next call to fetch the following page. Absent/empty when `hasMore` is false."
7164
+ }
7165
+ }
7166
+ };
7167
+ var TRADER_ITEM_PROPS = {
7168
+ authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other smartmoney_get_trader_* tools." },
7169
+ nickName: { type: "string", description: "Display nickname." },
7170
+ pnl: { type: "string", description: "Absolute PnL in USD over the requested `period` (numeric string)." },
7171
+ pnlRatio: { type: "string", description: 'PnL as a decimal ratio over the requested `period` (e.g. "0.35" = +35%).' },
7172
+ asset: { type: "string", description: "AUM (Assets Under Management) in USD \u2014 same field that the input `minAum` filter applies to." },
7173
+ winRate: { type: "string", description: "Lifetime win-rate as a decimal (0~1)." },
7174
+ maxDrawdown: { type: "string", description: 'Max drawdown as a decimal (e.g. "0.2" = 20%). Lower = lower risk.' },
7175
+ onboardDuration: { type: "string", description: "Days the trader has been onboarded on the leaderboard (numeric string)." },
7176
+ portrait: { type: "string", description: "Avatar image URL." },
7177
+ rates: {
7178
+ type: "array",
7179
+ description: "Equity-curve / PnL-rate time series (one entry per day).",
7180
+ items: {
7181
+ type: "object",
7182
+ properties: {
7183
+ statTime: { type: "string", description: 'Date stamp in `YYMMDD` (e.g. "240726" = 2024-07-26).' },
7184
+ value: { type: "string", description: "Cumulative PnL ratio at that day (decimal)." }
7185
+ }
7186
+ }
7187
+ }
7188
+ };
7189
+ var SIGNAL_ITEM_PROPS = {
7190
+ ccy: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP (outer identifier; the field name is `ccy`, not `instId`)." },
7191
+ dataVersion: {
7192
+ type: "string",
7193
+ description: "Snapshot version key in `yyyyMMddHH` UTC (10 digits, e.g. `2026043014` = 2026-04-30 14:00 UTC; floored to the hour)."
7194
+ },
7195
+ tradersWithPosition: {
7196
+ type: "integer",
7197
+ description: "Pool traders holding this asset at latest_snap (double-sided counted once). Higher = stronger consensus."
7198
+ },
7199
+ tradersQualified: {
7200
+ type: "integer",
7201
+ description: "Pool size after applying tier filters (incl. those without positions on this instrument)."
7202
+ },
7203
+ longTraders: { type: "integer", description: "Number of pool traders currently long this asset (incl. double-sided)." },
7204
+ shortTraders: { type: "integer", description: "Number of pool traders currently short this asset (incl. double-sided)." },
7205
+ notional: {
7206
+ type: "object",
7207
+ description: "Notional / capital-flow group.",
7208
+ properties: {
7209
+ longNotionalUsdt: { type: "string", description: "Sum of long-side notional in USDT." },
7210
+ shortNotionalUsdt: { type: "string", description: "Sum of short-side notional in USDT." },
7211
+ netNotionalUsdt: { type: "string", description: "Net directional notional in USDT = long \u2212 short." },
7212
+ totalNotionalUsdt: { type: "string", description: "Gross notional in USDT = long + short." },
7213
+ totalNotionalVs24h: {
7214
+ type: "string",
7215
+ description: "Capital-flow change ratio vs 24h: (curr \u2212 hist_24h) / hist_24h. Positive = smart money adding exposure; negative = retreating. NULL when hist=0."
7216
+ },
7217
+ smartMoneyLongAvgEntry: {
7218
+ type: "string",
7219
+ description: "Long-side notional-weighted average entry price (USDT). NULL when no long. Compare to current price to judge whether following the longs is cheap/expensive now."
7220
+ },
7221
+ smartMoneyShortAvgEntry: {
7222
+ type: "string",
7223
+ description: "Short-side notional-weighted average entry price (USDT). NULL when no short."
7224
+ }
7225
+ }
7226
+ },
7227
+ longShortRatio: {
7228
+ type: "object",
7229
+ description: "Long/short ratio + historical-delta group.",
7230
+ properties: {
7231
+ longRatioVs1h: { type: "string", description: "longRatio \u2212 hist_1h.longRatio. NULL when no hist." },
7232
+ longRatioVs24h: { type: "string", description: "longRatio \u2212 hist_24h.longRatio. NULL when no hist." },
7233
+ longRatioVs7d: { type: "string", description: "longRatio \u2212 hist_7d.longRatio. NULL when no hist." },
7234
+ longRatio: { type: "string", description: "Headcount long ratio = longTraders / tradersWithPosition. NULL when no traders." },
7235
+ shortRatio: { type: "string", description: "1 \u2212 longRatio. NULL when no traders." },
7236
+ weightedLongRatio: {
7237
+ type: "string",
7238
+ description: "Notional-weighted long ratio = \u03A3(long_notional) / \u03A3(notional). NULL when no notional."
7239
+ },
7240
+ weightedShortRatio: {
7241
+ type: "string",
7242
+ description: "Notional-weighted short ratio = \u03A3(short_notional) / \u03A3(notional). NULL when no notional."
7243
+ }
7244
+ }
7245
+ },
7246
+ winRate: {
7247
+ type: "object",
7248
+ description: "Capability (historical performance) group; driven by the `period` window.",
7249
+ properties: {
7250
+ avgLongWinRate: {
7251
+ type: "string",
7252
+ description: "Mean closed-position win-rate (full-market) over `period` days for users currently long. NULL when closed-position sample size is below the configured minimum."
7253
+ },
7254
+ avgShortWinRate: {
7255
+ type: "string",
7256
+ description: "Mean closed-position win-rate (full-market) over `period` days for users currently short. NULL when sample is below threshold."
7257
+ }
7258
+ }
7259
+ }
7260
+ };
7261
+ var SIGNAL_HISTORY_ITEM_PROPS = {
7262
+ ccy: { type: "string", description: "Base currency / instrument key for this bucket." },
7263
+ longRatio: {
7264
+ type: "string",
7265
+ description: "Headcount long ratio at this bucket. Decimal 0~1."
7266
+ },
7267
+ shortRatio: {
7268
+ type: "string",
7269
+ description: "Headcount short ratio at this bucket = 1 \u2212 longRatio. Decimal 0~1."
7270
+ },
7271
+ weightedLongRatio: {
7272
+ type: "string",
7273
+ description: "Notional-weighted long ratio at this bucket. Decimal 0~1."
7274
+ },
7275
+ weightedShortRatio: {
7276
+ type: "string",
7277
+ description: "Notional-weighted short ratio at this bucket. Decimal 0~1."
7278
+ },
7279
+ longTraders: {
7280
+ type: "integer",
7281
+ description: "Number of traders with long exposure at this bucket (includes dual-side traders)."
7282
+ },
7283
+ shortTraders: {
7284
+ type: "integer",
7285
+ description: "Number of traders with short exposure at this bucket (includes dual-side traders)."
7286
+ },
7287
+ tradersWithPosition: {
7288
+ type: "integer",
7289
+ description: "Pool traders holding this asset at this bucket. Few traders = unreliable signal."
7290
+ },
7291
+ netNotionalUsdt: {
7292
+ type: "string",
7293
+ description: "Net directional notional in USDT at this bucket = long notional \u2212 short notional."
7294
+ },
7295
+ totalNotionalUsdt: {
7296
+ type: "string",
7297
+ description: "Gross notional in USDT at this bucket = long notional + short notional. Tracks total capital deployed (rising = adding, falling = retreating)."
7298
+ },
7299
+ tradersQualified: { type: "integer", description: "Pool size after applying tier filters (includes traders without a position)." },
7300
+ dataVersion: { type: "string", description: "Snapshot version key in `yyyyMMddHH` UTC (10-digit, e.g. `2026042820`)." }
7301
+ };
6836
7302
  function registerSmartmoneyTools() {
6837
7303
  const tools = [
6838
- /* ---------- 1. Overview ---------- */
7304
+ /* ===================================================== */
7305
+ /* Trader family (6) */
7306
+ /* ===================================================== */
7307
+ /* ---------- T1. Top traders (leaderboard rank) ---------- */
6839
7308
  {
6840
- name: "smartmoney_get_overview",
7309
+ name: "smartmoney_get_traders_by_filter",
6841
7310
  module: "smartmoney",
6842
- description: "Multi-currency smart money overview, ranked by tradersWithPosition DESC (most-watched first). Requires either ts (recommended, Date.now()) or dataVersion (yyyyMMddHHmm UTC) \u2014 at least one must be set; ts wins when both are sent. For single-currency signal with entry prices and trend, use smartmoney_get_signal.",
7311
+ description: "Leaderboard ranking of OKX smart-money traders, filtered by pool conditions and ranked by `sortBy`. Use when: discovering top performers by criteria (PnL / win-rate / drawdown / AUM). See also: `smartmoney_get_performance_by_trader` (lookup by ID), `smartmoney_search_trader` (lookup by nickname). Note: `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8, different from signal tools' 10-digit UTC `asOfTime`/`dataVersion` \u2014 do not cross-pass.",
6843
7312
  isWrite: false,
7313
+ outputSchema: envelope(
7314
+ { type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
7315
+ {
7316
+ updateTime: {
7317
+ type: "string",
7318
+ description: "Snapshot version of the leaderboard, in `yyyyMMddHHmm` (UTC+8, e.g. `202604301815`). Lives at the response top level (shared by every item in `data`), NOT inside each trader row. Refreshed approximately every 5 minutes."
7319
+ },
7320
+ pagination: PAGINATION_PROP
7321
+ }
7322
+ ),
6844
7323
  inputSchema: {
6845
7324
  type: "object",
6846
7325
  properties: {
6847
- ts: {
6848
- type: "string",
6849
- description: "Recommended. Timestamp ms \u2014 use Date.now() for latest."
6850
- },
6851
- dataVersion: {
6852
- type: "string",
6853
- description: "Alternative. yyyyMMddHHmm UTC for prior snapshot. If both sent, ts wins."
6854
- },
6855
- instType: {
6856
- type: "string",
6857
- description: "SPOT|MARGIN|FUTURES|SWAP|OPTION (default SWAP)"
6858
- },
6859
- ...SIGNAL_POOL_FILTER_PROPS,
6860
- lmtNum: {
7326
+ updateTime: {
6861
7327
  type: "string",
6862
- description: "Trader pool size 1-500 (default 100)"
7328
+ description: 'Snapshot version key. Pass as a quoted 12-digit string `yyyyMMddHHmm` (UTC+8), e.g. `"202604301815"` (NOT integer). Omit to query the latest snapshot (refreshed every ~5 min).'
6863
7329
  },
6864
- instCcyList: {
7330
+ ...LEADERBOARD_POOL_FILTER_PROPS,
7331
+ after: {
6865
7332
  type: "string",
6866
- description: "Comma-separated currency codes e.g. BTC,ETH,SOL (prefix-matched against instId)"
7333
+ description: "Cursor for paginating backwards (older page). Pass the `authorId` of the last item from the previous page verbatim as a quoted string (NOT a number). Cursor anchors on `authorId` while preserving the current `sortBy` order."
6867
7334
  },
6868
- instCcy: {
7335
+ before: {
6869
7336
  type: "string",
6870
- description: "Single currency e.g. BTC; alias for instCcyList (instCcyList wins if both set)"
7337
+ description: "Cursor for paginating forwards (newer page). Pass the `authorId` of the first item from the previous page verbatim as a quoted string. Cursor anchors on `authorId` while preserving the current `sortBy` order."
6871
7338
  },
6872
- topInstruments: {
6873
- type: "string",
6874
- description: "Top N instruments 1-100 (default 20)"
7339
+ limit: {
7340
+ type: "integer",
7341
+ minimum: 1,
7342
+ maximum: 100,
7343
+ default: 10,
7344
+ description: "Max results per page (default 10, max 100)."
6875
7345
  }
6876
7346
  }
6877
7347
  },
6878
7348
  handler: async (rawArgs, context) => {
6879
7349
  const args = asRecord(rawArgs);
6880
- const dv = readString(args, "dataVersion");
6881
- const ts = readString(args, "ts");
6882
- if (!dv && !ts) {
6883
- throw new ValidationError('Either "dataVersion" or "ts" is required for smartmoney_get_overview.');
6884
- }
7350
+ const limit = readNumber(args, "limit");
6885
7351
  const response = await context.client.privateGet(
6886
- PATH_OVERVIEW,
7352
+ PATH_LEADERBOARD,
6887
7353
  compactObject({
6888
- dataVersion: dv,
6889
- ts,
6890
- instType: readString(args, "instType"),
7354
+ updateTime: readString(args, "updateTime"),
6891
7355
  ...readPoolFilters(args),
6892
- lmtNum: readString(args, "lmtNum"),
6893
- instCcyList: readString(args, "instCcyList"),
6894
- instCcy: readString(args, "instCcy"),
6895
- topInstruments: readString(args, "topInstruments")
7356
+ after: readString(args, "after"),
7357
+ before: readString(args, "before"),
7358
+ limit
6896
7359
  }),
6897
- publicRateLimit("smartmoney_get_overview", 5)
7360
+ publicRateLimit("smartmoney_get_traders_by_filter", SMARTMONEY_RPS)
6898
7361
  );
6899
- return normalizeResponse(response);
7362
+ const normalized = normalizeResponse(response);
7363
+ const { items, updateTime } = extractLeaderboardEnvelope(normalized.data);
7364
+ return {
7365
+ ...normalized,
7366
+ data: items,
7367
+ ...updateTime ? { updateTime } : {},
7368
+ pagination: buildPagination(items, limit ?? 10, "authorId")
7369
+ };
6900
7370
  }
6901
7371
  },
6902
- /* ---------- 2. Signal ---------- */
7372
+ /* ---------- T2. Trader performance (by authorIds) ---------- */
6903
7373
  {
6904
- name: "smartmoney_get_signal",
7374
+ name: "smartmoney_get_performance_by_trader",
6905
7375
  module: "smartmoney",
6906
- description: "Single-currency consensus signal: long/short ratio, entry prices, trend, capital flow. Requires either instId (e.g. BTC-USDT-SWAP, recommended) or instCcy (SPOT/SWAP only) \u2014 instId wins when both are sent. Requires either ts (recommended, Date.now()) or dataVersion (yyyyMMddHHmm UTC) \u2014 at least one must be set; ts wins when both are sent. For multi-currency overview, use smartmoney_get_overview. For timeline, use smartmoney_get_signal_history.",
7376
+ description: "PnL / win-rate / drawdown profile for one or more traders looked up by `authorIds`. Use when: caller already has trader IDs and needs their performance metrics. See also: `smartmoney_search_trader` (resolve nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (criteria-based discovery). Note: response `updateTime` is 12-digit `yyyyMMddHHmm` UTC+8 \u2014 do not pass to signal-side tools' `asOfTime` (10-digit UTC).",
6907
7377
  isWrite: false,
7378
+ outputSchema: envelope(
7379
+ { type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
7380
+ {
7381
+ updateTime: {
7382
+ type: "string",
7383
+ description: "Snapshot version of the leaderboard, in `yyyyMMddHHmm` (UTC+8, e.g. `202604301815`). Lives at the response top level (shared by every item in `data`), NOT inside each trader row."
7384
+ }
7385
+ }
7386
+ ),
6908
7387
  inputSchema: {
6909
7388
  type: "object",
6910
7389
  properties: {
6911
- instId: {
6912
- type: "string",
6913
- description: "Recommended. e.g. BTC-USDT-SWAP"
6914
- },
6915
- instCcy: {
6916
- type: "string",
6917
- description: "e.g. BTC (SPOT/SWAP only); instId takes precedence if both set"
6918
- },
6919
- ts: {
6920
- type: "string",
6921
- description: "Recommended. Timestamp ms \u2014 use Date.now() for latest."
6922
- },
6923
- dataVersion: {
6924
- type: "string",
6925
- description: "Alternative. yyyyMMddHHmm UTC for prior snapshot. If both sent, ts wins."
6926
- },
6927
- ...SIGNAL_POOL_FILTER_PROPS,
6928
- lmtNum: {
6929
- type: "string",
6930
- description: "Trader pool size 1-500 (default 100)"
6931
- },
6932
7390
  authorIds: {
7391
+ type: "array",
7392
+ items: { type: "string" },
7393
+ minItems: 1,
7394
+ description: 'Trader IDs to look up, e.g. `["1001", "1002"]`. Required.'
7395
+ },
7396
+ period: {
6933
7397
  type: "string",
6934
- description: "Comma-separated user IDs e.g. 1001,1002 \u2014 restricts the trader pool to these IDs only (precise filter)"
7398
+ enum: PERIOD_DAYS,
7399
+ default: "90",
7400
+ description: 'Performance lookback window in days. Pass as a quoted string: `"3"` / `"7"` / `"30"` / `"90"` (NOT integer). Default `"90"`.'
6935
7401
  }
6936
- }
7402
+ },
7403
+ required: ["authorIds"]
6937
7404
  },
6938
7405
  handler: async (rawArgs, context) => {
6939
7406
  const args = asRecord(rawArgs);
6940
- const instId = readString(args, "instId");
6941
- const instCcy = readString(args, "instCcy");
6942
- if (!instId && !instCcy) {
6943
- throw new ValidationError('Either "instId" or "instCcy" is required for smartmoney_get_signal.');
6944
- }
6945
- const dv = readString(args, "dataVersion");
6946
- const ts = readString(args, "ts");
6947
- if (!dv && !ts) {
6948
- throw new ValidationError('Either "dataVersion" or "ts" is required for smartmoney_get_signal.');
7407
+ const authorIds = readArrayAsCsv(args, "authorIds");
7408
+ if (!authorIds) {
7409
+ throw actionableError(
7410
+ '"authorIds" is required and must be a non-empty array.',
7411
+ 'Discover trader IDs via `smartmoney_get_traders_by_filter`, then pass them as an array (e.g. ["1001", "1002"]).'
7412
+ );
6949
7413
  }
6950
7414
  const response = await context.client.privateGet(
6951
- PATH_SIGNAL,
7415
+ PATH_LEADERBOARD,
6952
7416
  compactObject({
6953
- instId,
6954
- instCcy,
6955
- dataVersion: dv,
6956
- ts,
6957
- ...readPoolFilters(args),
6958
- lmtNum: readString(args, "lmtNum"),
6959
- authorIds: readString(args, "authorIds")
7417
+ authorIds,
7418
+ period: readString(args, "period")
6960
7419
  }),
6961
- publicRateLimit("smartmoney_get_signal", 5)
7420
+ publicRateLimit("smartmoney_get_performance_by_trader", SMARTMONEY_RPS)
6962
7421
  );
6963
- return normalizeResponse(response);
7422
+ const normalized = normalizeResponse(response);
7423
+ const { items, updateTime } = extractLeaderboardEnvelope(normalized.data);
7424
+ return {
7425
+ ...normalized,
7426
+ data: items,
7427
+ ...updateTime ? { updateTime } : {}
7428
+ };
6964
7429
  }
6965
7430
  },
6966
- /* ---------- 3. Signal History ---------- */
7431
+ /* ---------- T3. Trader current positions ---------- */
6967
7432
  {
6968
- name: "smartmoney_get_signal_history",
7433
+ name: "smartmoney_get_trader_positions",
6969
7434
  module: "smartmoney",
6970
- description: "Signal history timeline sorted by ts DESC for trend analysis. Requires instId. Requires either ts (recommended, Date.now()) or dataVersion (yyyyMMddHHmm UTC) \u2014 at least one must be set; ts wins when both are sent. For current snapshot, use smartmoney_get_signal.",
7435
+ description: "Currently-open positions held by a single trader (direction, size, leverage, entry, conviction). Use when: inspecting what a top trader is holding RIGHT NOW. See also: `smartmoney_get_trader_positions_history` (closed positions), `smartmoney_search_trader` (nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (discover trader).",
6971
7436
  isWrite: false,
7437
+ outputSchema: envelope({
7438
+ type: "array",
7439
+ items: {
7440
+ type: "object",
7441
+ properties: {
7442
+ posId: { type: "string", description: "Unique position ID. Stable across the position's lifetime." },
7443
+ instId: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP." },
7444
+ instType: {
7445
+ type: "string",
7446
+ description: "Instrument business line: `SWAP` (perpetual) | `SPOT` | `FUTURES` (delivery) | `MARGIN` | `OPTION`."
7447
+ },
7448
+ posSide: {
7449
+ type: "string",
7450
+ description: "Raw upstream position direction. `long` = long-side position (buy-to-open); `short` = short-side position (sell-to-open); `both` = net/one-way position mode where the sign of `pos` encodes direction. Prefer the derived `direction` field below for agent logic."
7451
+ },
7452
+ direction: {
7453
+ type: "string",
7454
+ enum: ["long", "short"],
7455
+ description: 'Derived clean direction (`long` | `short`) \u2014 handler computes this from `posSide` + sign of `pos` so agents do not have to branch on the `posSide="both"` net-mode case.'
7456
+ },
7457
+ posCcy: { type: "string", description: 'Position currency \u2014 the asset being held, e.g. "BTC".' },
7458
+ quoteCcy: { type: "string", description: 'Quote currency the position is priced/settled in, e.g. "USDT".' },
7459
+ pos: {
7460
+ type: "string",
7461
+ description: "Position size (numeric string). Unit depends on instType: coins for SPOT/MARGIN, contracts (\u5F20) for SWAP/FUTURES/OPTION."
7462
+ },
7463
+ lever: { type: "string", description: 'Leverage multiplier (numeric string; "1" for spot).' },
7464
+ avgPx: { type: "string", description: "Volume-weighted average entry price (numeric string)." },
7465
+ last: { type: "string", description: "Latest market/mark price for the instrument (numeric string)." },
7466
+ notionalUsd: { type: "string", description: "Current position notional value in USD." },
7467
+ upl: { type: "string", description: "Unrealized (floating) PnL, denominated in `quoteCcy`." },
7468
+ pnl: { type: "string", description: "Realized PnL accrued on this position so far, denominated in `quoteCcy`." },
7469
+ cTime: { type: "string", description: "Position open time as Unix milliseconds (numeric string)." },
7470
+ positionIntensity: {
7471
+ type: "string",
7472
+ description: "Conviction metric = notionalUsd / trader.asset (this position's notional as a share of the trader's AUM). Higher = the trader is betting a larger fraction of their book on this position."
7473
+ }
7474
+ }
7475
+ }
7476
+ }),
6972
7477
  inputSchema: {
6973
7478
  type: "object",
6974
7479
  properties: {
6975
- instId: {
7480
+ authorId: {
6976
7481
  type: "string",
6977
- description: "e.g. BTC-USDT-SWAP"
7482
+ description: "Trader's unique ID (obtain from `smartmoney_get_traders_by_filter`)."
6978
7483
  },
6979
- ts: {
7484
+ instId: {
7485
+ type: "string",
7486
+ description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") \u2014 the handler extracts the base currency for the upstream filter.'
7487
+ }
7488
+ },
7489
+ required: ["authorId"]
7490
+ },
7491
+ handler: async (rawArgs, context) => {
7492
+ const args = asRecord(rawArgs);
7493
+ const authorId = readString(args, "authorId");
7494
+ if (!authorId) {
7495
+ throw actionableError(
7496
+ '"authorId" is required.',
7497
+ "Discover trader IDs via `smartmoney_get_traders_by_filter` and pass one ID here."
7498
+ );
7499
+ }
7500
+ const response = await context.client.privateGet(
7501
+ PATH_POSITION_CURRENT,
7502
+ compactObject({
7503
+ authorId,
7504
+ instCcy: extractBaseCcy(readString(args, "instId"))
7505
+ }),
7506
+ publicRateLimit("smartmoney_get_trader_positions", SMARTMONEY_RPS)
7507
+ );
7508
+ const normalized = normalizeResponse(response);
7509
+ return { ...normalized, data: extractPositionData(normalized.data) };
7510
+ }
7511
+ },
7512
+ /* ---------- T4. Trader closed-position history ---------- */
7513
+ {
7514
+ name: "smartmoney_get_trader_positions_history",
7515
+ module: "smartmoney",
7516
+ description: "Closed-position history of a single trader, paginated by `posId` cursor. Use when: studying realized PnL pattern, holding duration, win/loss streaks, or how positions ended (closed vs liquidated). See also: `smartmoney_get_trader_positions` (currently-open), `smartmoney_search_trader` (nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7517
+ isWrite: false,
7518
+ outputSchema: envelope(
7519
+ {
7520
+ type: "array",
7521
+ items: {
7522
+ type: "object",
7523
+ properties: {
7524
+ posId: { type: "string", description: "Unique closed-position ID. Use as the `after` / `before` cursor when paginating." },
7525
+ instId: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP." },
7526
+ instType: {
7527
+ type: "string",
7528
+ description: "Instrument business line: `SWAP` (perpetual) | `FUTURES` (delivery) | `MARGIN` | `SPOT`."
7529
+ },
7530
+ ctVal: {
7531
+ type: "string",
7532
+ description: "Contract face value \u2014 USD value of a single contract (\u5F20). Numeric string. Empty/0 for non-contract instruments."
7533
+ },
7534
+ posSide: {
7535
+ type: "string",
7536
+ description: "Position direction at close. `long` = was long-side; `short` = was short-side."
7537
+ },
7538
+ lever: { type: "string", description: "Leverage multiplier used for this position (numeric string)." },
7539
+ mgnMode: {
7540
+ type: "string",
7541
+ description: "Margin mode used for this position. `cross` = cross-margin (shared collateral pool); `isolated` = isolated-margin (per-position collateral)."
7542
+ },
7543
+ marginCcy: {
7544
+ type: "string",
7545
+ description: 'Margin currency held as collateral for this position, e.g. "BTC" or "USDT".'
7546
+ },
7547
+ quoteCcy: { type: "string", description: 'Quote currency the position settled in, e.g. "USDT".' },
7548
+ openAvgPx: { type: "string", description: "Volume-weighted average price across all open fills (numeric string)." },
7549
+ closeAvgPx: { type: "string", description: "Volume-weighted average price across all close fills (numeric string)." },
7550
+ openMaxAmount: {
7551
+ type: "string",
7552
+ description: "Peak position size held during the position's lifetime, in contracts (\u5F20)."
7553
+ },
7554
+ closeAmount: {
7555
+ type: "string",
7556
+ description: "Total amount closed across all close fills, in contracts for SWAP/FUTURES or in base currency for SPOT/MARGIN (numeric string)."
7557
+ },
7558
+ realizedPnl: { type: "string", description: "Cumulative realized PnL during the position's lifetime, in `quoteCcy` units." },
7559
+ pnl: {
7560
+ type: "string",
7561
+ description: "Total realized PnL for this position including fees and funding, denominated in quoteCcy (numeric string). Differs from `realizedPnl` which may exclude fees."
7562
+ },
7563
+ pnlRatio: {
7564
+ type: "string",
7565
+ description: 'Realized PnL as a decimal ratio of cost basis (e.g. "0.15" = +15%, "-0.20" = \u221220%).'
7566
+ },
7567
+ fee: {
7568
+ type: "string",
7569
+ description: "Cumulative trading fee paid over the position's lifetime, denominated in quoteCcy (numeric string, negative = cost)."
7570
+ },
7571
+ fundingFee: {
7572
+ type: "string",
7573
+ description: "Cumulative funding fee paid or received over the position's lifetime, denominated in quoteCcy (numeric string; negative = paid, positive = received)."
7574
+ },
7575
+ liquidationStatus: {
7576
+ type: "string",
7577
+ description: 'Whether the position was liquidated. `"0"` = normal close (not liquidated); `"1"` = liquidated. Use this dedicated field for liquidation checks; `closeType` may also encode liquidation via the `liquidateClose` / `liquidateReceive` / `adl` values.'
7578
+ },
7579
+ closeType: {
7580
+ type: "string",
7581
+ description: "How the position was closed. `allClose` = entire position closed in one action; `partClose` = partially closed (position reduced but not fully exited); `liquidateClose` = forced liquidation; `liquidateReceive` = received liquidation transfer; `adl` = auto-deleveraging."
7582
+ },
7583
+ cTime: { type: "string", description: "Position open time as Unix milliseconds (numeric string)." },
7584
+ uTime: { type: "string", description: "Position close time as Unix milliseconds (numeric string)." }
7585
+ }
7586
+ }
7587
+ },
7588
+ { pagination: PAGINATION_PROP }
7589
+ ),
7590
+ inputSchema: {
7591
+ type: "object",
7592
+ properties: {
7593
+ authorId: {
6980
7594
  type: "string",
6981
- description: "Recommended. Timestamp ms \u2014 use Date.now() for latest."
7595
+ description: "Trader's unique ID (obtain from `smartmoney_get_traders_by_filter`)."
6982
7596
  },
6983
- dataVersion: {
7597
+ instId: {
6984
7598
  type: "string",
6985
- description: "Alternative. yyyyMMddHHmm UTC for prior snapshot. If both sent, ts wins."
7599
+ description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") \u2014 the handler extracts the base currency for the upstream filter.'
6986
7600
  },
6987
- granularity: {
7601
+ after: {
6988
7602
  type: "string",
6989
- description: "1h or 1d (default 1h)"
7603
+ description: "Cursor: returns positions with `posId` smaller than this value (older \u2014 paginate backwards). Pass as quoted string (the `posId` value verbatim, NOT a number \u2014 `posId` is a 19-digit ID and number coercion can lose precision)."
6990
7604
  },
6991
- limit: {
7605
+ before: {
6992
7606
  type: "string",
6993
- description: "Data points 1-500 (default 24)"
7607
+ description: "Cursor: returns positions with `posId` greater than this value (newer \u2014 paginate forwards). Pass as quoted string."
6994
7608
  },
6995
- ...SIGNAL_POOL_FILTER_PROPS
7609
+ limit: {
7610
+ type: "integer",
7611
+ minimum: 1,
7612
+ maximum: 100,
7613
+ default: 10,
7614
+ description: "Max positions per page (default 10, max 100)."
7615
+ }
6996
7616
  },
6997
- required: ["instId"]
7617
+ required: ["authorId"]
6998
7618
  },
6999
7619
  handler: async (rawArgs, context) => {
7000
7620
  const args = asRecord(rawArgs);
7001
- const dv = readString(args, "dataVersion");
7002
- const ts = readString(args, "ts");
7003
- if (!dv && !ts) {
7004
- throw new ValidationError('Either "dataVersion" or "ts" is required for smartmoney_get_signal_history.');
7621
+ const authorId = readString(args, "authorId");
7622
+ if (!authorId) {
7623
+ throw actionableError(
7624
+ '"authorId" is required.',
7625
+ "Discover trader IDs via `smartmoney_get_traders_by_filter`."
7626
+ );
7005
7627
  }
7628
+ const limit = readNumber(args, "limit");
7006
7629
  const response = await context.client.privateGet(
7007
- PATH_SIGNAL_HISTORY,
7630
+ PATH_POSITION_HISTORY,
7008
7631
  compactObject({
7009
- instId: requireString(args, "instId"),
7010
- dataVersion: dv,
7011
- ts,
7012
- granularity: readString(args, "granularity"),
7013
- limit: readString(args, "limit"),
7014
- ...readPoolFilters(args)
7632
+ authorId,
7633
+ instCcy: extractBaseCcy(readString(args, "instId")),
7634
+ after: readString(args, "after"),
7635
+ before: readString(args, "before"),
7636
+ limit
7015
7637
  }),
7016
- publicRateLimit("smartmoney_get_signal_history", 5)
7638
+ publicRateLimit("smartmoney_get_trader_positions_history", SMARTMONEY_RPS)
7017
7639
  );
7018
- return normalizeResponse(response);
7640
+ const normalized = normalizeResponse(response);
7641
+ const data = Array.isArray(normalized.data) ? normalized.data : [];
7642
+ return {
7643
+ ...normalized,
7644
+ data,
7645
+ pagination: buildPagination(data, limit ?? 10, "posId")
7646
+ };
7019
7647
  }
7020
7648
  },
7021
- /* ---------- 4. Traders (list) ---------- */
7649
+ /* ---------- T5. Trader order history ---------- */
7022
7650
  {
7023
- name: "smartmoney_get_traders",
7651
+ name: "smartmoney_get_trader_orders_history",
7024
7652
  module: "smartmoney",
7025
- description: "List/filter leaderboard traders. For single trader detail: smartmoney_get_trader_detail.",
7653
+ description: "Recent orders/fills placed by a single trader (direction, size, price, leverage), paginated by `ordId` cursor. Aligned with the cross-module `*_get_orders` family. Use when: tracking a top trader's latest trade activity. See also: `smartmoney_search_trader` (nickname \u2192 authorId), `smartmoney_get_traders_by_filter` (discover trader).",
7026
7654
  isWrite: false,
7655
+ outputSchema: envelope(
7656
+ {
7657
+ type: "array",
7658
+ items: {
7659
+ type: "object",
7660
+ properties: {
7661
+ ordId: { type: "string", description: "Unique order ID. Use as the `after` / `before` cursor when paginating." },
7662
+ instId: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP." },
7663
+ displayId: { type: "string", description: "Display-form instrument ID used in OKX UI." },
7664
+ instType: {
7665
+ type: "string",
7666
+ description: "Instrument business line: `SWAP` (perpetual contract) | `SPOT`."
7667
+ },
7668
+ baseName: { type: "string", description: 'Base currency symbol, e.g. "BTC".' },
7669
+ quoteName: { type: "string", description: 'Quote currency symbol, e.g. "USD".' },
7670
+ tradeQuoteCcy: { type: "string", description: "Quote currency the fill actually settled in." },
7671
+ side: {
7672
+ type: "string",
7673
+ description: "Order side. `buy` = open long / close short; `sell` = open short / close long."
7674
+ },
7675
+ posSide: {
7676
+ type: "string",
7677
+ description: "Position direction the order applies to: `long` | `short`. Indicates whether the trader was opening/closing a long-side or short-side position."
7678
+ },
7679
+ ordType: {
7680
+ type: "string",
7681
+ description: "Order type. `limit` = price-protected limit order; `market` = immediate at best available price."
7682
+ },
7683
+ lever: { type: "string", description: 'Leverage multiplier used for this order (numeric string; "1" for spot).' },
7684
+ px: { type: "string", description: "Submitted order price (numeric string). For market orders this may be empty/0." },
7685
+ avgPx: { type: "string", description: "Volume-weighted average fill price (numeric string)." },
7686
+ sz: {
7687
+ type: "string",
7688
+ description: "Order size. Unit depends on instType: coins (\u5E01) for SPOT, contracts (\u5F20) for SWAP/FUTURES."
7689
+ },
7690
+ value: {
7691
+ type: "string",
7692
+ description: "Order notional value, denominated in `quoteName` units."
7693
+ },
7694
+ cTime: { type: "string", description: "Order creation time as Unix milliseconds (numeric string)." },
7695
+ fillTime: {
7696
+ type: "string",
7697
+ description: "Timestamp when the order was last filled, as Unix milliseconds (numeric string)."
7698
+ },
7699
+ uTime: {
7700
+ type: "string",
7701
+ description: "Timestamp when the order record was last updated, as Unix milliseconds (numeric string)."
7702
+ }
7703
+ }
7704
+ }
7705
+ },
7706
+ { pagination: PAGINATION_PROP }
7707
+ ),
7027
7708
  inputSchema: {
7028
7709
  type: "object",
7029
7710
  properties: {
7030
- dataVersion: {
7711
+ authorId: {
7031
7712
  type: "string",
7032
- description: "yyyyMMddHHmm, omit=latest"
7713
+ description: "Trader's unique ID (obtain from `smartmoney_get_traders_by_filter`)."
7033
7714
  },
7034
- ...LEADERBOARD_POOL_FILTER_PROPS,
7035
- authorIds: {
7715
+ instId: {
7036
7716
  type: "string",
7037
- description: "Comma-separated author IDs"
7717
+ description: 'Optional instrument filter. Accepts either full instId (e.g. "BTC-USDT-SWAP") or bare base currency (e.g. "BTC") \u2014 the handler extracts the base currency for the upstream filter.'
7038
7718
  },
7039
7719
  after: {
7040
7720
  type: "string",
7041
- description: "Cursor after this authorId"
7721
+ description: "Cursor: returns trades with `ordId` smaller than this value (older \u2014 paginate backwards). Pass as quoted string (the `ordId` value verbatim, NOT a number \u2014 `ordId` is a 19-digit ID and number coercion can lose precision)."
7042
7722
  },
7043
7723
  before: {
7044
7724
  type: "string",
7045
- description: "Cursor before this authorId"
7725
+ description: "Cursor: returns trades with `ordId` greater than this value (newer \u2014 paginate forwards). Pass as quoted string."
7046
7726
  },
7047
7727
  limit: {
7048
- type: "string",
7049
- description: "Max results 1-100"
7728
+ type: "integer",
7729
+ minimum: 1,
7730
+ maximum: 100,
7731
+ default: 10,
7732
+ description: "Max trades per page (default 10, max 100)."
7050
7733
  }
7051
- }
7734
+ },
7735
+ required: ["authorId"]
7052
7736
  },
7053
7737
  handler: async (rawArgs, context) => {
7054
7738
  const args = asRecord(rawArgs);
7739
+ const authorId = readString(args, "authorId");
7740
+ if (!authorId) {
7741
+ throw actionableError(
7742
+ '"authorId" is required.',
7743
+ "Discover trader IDs via `smartmoney_get_traders_by_filter`."
7744
+ );
7745
+ }
7746
+ const limit = readNumber(args, "limit");
7055
7747
  const response = await context.client.privateGet(
7056
- PATH_LEADERBOARD,
7748
+ PATH_TRADE_RECORDS,
7057
7749
  compactObject({
7058
- dataVersion: readString(args, "dataVersion"),
7059
- ...readPoolFilters(args),
7060
- authorIds: readString(args, "authorIds"),
7750
+ authorId,
7751
+ instCcy: extractBaseCcy(readString(args, "instId")),
7061
7752
  after: readString(args, "after"),
7062
7753
  before: readString(args, "before"),
7063
- limit: readString(args, "limit")
7754
+ limit
7064
7755
  }),
7065
- publicRateLimit("smartmoney_get_traders", 5)
7756
+ publicRateLimit("smartmoney_get_trader_orders_history", SMARTMONEY_RPS)
7066
7757
  );
7067
7758
  const normalized = normalizeResponse(response);
7068
- return { ...normalized, data: extractLeaderboardData(normalized.data) };
7759
+ const data = Array.isArray(normalized.data) ? normalized.data : [];
7760
+ return {
7761
+ ...normalized,
7762
+ data,
7763
+ pagination: buildPagination(data, limit ?? 10, "ordId")
7764
+ };
7069
7765
  }
7070
7766
  },
7071
- /* ---------- 5. Trader Detail (composite) ---------- */
7767
+ /* ---------- T6. Search top traders by nickname keyword ---------- */
7072
7768
  {
7073
- name: "smartmoney_get_trader_detail",
7769
+ name: "smartmoney_search_trader",
7074
7770
  module: "smartmoney",
7075
- description: "Trader portrait: profile + positions + trades. Requires authorId from smartmoney_get_traders. Do NOT use for listing \u2014 use smartmoney_get_traders.",
7771
+ description: "Search Top Traders by nickname keyword, ranked by OKX-platform follower count DESC. Returns up to 10 matches; intersects KOL full-text recall with the Top Trader set. Use when: resolving a nickname or partial name to `authorId`(s) before calling other `smartmoney_get_trader_*` tools. See also: `smartmoney_get_traders_by_filter` (discover top performers by criteria), `smartmoney_get_performance_by_trader` (lookup by known authorId).",
7076
7772
  isWrite: false,
7773
+ outputSchema: envelope({
7774
+ type: "array",
7775
+ description: "Matched Top Traders (\u226410), sorted by `followerCount` DESC. Empty array when no recall intersects the Top Trader set.",
7776
+ items: {
7777
+ type: "object",
7778
+ properties: {
7779
+ authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other `smartmoney_get_trader_*` tools." },
7780
+ nickName: { type: "string", description: "Display nickname matched against the keyword." },
7781
+ followerCount: { type: "string", description: "OKX-platform follower count (numeric string; Twitter followers excluded). Sort key." }
7782
+ }
7783
+ }
7784
+ }),
7077
7785
  inputSchema: {
7078
7786
  type: "object",
7079
7787
  properties: {
7080
- authorId: {
7788
+ keyword: {
7081
7789
  type: "string",
7082
- description: "Trader author ID"
7790
+ description: "Nickname search keyword. Required, must be non-empty / non-whitespace. Matched candidates are intersected with the Top Trader set."
7791
+ }
7792
+ },
7793
+ required: ["keyword"]
7794
+ },
7795
+ handler: async (rawArgs, context) => {
7796
+ const args = asRecord(rawArgs);
7797
+ const keyword = readString(args, "keyword");
7798
+ if (!keyword || keyword.trim() === "") {
7799
+ throw actionableError(
7800
+ '"keyword" is required and must be non-empty.',
7801
+ 'Pass a nickname fragment (e.g. "alice", "\u5C0F\u660E"). For known author IDs use `smartmoney_get_performance_by_trader` instead.'
7802
+ );
7803
+ }
7804
+ const response = await context.client.privateGet(
7805
+ PATH_TOP_TRADER_SEARCH,
7806
+ compactObject({ keyword }),
7807
+ publicRateLimit("smartmoney_search_trader", SMARTMONEY_RPS)
7808
+ );
7809
+ const normalized = normalizeResponse(response);
7810
+ const data = Array.isArray(normalized.data) ? normalized.data : [];
7811
+ return { ...normalized, data };
7812
+ }
7813
+ },
7814
+ /* ===================================================== */
7815
+ /* Signal/Coin family (4) */
7816
+ /* ===================================================== */
7817
+ /* ---------- S1. Signal overview by filter (multi-asset, tier-filtered pool) ---------- */
7818
+ {
7819
+ name: "smartmoney_get_signal_overview_by_filter",
7820
+ module: "smartmoney",
7821
+ description: "Multi-asset smart-money consensus signals (long/short ratio, weighted entry, capital flow, deltas vs 1h/24h/7d), aggregated over a tier-filtered trader pool (PnL / win-rate / drawdown / AUM). Pick instruments via `topInstruments` OR `instCcyList` \u2014 exactly one. Snapshot time auto-resolved to current hour. Use when: latest cross-asset consensus from a criteria-defined pool. See also: `smartmoney_get_signal_overview_by_trader` (restrict pool to specific traders), `smartmoney_get_signal_trend_by_filter` (time-series instead of latest snapshot).",
7822
+ isWrite: false,
7823
+ outputSchema: envelope({
7824
+ type: "array",
7825
+ description: "Per-instrument snapshot, one element per requested coin.",
7826
+ items: { type: "object", properties: SIGNAL_ITEM_PROPS }
7827
+ }),
7828
+ inputSchema: {
7829
+ type: "object",
7830
+ properties: {
7831
+ topInstruments: {
7832
+ type: "integer",
7833
+ minimum: 1,
7834
+ maximum: 100,
7835
+ default: 20,
7836
+ description: "Top-N hottest instruments to aggregate (sorted by `tradersWithPosition` DESC). Mutually exclusive with `instCcyList`; one of the two is required (default 20 if neither provided)."
7083
7837
  },
7084
- period: {
7085
- type: "string",
7086
- description: "3|7|30|90 days, omit=all"
7838
+ instCcyList: {
7839
+ type: "array",
7840
+ items: { type: "string" },
7841
+ minItems: 1,
7842
+ description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`.'
7843
+ },
7844
+ ...SIGNAL_POOL_FILTER_PROPS,
7845
+ lmtNum: {
7846
+ type: "integer",
7847
+ minimum: 1,
7848
+ maximum: 2e3,
7849
+ default: 100,
7850
+ description: "Top-N traders to pull into the aggregation pool, ranked by `sortBy` (DESC). Larger pool = stronger signal but slower. Default 100 is fine for most cases."
7851
+ }
7852
+ }
7853
+ },
7854
+ handler: async (rawArgs, context) => {
7855
+ const args = asRecord(rawArgs);
7856
+ const instCcyList = readArrayAsCsv(args, "instCcyList");
7857
+ const topInstrumentsRaw = readNumber(args, "topInstruments");
7858
+ if (instCcyList && topInstrumentsRaw !== void 0) {
7859
+ throw actionableError(
7860
+ '"topInstruments" and "instCcyList" are mutually exclusive.',
7861
+ "Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7862
+ );
7863
+ }
7864
+ const response = await context.client.privateGet(
7865
+ PATH_OVERVIEW,
7866
+ compactObject({
7867
+ ...instCcyList ? { instCcyList } : { topInstruments: topInstrumentsRaw ?? 20 },
7868
+ ...readSignalPoolFilters(args),
7869
+ lmtNum: readNumber(args, "lmtNum")
7870
+ }),
7871
+ publicRateLimit("smartmoney_get_signal_overview_by_filter", SMARTMONEY_RPS)
7872
+ );
7873
+ return normalizeResponse(response);
7874
+ }
7875
+ },
7876
+ /* ---------- S2. Signal overview by trader (multi-asset, authorIds-restricted) ---------- */
7877
+ {
7878
+ name: "smartmoney_get_signal_overview_by_trader",
7879
+ module: "smartmoney",
7880
+ description: "Multi-asset smart-money signals aggregated over a hand-picked set of traders (`authorIds`). Pick instruments via `topInstruments` OR `instCcyList`. Pool sizing / ranking / capability filters not exposed \u2014 backend uses defaults for direct-lookup scenarios. Use when: caller already knows which traders to follow and wants their cross-asset consensus at the latest hour. See also: `smartmoney_get_signal_overview_by_filter` (criteria-defined pool), `smartmoney_get_signal_trend_by_trader` (time-series), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds).",
7881
+ isWrite: false,
7882
+ outputSchema: envelope({
7883
+ type: "array",
7884
+ description: "Per-instrument snapshot, one element per requested coin.",
7885
+ items: { type: "object", properties: SIGNAL_ITEM_PROPS }
7886
+ }),
7887
+ inputSchema: {
7888
+ type: "object",
7889
+ properties: {
7890
+ authorIds: {
7891
+ type: "array",
7892
+ items: { type: "string" },
7893
+ minItems: 1,
7894
+ description: 'Trader IDs to aggregate over, e.g. `["1001", "1002"]`. Required.'
7895
+ },
7896
+ topInstruments: {
7897
+ type: "integer",
7898
+ minimum: 1,
7899
+ maximum: 100,
7900
+ default: 20,
7901
+ description: "Top-N hottest instruments held by the given traders (sorted by `tradersWithPosition` DESC). Mutually exclusive with `instCcyList`; one of the two is required (default 20 if neither provided)."
7087
7902
  },
7903
+ instCcyList: {
7904
+ type: "array",
7905
+ items: { type: "string" },
7906
+ minItems: 1,
7907
+ description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`.'
7908
+ }
7909
+ },
7910
+ required: ["authorIds"]
7911
+ },
7912
+ handler: async (rawArgs, context) => {
7913
+ const args = asRecord(rawArgs);
7914
+ const authorIds = readArrayAsCsv(args, "authorIds");
7915
+ if (!authorIds) {
7916
+ throw actionableError(
7917
+ '"authorIds" is required and must be a non-empty array.',
7918
+ 'Pass IDs from `smartmoney_get_traders_by_filter` as an array (e.g. ["1001", "1002"]).'
7919
+ );
7920
+ }
7921
+ const instCcyList = readArrayAsCsv(args, "instCcyList");
7922
+ const topInstrumentsRaw = readNumber(args, "topInstruments");
7923
+ if (instCcyList && topInstrumentsRaw !== void 0) {
7924
+ throw actionableError(
7925
+ '"topInstruments" and "instCcyList" are mutually exclusive.',
7926
+ "Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
7927
+ );
7928
+ }
7929
+ const response = await context.client.privateGet(
7930
+ PATH_OVERVIEW,
7931
+ compactObject({
7932
+ authorIds,
7933
+ ...instCcyList ? { instCcyList } : { topInstruments: topInstrumentsRaw ?? 20 }
7934
+ }),
7935
+ publicRateLimit("smartmoney_get_signal_overview_by_trader", SMARTMONEY_RPS)
7936
+ );
7937
+ return normalizeResponse(response);
7938
+ }
7939
+ },
7940
+ /* ---------- S3. Signal trend by filter (single-asset, tier-filtered pool, asOfTime anchor) ---------- */
7941
+ {
7942
+ name: "smartmoney_get_signal_trend_by_filter",
7943
+ module: "smartmoney",
7944
+ description: "Time-series of single-asset smart-money signal across hourly/daily buckets, aggregated over a tier-filtered trader pool. Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). Use when: tracking how long/short conviction and capital evolve over time (smart money adding exposure or retreating). See also: `smartmoney_get_signal_overview_by_filter` (latest snapshot only), `smartmoney_get_signal_trend_by_trader` (restrict to specific traders). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` \u2014 do not cross-pass.",
7945
+ isWrite: false,
7946
+ outputSchema: envelope({
7947
+ type: "array",
7948
+ description: "Time-bucket series for the requested instrument, sorted by time DESC (newest first).",
7949
+ items: { type: "object", properties: SIGNAL_HISTORY_ITEM_PROPS }
7950
+ }),
7951
+ inputSchema: {
7952
+ type: "object",
7953
+ properties: {
7088
7954
  instCcy: {
7089
7955
  type: "string",
7090
- description: "Currency filter e.g. BTC"
7956
+ description: 'Base currency to scope the time-series, e.g. "BTC". Required.'
7091
7957
  },
7092
- tradeLimit: {
7958
+ asOfTime: {
7093
7959
  type: "string",
7094
- description: "Max trades 1-100"
7960
+ description: 'Anchor snapshot time. Pass as a quoted 10-digit string `yyyyMMddHH` UTC, e.g. `"2026050100"` (NOT integer 2026050100). Returns the latest `limit` buckets ending at this anchor. Omit to use the current UTC hour.'
7961
+ },
7962
+ granularity: {
7963
+ type: "string",
7964
+ enum: ["1h", "1d"],
7965
+ default: "1h",
7966
+ description: "Time-bucket size. `1h` = hourly snapshots (intraday/short-term trend), `1d` = daily snapshots (multi-day trend)."
7967
+ },
7968
+ limit: {
7969
+ type: "integer",
7970
+ minimum: 1,
7971
+ maximum: 500,
7972
+ default: 24,
7973
+ description: "Number of buckets to return (newest first), ending at `asOfTime`. Default 24, max 500."
7974
+ },
7975
+ ...SIGNAL_POOL_FILTER_PROPS,
7976
+ lmtNum: {
7977
+ type: "integer",
7978
+ minimum: 1,
7979
+ maximum: 2e3,
7980
+ default: 100,
7981
+ description: "Top-N traders to pull into the aggregation pool, ranked by `sortBy` (DESC). Default 100, max 2000."
7095
7982
  }
7096
7983
  },
7097
- required: ["authorId"]
7984
+ required: ["instCcy"]
7098
7985
  },
7099
7986
  handler: async (rawArgs, context) => {
7100
7987
  const args = asRecord(rawArgs);
7101
- const authorId = requireString(args, "authorId");
7102
- const period = readString(args, "period");
7103
7988
  const instCcy = readString(args, "instCcy");
7104
- const tradeLimit = readString(args, "tradeLimit");
7105
- const [profileRes, positionsRes, tradesRes] = await Promise.all([
7106
- context.client.privateGet(
7107
- PATH_LEADERBOARD,
7108
- compactObject({ authorIds: authorId, period }),
7109
- publicRateLimit("smartmoney_get_traders", 5)
7110
- ),
7111
- context.client.privateGet(
7112
- PATH_POSITION_CURRENT,
7113
- compactObject({ authorId, instCcy }),
7114
- publicRateLimit("smartmoney_trader_positions", 5)
7115
- ),
7116
- context.client.privateGet(
7117
- PATH_TRADE_RECORDS,
7118
- compactObject({ authorId, instCcy, limit: tradeLimit }),
7119
- publicRateLimit("smartmoney_trade_records", 5)
7120
- )
7121
- ]);
7122
- const profileNorm = normalizeResponse(profileRes);
7123
- const positionsNorm = normalizeResponse(positionsRes);
7124
- const tradesNorm = normalizeResponse(tradesRes);
7125
- return {
7126
- endpoint: "smartmoney_get_trader_detail (composite)",
7127
- requestTime: (/* @__PURE__ */ new Date()).toISOString(),
7128
- data: {
7129
- profile: extractLeaderboardData(profileNorm.data),
7130
- positions: positionsNorm.data,
7131
- trades: tradesNorm.data
7989
+ if (!instCcy) {
7990
+ throw actionableError(
7991
+ '"instCcy" is required.',
7992
+ 'Pass a base currency, e.g. "BTC".'
7993
+ );
7994
+ }
7995
+ const response = await context.client.privateGet(
7996
+ PATH_SIGNAL_HISTORY,
7997
+ compactObject({
7998
+ instCcy,
7999
+ asOfTime: readString(args, "asOfTime"),
8000
+ granularity: readString(args, "granularity"),
8001
+ limit: readNumber(args, "limit"),
8002
+ ...readSignalPoolFilters(args),
8003
+ lmtNum: readNumber(args, "lmtNum")
8004
+ }),
8005
+ publicRateLimit("smartmoney_get_signal_trend_by_filter", SMARTMONEY_RPS)
8006
+ );
8007
+ return normalizeResponse(response);
8008
+ }
8009
+ },
8010
+ /* ---------- S4. Signal trend by trader (single-asset, authorIds-restricted) ---------- */
8011
+ {
8012
+ name: "smartmoney_get_signal_trend_by_trader",
8013
+ module: "smartmoney",
8014
+ description: "Time-series of single-asset smart-money signal aggregated over a hand-picked set of traders (`authorIds`). Returns the latest `limit` buckets ending at `asOfTime` (defaults to current UTC hour). Pool sizing / ranking / capability filters not exposed \u2014 backend uses defaults for direct-lookup scenarios. Use when: tracking how a specific group of traders has evolved their long/short consensus over time on one coin. See also: `smartmoney_get_signal_trend_by_filter` (criteria-defined pool), `smartmoney_get_signal_overview_by_trader` (latest snapshot only), `smartmoney_get_traders_by_filter` / `smartmoney_search_trader` (discover authorIds). Note: `asOfTime` is 10-digit `yyyyMMddHH` UTC, different from leaderboard tools' 12-digit UTC+8 `updateTime` \u2014 do not cross-pass.",
8015
+ isWrite: false,
8016
+ outputSchema: envelope({
8017
+ type: "array",
8018
+ description: "Time-bucket series for the requested instrument, sorted by time DESC (newest first).",
8019
+ items: { type: "object", properties: SIGNAL_HISTORY_ITEM_PROPS }
8020
+ }),
8021
+ inputSchema: {
8022
+ type: "object",
8023
+ properties: {
8024
+ authorIds: {
8025
+ type: "array",
8026
+ items: { type: "string" },
8027
+ minItems: 1,
8028
+ description: 'Trader IDs to aggregate over, e.g. `["1001", "1002"]`. Required.'
8029
+ },
8030
+ instCcy: {
8031
+ type: "string",
8032
+ description: 'Base currency to scope the time-series, e.g. "BTC". Required.'
8033
+ },
8034
+ asOfTime: {
8035
+ type: "string",
8036
+ description: 'Anchor snapshot time. Pass as a quoted 10-digit string `yyyyMMddHH` UTC, e.g. `"2026050100"` (NOT integer 2026050100). Returns the latest `limit` buckets ending at this anchor. Omit to use the current UTC hour.'
8037
+ },
8038
+ granularity: {
8039
+ type: "string",
8040
+ enum: ["1h", "1d"],
8041
+ default: "1h",
8042
+ description: "Time-bucket size. `1h` = hourly snapshots (intraday/short-term trend), `1d` = daily snapshots (multi-day trend)."
8043
+ },
8044
+ limit: {
8045
+ type: "integer",
8046
+ minimum: 1,
8047
+ maximum: 500,
8048
+ default: 24,
8049
+ description: "Number of buckets to return (newest first), ending at `asOfTime`. Default 24, max 500."
7132
8050
  }
7133
- };
8051
+ },
8052
+ required: ["authorIds", "instCcy"]
8053
+ },
8054
+ handler: async (rawArgs, context) => {
8055
+ const args = asRecord(rawArgs);
8056
+ const authorIds = readArrayAsCsv(args, "authorIds");
8057
+ const instCcy = readString(args, "instCcy");
8058
+ if (!authorIds) {
8059
+ throw actionableError(
8060
+ '"authorIds" is required and must be a non-empty array.',
8061
+ 'Pass IDs from `smartmoney_get_traders_by_filter` as an array (e.g. ["1001", "1002"]).'
8062
+ );
8063
+ }
8064
+ if (!instCcy) {
8065
+ throw actionableError(
8066
+ '"instCcy" is required.',
8067
+ 'Pass a base currency, e.g. "BTC".'
8068
+ );
8069
+ }
8070
+ const response = await context.client.privateGet(
8071
+ PATH_SIGNAL_HISTORY,
8072
+ compactObject({
8073
+ authorIds,
8074
+ instCcy,
8075
+ asOfTime: readString(args, "asOfTime"),
8076
+ granularity: readString(args, "granularity"),
8077
+ limit: readNumber(args, "limit")
8078
+ }),
8079
+ publicRateLimit("smartmoney_get_signal_trend_by_trader", SMARTMONEY_RPS)
8080
+ );
8081
+ return normalizeResponse(response);
7134
8082
  }
7135
8083
  }
7136
8084
  ];
@@ -7189,8 +8137,12 @@ function buildContractTradeTools(cfg) {
7189
8137
  clOrdId: { type: "string", description: "Client order ID (max 32 chars)" },
7190
8138
  tpTriggerPx: { type: "string", description: "TP trigger price" },
7191
8139
  tpOrdPx: { type: "string", description: "TP order price; -1=market" },
8140
+ tpOrdKind: TP_ORD_KIND_SCHEMA,
8141
+ tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
7192
8142
  slTriggerPx: { type: "string", description: "SL trigger price" },
7193
- slOrdPx: { type: "string", description: "SL order price; -1=market" }
8143
+ slOrdPx: { type: "string", description: "SL order price; -1=market" },
8144
+ slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
8145
+ stpMode: STP_MODE_SCHEMA
7194
8146
  },
7195
8147
  required: ["instId", "tdMode", "side", "ordType", "sz"]
7196
8148
  },
@@ -7219,6 +8171,9 @@ function buildContractTradeTools(cfg) {
7219
8171
  px: readString(args, "px"),
7220
8172
  reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
7221
8173
  clOrdId: readString(args, "clOrdId"),
8174
+ stpMode: readString(args, "stpMode"),
8175
+ // Phase 3c CLI power-user flag (issue #182, CLI-only no MCP/skill exposure)
8176
+ pxAmendType: readString(args, "pxAmendType"),
7222
8177
  tag: context.config.sourceTag,
7223
8178
  attachAlgoOrds
7224
8179
  }),
@@ -9218,6 +10173,8 @@ function registerOptionTools() {
9218
10173
  type: "string",
9219
10174
  description: "TP order price; -1=market"
9220
10175
  },
10176
+ tpOrdKind: TP_ORD_KIND_SCHEMA,
10177
+ tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
9221
10178
  slTriggerPx: {
9222
10179
  type: "string",
9223
10180
  description: "SL trigger price"
@@ -9225,7 +10182,9 @@ function registerOptionTools() {
9225
10182
  slOrdPx: {
9226
10183
  type: "string",
9227
10184
  description: "SL order price; -1=market"
9228
- }
10185
+ },
10186
+ slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
10187
+ stpMode: STP_MODE_SCHEMA
9229
10188
  },
9230
10189
  required: ["instId", "tdMode", "side", "ordType", "sz"]
9231
10190
  },
@@ -9253,6 +10212,7 @@ function registerOptionTools() {
9253
10212
  px: readString(args, "px"),
9254
10213
  reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
9255
10214
  clOrdId: readString(args, "clOrdId"),
10215
+ stpMode: readString(args, "stpMode"),
9256
10216
  tag: context.config.sourceTag,
9257
10217
  attachAlgoOrds
9258
10218
  }),
@@ -9621,6 +10581,8 @@ function registerSpotTradeTools() {
9621
10581
  type: "string",
9622
10582
  description: "TP order price, -1=market"
9623
10583
  },
10584
+ tpOrdKind: TP_ORD_KIND_SCHEMA,
10585
+ tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
9624
10586
  slTriggerPx: {
9625
10587
  type: "string",
9626
10588
  description: "SL trigger price"
@@ -9628,13 +10590,16 @@ function registerSpotTradeTools() {
9628
10590
  slOrdPx: {
9629
10591
  type: "string",
9630
10592
  description: "SL order price, -1=market"
9631
- }
10593
+ },
10594
+ slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
10595
+ stpMode: STP_MODE_SCHEMA
9632
10596
  },
9633
10597
  required: ["instId", "tdMode", "side", "ordType", "sz"]
9634
10598
  },
9635
10599
  handler: async (rawArgs, context) => {
9636
10600
  const args = asRecord(rawArgs);
9637
10601
  const attachAlgoOrds = buildAttachAlgoOrds(args);
10602
+ const banAmend = args.banAmend;
9638
10603
  const response = await context.client.privatePost(
9639
10604
  "/api/v5/trade/order",
9640
10605
  compactObject({
@@ -9646,6 +10611,11 @@ function registerSpotTradeTools() {
9646
10611
  tgtCcy: readString(args, "tgtCcy"),
9647
10612
  px: readString(args, "px"),
9648
10613
  clOrdId: readString(args, "clOrdId"),
10614
+ stpMode: readString(args, "stpMode"),
10615
+ // Phase 3c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
10616
+ tradeQuoteCcy: readString(args, "tradeQuoteCcy"),
10617
+ banAmend: typeof banAmend === "boolean" ? String(banAmend) : void 0,
10618
+ pxAmendType: readString(args, "pxAmendType"),
9649
10619
  tag: context.config.sourceTag,
9650
10620
  attachAlgoOrds
9651
10621
  }),
@@ -9810,7 +10780,7 @@ function registerSpotTradeTools() {
9810
10780
  {
9811
10781
  name: "spot_place_algo_order",
9812
10782
  module: "spot",
9813
- description: "Place a spot algo order: TP/SL (conditional/oco) or trailing stop (move_order_stop). [CAUTION] Executes real trades.",
10783
+ description: "Place a spot algo order. [CAUTION] Executes real trades. conditional: single TP/SL. oco: TP+SL pair. move_order_stop: trailing stop. trigger: pending order at triggerPx. chase: follow best bid/ask. iceberg: split large order into child orders. twap: time-weighted split.",
9814
10784
  isWrite: true,
9815
10785
  inputSchema: {
9816
10786
  type: "object",
@@ -9830,8 +10800,8 @@ function registerSpotTradeTools() {
9830
10800
  },
9831
10801
  ordType: {
9832
10802
  type: "string",
9833
- enum: ["conditional", "oco", "move_order_stop"],
9834
- description: "conditional=single TP/SL, oco=TP+SL pair, move_order_stop=trailing stop"
10803
+ enum: ["conditional", "oco", "move_order_stop", "trigger", "chase", "iceberg", "twap"],
10804
+ description: "conditional=single TP/SL, oco=TP+SL pair, move_order_stop=trailing stop, trigger=pending order, chase=follow best bid/ask, iceberg=split order, twap=time-weighted split"
9835
10805
  },
9836
10806
  sz: {
9837
10807
  type: "string",
@@ -9845,6 +10815,8 @@ function registerSpotTradeTools() {
9845
10815
  type: "string",
9846
10816
  description: "TP order price, -1=market (conditional/oco only)"
9847
10817
  },
10818
+ tpOrdKind: TP_ORD_KIND_SCHEMA,
10819
+ tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
9848
10820
  slTriggerPx: {
9849
10821
  type: "string",
9850
10822
  description: "SL trigger price (conditional/oco only)"
@@ -9853,6 +10825,8 @@ function registerSpotTradeTools() {
9853
10825
  type: "string",
9854
10826
  description: "SL order price, -1=market (conditional/oco only)"
9855
10827
  },
10828
+ slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
10829
+ stpMode: STP_MODE_SCHEMA,
9856
10830
  tgtCcy: {
9857
10831
  type: "string",
9858
10832
  enum: ["base_ccy", "quote_ccy"],
@@ -9869,30 +10843,50 @@ function registerSpotTradeTools() {
9869
10843
  activePx: {
9870
10844
  type: "string",
9871
10845
  description: "Activation price, trailing starts when market hits this (move_order_stop only)"
9872
- }
10846
+ },
10847
+ ...TRIGGER_FLAGS_SCHEMA,
10848
+ ...CHASE_FLAGS_SCHEMA,
10849
+ ...ICEBERG_TWAP_FLAGS_SCHEMA
9873
10850
  },
9874
10851
  required: ["instId", "side", "ordType", "sz"]
9875
10852
  },
9876
10853
  handler: async (rawArgs, context) => {
9877
10854
  const args = asRecord(rawArgs);
9878
- const response = await context.client.privatePost(
9879
- "/api/v5/trade/order-algo",
9880
- compactObject({
9881
- instId: requireString(args, "instId"),
9882
- tdMode: readString(args, "tdMode") ?? "cash",
9883
- side: requireString(args, "side"),
9884
- ordType: requireString(args, "ordType"),
9885
- sz: requireString(args, "sz"),
9886
- tgtCcy: readString(args, "tgtCcy"),
9887
- tpTriggerPx: readString(args, "tpTriggerPx"),
9888
- tpOrdPx: readString(args, "tpOrdPx"),
9889
- slTriggerPx: readString(args, "slTriggerPx"),
9890
- slOrdPx: readString(args, "slOrdPx"),
9891
- callbackRatio: readString(args, "callbackRatio"),
9892
- callbackSpread: readString(args, "callbackSpread"),
9893
- activePx: readString(args, "activePx"),
9894
- tag: context.config.sourceTag
9895
- }),
10855
+ const ordType = requireString(args, "ordType");
10856
+ const base = compactObject({
10857
+ instId: requireString(args, "instId"),
10858
+ tdMode: readString(args, "tdMode") ?? "cash",
10859
+ side: requireString(args, "side"),
10860
+ ordType,
10861
+ sz: requireString(args, "sz"),
10862
+ tgtCcy: readString(args, "tgtCcy"),
10863
+ stpMode: readString(args, "stpMode"),
10864
+ // Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
10865
+ pxAmendType: readString(args, "pxAmendType"),
10866
+ tag: context.config.sourceTag
10867
+ });
10868
+ switch (ordType) {
10869
+ case "trigger":
10870
+ Object.assign(base, buildTriggerOrdTypeBody(args));
10871
+ break;
10872
+ case "chase":
10873
+ Object.assign(base, buildChaseOrdTypeBody(args));
10874
+ break;
10875
+ case "iceberg":
10876
+ case "twap":
10877
+ Object.assign(base, buildIcebergTwapOrdTypeBody(args));
10878
+ break;
10879
+ default:
10880
+ Object.assign(base, compactObject({
10881
+ ...buildAlgoConditionalCommonFields(args),
10882
+ callbackRatio: readString(args, "callbackRatio"),
10883
+ callbackSpread: readString(args, "callbackSpread")
10884
+ }));
10885
+ break;
10886
+ }
10887
+ const response = await context.client.privatePost(
10888
+ "/api/v5/trade/order-algo",
10889
+ base,
9896
10890
  privateRateLimit("spot_place_algo_order", 20)
9897
10891
  );
9898
10892
  return normalizeResponse(response);
@@ -10941,7 +11935,7 @@ var DOWNLOAD_TIMEOUT_MS = 3e4;
10941
11935
  var PLATFORM_MAP = {
10942
11936
  "darwin-arm64": "darwin-arm64",
10943
11937
  "darwin-x64": "darwin-x64",
10944
- "linux-arm64": "linux-x64",
11938
+ "linux-arm64": "linux-arm64",
10945
11939
  "linux-x64": "linux-x64",
10946
11940
  "win32-arm64": "win32-arm64",
10947
11941
  "win32-x64": "win32-x64"
@@ -12374,7 +13368,7 @@ async function cmdDiagnoseMcp(options = {}) {
12374
13368
 
12375
13369
  // src/commands/diagnose.ts
12376
13370
  var CLI_VERSION = readCliVersion();
12377
- var GIT_HASH = true ? "7705482" : "dev";
13371
+ var GIT_HASH = true ? "d0f8ad6" : "dev";
12378
13372
  function maskKey2(key) {
12379
13373
  if (!key) return "(not set)";
12380
13374
  if (key.length <= 8) return "****";
@@ -13066,7 +14060,7 @@ var CLI_REGISTRY = {
13066
14060
  },
13067
14061
  place: {
13068
14062
  toolName: "spot_place_order",
13069
- usage: "okx spot place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--px <price>] [--tdMode <cash|cross|isolated>]\n [--tgtCcy <base_ccy|quote_ccy>] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
14063
+ usage: 'okx spot place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--px <price>] [--tdMode <cash|cross|isolated>]\n [--tgtCcy <base_ccy|quote_ccy>] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpOrdKind <condition|limit>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--stpMode <cancel_maker|cancel_taker|cancel_both>]\n power-user (CLI-only): [--tradeQuoteCcy <USDT|USDC|BTC>] [--banAmend] [--pxAmendType <0|1>]\n power-user (CLI-only): [--tpLevel "px:78000,sz:0.5,kind:limit"] [--tpLevel "px:81000,sz:0.5"] (repeatable; split multi-tier take-profit; mutually exclusive with --tpTriggerPx/--tpOrdPx)',
13070
14064
  description: "Place a new spot order (supports attached TP/SL)"
13071
14065
  },
13072
14066
  amend: {
@@ -13102,8 +14096,8 @@ var CLI_REGISTRY = {
13102
14096
  },
13103
14097
  place: {
13104
14098
  toolName: "spot_place_algo_order",
13105
- usage: "okx spot algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--tdMode <cash|cross|isolated>]",
13106
- description: "Place a spot algo order (take-profit/stop-loss)"
14099
+ usage: 'okx spot algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco|move_order_stop|trigger|chase|iceberg|twap>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpOrdKind <condition|limit>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--stpMode <cancel_maker|cancel_taker|cancel_both>] [--tdMode <cash|cross|isolated>]\n trigger: [--triggerPx <price>] [--orderPx <price|-1>] [--advanceOrdType <fok|ioc>] [--triggerPxType <last|index|mark>]\n chase: [--chaseType <distance|ratio>] [--chaseVal <n>] [--maxChaseType <distance|ratio>] [--maxChaseVal <n>]\n iceberg/twap: [--pxVar <n>|--pxSpread <n>] [--szLimit <n>] [--pxLimit <price>] [--timeInterval <secs>]\n power-user (CLI-only): [--tpTriggerRatio <ratio>] [--slTriggerRatio <ratio>] [--closeFraction <frac>] [--pxAmendType <0|1>]\n power-user (CLI-only): [--tpLevel "px:78000,sz:0.5,kind:limit"] [--tpLevel "px:81000,sz:0.5"] (repeatable; split multi-tier take-profit)',
14100
+ description: "Place a spot algo order (TP/SL, pending order, chase, iceberg, twap)"
13107
14101
  },
13108
14102
  trail: {
13109
14103
  toolName: "spot_place_algo_order",
@@ -13150,7 +14144,7 @@ var CLI_REGISTRY = {
13150
14144
  },
13151
14145
  place: {
13152
14146
  toolName: "swap_place_order",
13153
- usage: "okx swap place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--posSide <side>] [--px <price>]\n [--tdMode <cross|isolated>] [--tgtCcy <base_ccy|quote_ccy|margin>] [--reduceOnly] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
14147
+ usage: 'okx swap place --instId <id> --side <buy|sell> --ordType <type> --sz <n> [--posSide <side>] [--px <price>]\n [--tdMode <cross|isolated>] [--tgtCcy <base_ccy|quote_ccy|margin>] [--reduceOnly] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpOrdKind <condition|limit>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--stpMode <cancel_maker|cancel_taker|cancel_both>]\n power-user (CLI-only): [--pxAmendType <0|1>]\n power-user (CLI-only): [--tpLevel "px:78000,sz:0.5,kind:limit"] [--tpLevel "px:81000,sz:0.5"] (repeatable; split multi-tier take-profit; mutually exclusive with --tpTriggerPx/--tpOrdPx)',
13154
14148
  description: "Place a new perpetual swap order (supports attached TP/SL)"
13155
14149
  },
13156
14150
  cancel: {
@@ -13202,8 +14196,8 @@ var CLI_REGISTRY = {
13202
14196
  },
13203
14197
  place: {
13204
14198
  toolName: "swap_place_algo_order",
13205
- usage: "okx swap algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>]\n [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]",
13206
- description: "Place a swap algo order (take-profit/stop-loss)"
14199
+ usage: 'okx swap algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco|move_order_stop|trigger|chase|iceberg|twap>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpOrdKind <condition|limit>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--stpMode <cancel_maker|cancel_taker|cancel_both>] [--cxlOnClosePos]\n [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]\n trigger: [--triggerPx <price>] [--orderPx <price|-1>] [--advanceOrdType <fok|ioc>] [--triggerPxType <last|index|mark>]\n chase: [--chaseType <distance|ratio>] [--chaseVal <n>] [--maxChaseType <distance|ratio>] [--maxChaseVal <n>]\n iceberg/twap: [--pxVar <n>|--pxSpread <n>] [--szLimit <n>] [--pxLimit <price>] [--timeInterval <secs>]\n power-user (CLI-only): [--tpTriggerRatio <ratio>] [--slTriggerRatio <ratio>] [--closeFraction <frac>] [--pxAmendType <0|1>]\n power-user (CLI-only): [--tpLevel "px:78000,sz:0.5,kind:limit"] [--tpLevel "px:81000,sz:0.5"] (repeatable; split multi-tier take-profit)',
14200
+ description: "Place a swap algo order (TP/SL, pending order, chase, iceberg, twap)"
13207
14201
  },
13208
14202
  amend: {
13209
14203
  toolName: "swap_amend_algo_order",
@@ -13240,7 +14234,7 @@ var CLI_REGISTRY = {
13240
14234
  },
13241
14235
  place: {
13242
14236
  toolName: "futures_place_order",
13243
- usage: "okx futures place --instId <id> --side <buy|sell> --ordType <type> --sz <n>\n [--tdMode <cross|isolated>] [--posSide <net|long|short>] [--px <price>] [--reduceOnly]\n [--tgtCcy <base_ccy|quote_ccy|margin>] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
14237
+ usage: 'okx futures place --instId <id> --side <buy|sell> --ordType <type> --sz <n>\n [--tdMode <cross|isolated>] [--posSide <net|long|short>] [--px <price>] [--reduceOnly]\n [--tgtCcy <base_ccy|quote_ccy|margin>] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpOrdKind <condition|limit>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--stpMode <cancel_maker|cancel_taker|cancel_both>]\n power-user (CLI-only): [--pxAmendType <0|1>]\n power-user (CLI-only): [--tpLevel "px:78000,sz:0.5,kind:limit"] [--tpLevel "px:81000,sz:0.5"] (repeatable; split multi-tier take-profit; mutually exclusive with --tpTriggerPx/--tpOrdPx)',
13244
14238
  description: "Place a new futures order (supports attached TP/SL)"
13245
14239
  },
13246
14240
  cancel: {
@@ -13296,7 +14290,7 @@ var CLI_REGISTRY = {
13296
14290
  },
13297
14291
  place: {
13298
14292
  toolName: "futures_place_algo_order",
13299
- usage: "okx futures algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>]\n [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]",
14293
+ usage: 'okx futures algo place --instId <id> --side <buy|sell> --sz <n> [--ordType <conditional|oco|move_order_stop|trigger|chase|iceberg|twap>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpOrdKind <condition|limit>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--stpMode <cancel_maker|cancel_taker|cancel_both>] [--cxlOnClosePos]\n [--posSide <net|long|short>] [--tdMode <cross|isolated>] [--reduceOnly]\n trigger: [--triggerPx <price>] [--orderPx <price|-1>] [--advanceOrdType <fok|ioc>] [--triggerPxType <last|index|mark>]\n chase: [--chaseType <distance|ratio>] [--chaseVal <n>] [--maxChaseType <distance|ratio>] [--maxChaseVal <n>]\n iceberg/twap: [--pxVar <n>|--pxSpread <n>] [--szLimit <n>] [--pxLimit <price>] [--timeInterval <secs>]\n power-user (CLI-only): [--tpTriggerRatio <ratio>] [--slTriggerRatio <ratio>] [--closeFraction <frac>] [--pxAmendType <0|1>]\n power-user (CLI-only): [--tpLevel "px:78000,sz:0.5,kind:limit"] [--tpLevel "px:81000,sz:0.5"] (repeatable; split multi-tier take-profit)',
13300
14294
  description: "Place a futures algo order (take-profit/stop-loss)"
13301
14295
  },
13302
14296
  amend: {
@@ -13349,7 +14343,7 @@ var CLI_REGISTRY = {
13349
14343
  },
13350
14344
  place: {
13351
14345
  toolName: "option_place_order",
13352
- usage: "okx option place --instId <id> --tdMode <cash|cross|isolated> --side <buy|sell> --ordType <type> --sz <n>\n [--px <price>] [--tgtCcy <base_ccy|quote_ccy|margin>] [--reduceOnly] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--slTriggerPx <price>] [--slOrdPx <price|-1>]",
14346
+ usage: "okx option place --instId <id> --tdMode <cash|cross|isolated> --side <buy|sell> --ordType <type> --sz <n>\n [--px <price>] [--tgtCcy <base_ccy|quote_ccy|margin>] [--reduceOnly] [--clOrdId <id>]\n [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpOrdKind <condition|limit>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--stpMode <cancel_maker|cancel_taker|cancel_both>]",
13353
14347
  description: "Place a new option order"
13354
14348
  },
13355
14349
  cancel: {
@@ -13379,7 +14373,7 @@ var CLI_REGISTRY = {
13379
14373
  },
13380
14374
  place: {
13381
14375
  toolName: "option_place_algo_order",
13382
- usage: "okx option algo place --instId <id> --tdMode <cash|cross|isolated> --side <buy|sell> --sz <n>\n [--ordType <conditional|oco>] [--tpTriggerPx <price>] [--tpOrdPx <price|-1>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--reduceOnly] [--clOrdId <id>]",
14376
+ usage: "okx option algo place --instId <id> --tdMode <cash|cross|isolated> --side <buy|sell> --sz <n>\n [--ordType <conditional|oco>] [--tpTriggerPx <price>] [--tpOrdPx <price|-1>] [--tpTriggerPxType <last|index|mark>]\n [--slTriggerPx <price>] [--slOrdPx <price|-1>] [--slTriggerPxType <last|index|mark>]\n [--reduceOnly] [--clOrdId <id>]",
13383
14377
  description: "Place an option algo order (take-profit/stop-loss)"
13384
14378
  },
13385
14379
  amend: {
@@ -13678,32 +14672,57 @@ var CLI_REGISTRY = {
13678
14672
  },
13679
14673
  // ── smartmoney ─────────────────────────────────────────────────────────────
13680
14674
  smartmoney: {
13681
- description: "Smart money signals \u2014 trader leaderboard, consensus signals, and position analysis",
14675
+ description: "Smart money analytics \u2014 trader leaderboard, consensus signals, and position analysis",
13682
14676
  commands: {
13683
- overview: {
13684
- toolName: "smartmoney_get_overview",
13685
- usage: "okx smartmoney overview [--ts <ms> | --dataVersion <ver>] [--instType <SWAP|SPOT>] [--sortType <pnl|pnlRatio>] [--period <3|7|30|90>] [--pnl <tier>] [--winRatio <tier>] [--maxRetreat <tier>] [--asset <tier>] [--lmtNum <n>] [--instCcyList <ccys>] [--instCcy <ccy>] [--topInstruments <n>] [--json]",
13686
- description: "Multi-currency smart money overview ranked by tradersWithPosition DESC (requires --ts or --dataVersion; --ts takes precedence)"
13687
- },
13688
- signal: {
13689
- toolName: "smartmoney_get_signal",
13690
- usage: "okx smartmoney signal [--instId <id>] [--instCcy <ccy>] [--ts <ms> | --dataVersion <ver>] [--sortType <pnl|pnlRatio>] [--period <3|7|30|90>] [--pnl <tier>] [--winRatio <tier>] [--maxRetreat <tier>] [--asset <tier>] [--lmtNum <n>] [--authorIds <ids>] [--json]",
13691
- description: "Single-currency aggregated consensus signal (requires --instId or --instCcy, and --ts or --dataVersion; --instId / --ts take precedence)"
13692
- },
13693
- "signal-history": {
13694
- toolName: "smartmoney_get_signal_history",
13695
- usage: "okx smartmoney signal-history --instId <id> [--ts <ms> | --dataVersion <ver>] [--granularity <1h|1d>] [--limit <n>] [--sortType <pnl|pnlRatio>] [--period <3|7|30|90>] [--pnl <tier>] [--winRatio <tier>] [--maxRetreat <tier>] [--asset <tier>] [--json]",
13696
- description: "Signal history timeline sorted by ts DESC (requires --instId and --ts/--dataVersion)"
13697
- },
13698
- traders: {
13699
- toolName: "smartmoney_get_traders",
13700
- usage: 'okx smartmoney traders [--dataVersion <ts>] [--sortType <pnl|pnl_ratio>] [--period <""|3|7|30|90>] [--pnl <n>] [--winRatio <r>] [--maxRetreat <r>] [--asset <n>] [--authorIds <ids>] [--limit <n>] [--after <id>] [--before <id>] [--json]',
13701
- description: "List/filter traders from the smart money leaderboard"
13702
- },
13703
- trader: {
13704
- toolName: "smartmoney_get_trader_detail",
13705
- usage: "okx smartmoney trader --authorId <id> [--period <3|7|30|90>] [--instCcy <ccy>] [--tradeLimit <n>] [--json]",
13706
- description: "Trader full portrait (profile + positions + trades)"
14677
+ "traders-by-filter": {
14678
+ toolName: "smartmoney_get_traders_by_filter",
14679
+ usage: "okx smartmoney traders-by-filter [--updateTime <ts>] [--sortBy <pnl|pnlRatio>] [--period <3|7|30|90>] [--minPnl <n>] [--minWinRate <r>] [--maxDrawdown <r>] [--minAum <n>] [--after <id>] [--before <id>] [--limit <n>] [--json]",
14680
+ description: "Leaderboard of top smart-money traders, ranked and filtered by pool conditions"
14681
+ },
14682
+ "performance-by-trader": {
14683
+ toolName: "smartmoney_get_performance_by_trader",
14684
+ usage: "okx smartmoney performance-by-trader --authorIds <id1,id2> [--period <3|7|30|90>] [--json]",
14685
+ description: "PnL / win-rate / drawdown profile for one or more traders by authorIds"
14686
+ },
14687
+ "trader-positions": {
14688
+ toolName: "smartmoney_get_trader_positions",
14689
+ usage: "okx smartmoney trader-positions --authorId <id> [--instId <id>] [--json]",
14690
+ description: "Currently-open positions held by a single trader"
14691
+ },
14692
+ "trader-positions-history": {
14693
+ toolName: "smartmoney_get_trader_positions_history",
14694
+ usage: "okx smartmoney trader-positions-history --authorId <id> [--instId <id>] [--after <posId>] [--before <posId>] [--limit <n>] [--json]",
14695
+ description: "Closed-position history of a single trader (paginated)"
14696
+ },
14697
+ "trader-orders-history": {
14698
+ toolName: "smartmoney_get_trader_orders_history",
14699
+ usage: "okx smartmoney trader-orders-history --authorId <id> [--instId <id>] [--after <ordId>] [--before <ordId>] [--limit <n>] [--json]",
14700
+ description: "Recent orders/fills placed by a single trader (paginated)"
14701
+ },
14702
+ "search-trader": {
14703
+ toolName: "smartmoney_search_trader",
14704
+ usage: "okx smartmoney search-trader --keyword <name> [--json]",
14705
+ description: "Search Top Traders by nickname keyword (\u226410 results, ranked by follower count)"
14706
+ },
14707
+ "signal-overview-by-filter": {
14708
+ toolName: "smartmoney_get_signal_overview_by_filter",
14709
+ usage: "okx smartmoney signal-overview-by-filter [--topInstruments <n> | --instCcyList <BTC,ETH,...>] [--sortBy <pnl|pnlRatio>] [--period <3|7|30|90>] [--pnlTier <tier>] [--winRateTier <tier>] [--maxDrawdownTier <tier>] [--aumTier <tier>] [--lmtNum <n>] [--json]",
14710
+ description: "Multi-asset smart-money consensus signal aggregated over a tier-filtered pool"
14711
+ },
14712
+ "signal-overview-by-trader": {
14713
+ toolName: "smartmoney_get_signal_overview_by_trader",
14714
+ usage: "okx smartmoney signal-overview-by-trader --authorIds <id1,id2> [--topInstruments <n> | --instCcyList <BTC,ETH,...>] [--json]",
14715
+ description: "Multi-asset smart-money signal aggregated over a hand-picked set of traders (authorIds-direct-lookup; pool filters not exposed)"
14716
+ },
14717
+ "signal-trend-by-filter": {
14718
+ toolName: "smartmoney_get_signal_trend_by_filter",
14719
+ usage: "okx smartmoney signal-trend-by-filter --instCcy <ccy> [--asOfTime <yyyyMMddHH>] [--granularity <1h|1d>] [--limit <n>] [--sortBy <pnl|pnlRatio>] [--period <3|7|30|90>] [--pnlTier <tier>] [--winRateTier <tier>] [--maxDrawdownTier <tier>] [--aumTier <tier>] [--lmtNum <n>] [--json]",
14720
+ description: "Single-coin smart-money signal time-series aggregated over a tier-filtered pool, anchored at asOfTime"
14721
+ },
14722
+ "signal-trend-by-trader": {
14723
+ toolName: "smartmoney_get_signal_trend_by_trader",
14724
+ usage: "okx smartmoney signal-trend-by-trader --authorIds <id1,id2> --instCcy <ccy> [--asOfTime <yyyyMMddHH>] [--granularity <1h|1d>] [--limit <n>] [--json]",
14725
+ description: "Single-coin smart-money signal time-series aggregated over a hand-picked set of traders (authorIds-direct-lookup; pool filters not exposed)"
13707
14726
  }
13708
14727
  }
13709
14728
  },
@@ -14407,6 +15426,33 @@ var CLI_OPTIONS = {
14407
15426
  tpOrdPx: { type: "string" },
14408
15427
  slTriggerPx: { type: "string" },
14409
15428
  slOrdPx: { type: "string" },
15429
+ // Phase 1 algo order flags (issue #178)
15430
+ tpOrdKind: { type: "string" },
15431
+ tpTriggerPxType: { type: "string" },
15432
+ slTriggerPxType: { type: "string" },
15433
+ stpMode: { type: "string" },
15434
+ cxlOnClosePos: { type: "boolean", default: false },
15435
+ // Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
15436
+ tpTriggerRatio: { type: "string" },
15437
+ slTriggerRatio: { type: "string" },
15438
+ closeFraction: { type: "string" },
15439
+ banAmend: { type: "boolean", default: false },
15440
+ pxAmendType: { type: "string" },
15441
+ // Phase 3b CLI power-user flag (issue #183, CLI-only no MCP/skill exposure)
15442
+ tpLevel: { type: "string", multiple: true },
15443
+ // Phase 2 algo ordType flags (issue #181)
15444
+ orderPx: { type: "string" },
15445
+ advanceOrdType: { type: "string" },
15446
+ triggerPxType: { type: "string" },
15447
+ chaseType: { type: "string" },
15448
+ chaseVal: { type: "string" },
15449
+ maxChaseType: { type: "string" },
15450
+ maxChaseVal: { type: "string" },
15451
+ pxVar: { type: "string" },
15452
+ pxSpread: { type: "string" },
15453
+ szLimit: { type: "string" },
15454
+ pxLimit: { type: "string" },
15455
+ timeInterval: { type: "string" },
14410
15456
  algoId: { type: "string" },
14411
15457
  reduceOnly: { type: "boolean", default: false },
14412
15458
  // algo amend
@@ -14494,19 +15540,23 @@ var CLI_OPTIONS = {
14494
15540
  // smartmoney
14495
15541
  authorId: { type: "string" },
14496
15542
  authorIds: { type: "string" },
14497
- dataVersion: { type: "string" },
14498
- sortType: { type: "string" },
15543
+ updateTime: { type: "string" },
14499
15544
  granularity: { type: "string" },
14500
15545
  lmtNum: { type: "string" },
14501
15546
  instCcy: { type: "string" },
14502
15547
  instCcyList: { type: "string" },
14503
15548
  topInstruments: { type: "string" },
14504
- tradeLimit: { type: "string" },
14505
- // smartmoney pool filters
14506
- pnl: { type: "string" },
14507
- winRatio: { type: "string" },
14508
- maxRetreat: { type: "string" },
14509
- asset: { type: "string" },
15549
+ asOfTime: { type: "string" },
15550
+ // smartmoney pool filters — leaderboard (numeric thresholds)
15551
+ minPnl: { type: "string" },
15552
+ minWinRate: { type: "string" },
15553
+ maxDrawdown: { type: "string" },
15554
+ minAum: { type: "string" },
15555
+ // smartmoney pool filters — signal endpoints (enum tiers)
15556
+ pnlTier: { type: "string" },
15557
+ winRateTier: { type: "string" },
15558
+ maxDrawdownTier: { type: "string" },
15559
+ aumTier: { type: "string" },
14510
15560
  // upgrade
14511
15561
  beta: { type: "boolean", default: false },
14512
15562
  check: { type: "boolean", default: false },
@@ -14593,6 +15643,42 @@ var CLI_OPTIONS = {
14593
15643
  output: { type: "string" }
14594
15644
  // diagnose --output only: save report to file
14595
15645
  };
15646
+ var TP_LEVEL_KEY_MAP = {
15647
+ px: "tpOrdPx",
15648
+ sz: "sz",
15649
+ kind: "tpOrdKind",
15650
+ triggerPx: "tpTriggerPx",
15651
+ triggerPxType: "tpTriggerPxType",
15652
+ amendPxOnTrigger: "amendPxOnTriggerType",
15653
+ clOrdId: "attachAlgoClOrdId"
15654
+ };
15655
+ function parseTpLevel(s) {
15656
+ if (!s || !s.includes(":")) {
15657
+ throw new Error(
15658
+ `Invalid --tpLevel format: "${s}". Expected "key:value,key:value,..." (e.g. "px:78000,sz:0.5,kind:limit")`
15659
+ );
15660
+ }
15661
+ const result = {};
15662
+ const pairs = s.split(",");
15663
+ for (const pair of pairs) {
15664
+ const colonIdx = pair.indexOf(":");
15665
+ if (colonIdx < 1) {
15666
+ throw new Error(
15667
+ `Invalid --tpLevel format: "${pair}". Each entry must be "key:value", not "${pair}". Valid keys: ${Object.keys(TP_LEVEL_KEY_MAP).join(", ")}`
15668
+ );
15669
+ }
15670
+ const key = pair.slice(0, colonIdx).trim();
15671
+ const value = pair.slice(colonIdx + 1).trim();
15672
+ const mapped = TP_LEVEL_KEY_MAP[key];
15673
+ if (!mapped) {
15674
+ throw new Error(
15675
+ `Unknown --tpLevel key: "${key}". Valid keys: ${Object.keys(TP_LEVEL_KEY_MAP).join(", ")}`
15676
+ );
15677
+ }
15678
+ result[mapped] = value;
15679
+ }
15680
+ return result;
15681
+ }
14596
15682
  function parseCli(argv) {
14597
15683
  const negated = /* @__PURE__ */ new Set();
14598
15684
  const filtered = argv.filter((arg) => {
@@ -15338,8 +16424,16 @@ async function cmdSpotPlace(run, opts) {
15338
16424
  clOrdId: opts.clOrdId,
15339
16425
  tpTriggerPx: opts.tpTriggerPx,
15340
16426
  tpOrdPx: opts.tpOrdPx,
16427
+ tpOrdKind: opts.tpOrdKind,
16428
+ tpTriggerPxType: opts.tpTriggerPxType,
15341
16429
  slTriggerPx: opts.slTriggerPx,
15342
- slOrdPx: opts.slOrdPx
16430
+ slOrdPx: opts.slOrdPx,
16431
+ slTriggerPxType: opts.slTriggerPxType,
16432
+ stpMode: opts.stpMode,
16433
+ tradeQuoteCcy: opts.tradeQuoteCcy,
16434
+ banAmend: opts.banAmend,
16435
+ pxAmendType: opts.pxAmendType,
16436
+ tpLevels: opts.tpLevels
15343
16437
  });
15344
16438
  const data = getData4(result);
15345
16439
  if (opts.json) return printJson(data);
@@ -15364,11 +16458,33 @@ async function cmdSpotAlgoPlace(run, opts) {
15364
16458
  tgtCcy: opts.tgtCcy,
15365
16459
  tpTriggerPx: opts.tpTriggerPx,
15366
16460
  tpOrdPx: opts.tpOrdPx,
16461
+ tpOrdKind: opts.tpOrdKind,
16462
+ tpTriggerPxType: opts.tpTriggerPxType,
15367
16463
  slTriggerPx: opts.slTriggerPx,
15368
16464
  slOrdPx: opts.slOrdPx,
16465
+ slTriggerPxType: opts.slTriggerPxType,
16466
+ stpMode: opts.stpMode,
15369
16467
  callbackRatio: opts.callbackRatio,
15370
16468
  callbackSpread: opts.callbackSpread,
15371
- activePx: opts.activePx
16469
+ activePx: opts.activePx,
16470
+ triggerPx: opts.triggerPx,
16471
+ orderPx: opts.orderPx,
16472
+ advanceOrdType: opts.advanceOrdType,
16473
+ triggerPxType: opts.triggerPxType,
16474
+ chaseType: opts.chaseType,
16475
+ chaseVal: opts.chaseVal,
16476
+ maxChaseType: opts.maxChaseType,
16477
+ maxChaseVal: opts.maxChaseVal,
16478
+ pxVar: opts.pxVar,
16479
+ pxSpread: opts.pxSpread,
16480
+ szLimit: opts.szLimit,
16481
+ pxLimit: opts.pxLimit,
16482
+ timeInterval: opts.timeInterval,
16483
+ tpTriggerRatio: opts.tpTriggerRatio,
16484
+ slTriggerRatio: opts.slTriggerRatio,
16485
+ closeFraction: opts.closeFraction,
16486
+ pxAmendType: opts.pxAmendType,
16487
+ tpLevels: opts.tpLevels
15372
16488
  });
15373
16489
  const data = getData4(result);
15374
16490
  if (opts.json) return printJson(data);
@@ -15602,8 +16718,14 @@ async function cmdSwapPlace(run, opts) {
15602
16718
  clOrdId: opts.clOrdId,
15603
16719
  tpTriggerPx: opts.tpTriggerPx,
15604
16720
  tpOrdPx: opts.tpOrdPx,
16721
+ tpOrdKind: opts.tpOrdKind,
16722
+ tpTriggerPxType: opts.tpTriggerPxType,
15605
16723
  slTriggerPx: opts.slTriggerPx,
15606
- slOrdPx: opts.slOrdPx
16724
+ slOrdPx: opts.slOrdPx,
16725
+ slTriggerPxType: opts.slTriggerPxType,
16726
+ stpMode: opts.stpMode,
16727
+ pxAmendType: opts.pxAmendType,
16728
+ tpLevels: opts.tpLevels
15607
16729
  });
15608
16730
  const data = getData5(result);
15609
16731
  if (opts.json) return printJson(data);
@@ -15629,12 +16751,35 @@ async function cmdSwapAlgoPlace(run, opts) {
15629
16751
  posSide: opts.posSide,
15630
16752
  tpTriggerPx: opts.tpTriggerPx,
15631
16753
  tpOrdPx: opts.tpOrdPx,
16754
+ tpOrdKind: opts.tpOrdKind,
16755
+ tpTriggerPxType: opts.tpTriggerPxType,
15632
16756
  slTriggerPx: opts.slTriggerPx,
15633
16757
  slOrdPx: opts.slOrdPx,
16758
+ slTriggerPxType: opts.slTriggerPxType,
16759
+ stpMode: opts.stpMode,
16760
+ cxlOnClosePos: opts.cxlOnClosePos,
15634
16761
  reduceOnly: opts.reduceOnly,
15635
16762
  callbackRatio: opts.callbackRatio,
15636
16763
  callbackSpread: opts.callbackSpread,
15637
- activePx: opts.activePx
16764
+ activePx: opts.activePx,
16765
+ triggerPx: opts.triggerPx,
16766
+ orderPx: opts.orderPx,
16767
+ advanceOrdType: opts.advanceOrdType,
16768
+ triggerPxType: opts.triggerPxType,
16769
+ chaseType: opts.chaseType,
16770
+ chaseVal: opts.chaseVal,
16771
+ maxChaseType: opts.maxChaseType,
16772
+ maxChaseVal: opts.maxChaseVal,
16773
+ pxVar: opts.pxVar,
16774
+ pxSpread: opts.pxSpread,
16775
+ szLimit: opts.szLimit,
16776
+ pxLimit: opts.pxLimit,
16777
+ timeInterval: opts.timeInterval,
16778
+ tpTriggerRatio: opts.tpTriggerRatio,
16779
+ slTriggerRatio: opts.slTriggerRatio,
16780
+ closeFraction: opts.closeFraction,
16781
+ pxAmendType: opts.pxAmendType,
16782
+ tpLevels: opts.tpLevels
15638
16783
  });
15639
16784
  const data = getData5(result);
15640
16785
  if (opts.json) return printJson(data);
@@ -15908,8 +17053,14 @@ async function cmdFuturesPlace(run, opts) {
15908
17053
  clOrdId: opts.clOrdId,
15909
17054
  tpTriggerPx: opts.tpTriggerPx,
15910
17055
  tpOrdPx: opts.tpOrdPx,
17056
+ tpOrdKind: opts.tpOrdKind,
17057
+ tpTriggerPxType: opts.tpTriggerPxType,
15911
17058
  slTriggerPx: opts.slTriggerPx,
15912
- slOrdPx: opts.slOrdPx
17059
+ slOrdPx: opts.slOrdPx,
17060
+ slTriggerPxType: opts.slTriggerPxType,
17061
+ stpMode: opts.stpMode,
17062
+ pxAmendType: opts.pxAmendType,
17063
+ tpLevels: opts.tpLevels
15913
17064
  });
15914
17065
  const data = getData6(result);
15915
17066
  if (opts.json) return printJson(data);
@@ -16037,12 +17188,35 @@ async function cmdFuturesAlgoPlace(run, opts) {
16037
17188
  posSide: opts.posSide,
16038
17189
  tpTriggerPx: opts.tpTriggerPx,
16039
17190
  tpOrdPx: opts.tpOrdPx,
17191
+ tpOrdKind: opts.tpOrdKind,
17192
+ tpTriggerPxType: opts.tpTriggerPxType,
16040
17193
  slTriggerPx: opts.slTriggerPx,
16041
17194
  slOrdPx: opts.slOrdPx,
17195
+ slTriggerPxType: opts.slTriggerPxType,
17196
+ stpMode: opts.stpMode,
17197
+ cxlOnClosePos: opts.cxlOnClosePos,
16042
17198
  reduceOnly: opts.reduceOnly,
16043
17199
  callbackRatio: opts.callbackRatio,
16044
17200
  callbackSpread: opts.callbackSpread,
16045
- activePx: opts.activePx
17201
+ activePx: opts.activePx,
17202
+ triggerPx: opts.triggerPx,
17203
+ orderPx: opts.orderPx,
17204
+ advanceOrdType: opts.advanceOrdType,
17205
+ triggerPxType: opts.triggerPxType,
17206
+ chaseType: opts.chaseType,
17207
+ chaseVal: opts.chaseVal,
17208
+ maxChaseType: opts.maxChaseType,
17209
+ maxChaseVal: opts.maxChaseVal,
17210
+ pxVar: opts.pxVar,
17211
+ pxSpread: opts.pxSpread,
17212
+ szLimit: opts.szLimit,
17213
+ pxLimit: opts.pxLimit,
17214
+ timeInterval: opts.timeInterval,
17215
+ tpTriggerRatio: opts.tpTriggerRatio,
17216
+ slTriggerRatio: opts.slTriggerRatio,
17217
+ closeFraction: opts.closeFraction,
17218
+ pxAmendType: opts.pxAmendType,
17219
+ tpLevels: opts.tpLevels
16046
17220
  });
16047
17221
  const data = getData6(result);
16048
17222
  if (opts.json) return printJson(data);
@@ -16274,8 +17448,12 @@ async function cmdOptionPlace(run, opts) {
16274
17448
  clOrdId: opts.clOrdId,
16275
17449
  tpTriggerPx: opts.tpTriggerPx,
16276
17450
  tpOrdPx: opts.tpOrdPx,
17451
+ tpOrdKind: opts.tpOrdKind,
17452
+ tpTriggerPxType: opts.tpTriggerPxType,
16277
17453
  slTriggerPx: opts.slTriggerPx,
16278
- slOrdPx: opts.slOrdPx
17454
+ slOrdPx: opts.slOrdPx,
17455
+ slTriggerPxType: opts.slTriggerPxType,
17456
+ stpMode: opts.stpMode
16279
17457
  });
16280
17458
  const data = getData7(result);
16281
17459
  if (opts.json) return printJson(data);
@@ -16894,175 +18072,267 @@ function extractFixedOffers(result) {
16894
18072
  }
16895
18073
 
16896
18074
  // src/commands/smartmoney.ts
16897
- function printDataList2(data, json, emptyMsg, mapper) {
16898
- if (json) {
16899
- printJson(data);
16900
- return;
16901
- }
18075
+ function printDataTable(data, emptyMsg, mapper) {
16902
18076
  if (!data.length) {
16903
18077
  outputLine(emptyMsg);
16904
18078
  return;
16905
18079
  }
16906
18080
  printTable(data.map(mapper));
16907
18081
  }
16908
- function poolFilterArgs(o) {
18082
+ function csvToArray(value) {
18083
+ if (!value) return void 0;
18084
+ const arr = value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
18085
+ return arr.length > 0 ? arr : void 0;
18086
+ }
18087
+ function printPaginationHint(result) {
18088
+ if (!result || typeof result !== "object") return;
18089
+ const pagination = result.pagination;
18090
+ if (!pagination || typeof pagination !== "object") return;
18091
+ const { hasMore, nextAfter } = pagination;
18092
+ if (hasMore !== true) return;
18093
+ const cursor = typeof nextAfter === "string" || typeof nextAfter === "number" ? String(nextAfter) : "";
18094
+ errorLine(cursor ? `more results \u2014 pass --after ${cursor} for next page` : "more results \u2014 pass --after <cursor> for next page");
18095
+ }
18096
+ function signalPoolFilterArgs(o) {
16909
18097
  const result = {};
16910
- if (o.sortType) result.sortType = o.sortType;
18098
+ if (o.sortBy) result.sortBy = o.sortBy;
16911
18099
  if (o.period) result.period = o.period;
16912
- if (o.pnl) result.pnl = o.pnl;
16913
- if (o.winRatio) result.winRatio = o.winRatio;
16914
- if (o.maxRetreat) result.maxRetreat = o.maxRetreat;
16915
- if (o.asset) result.asset = o.asset;
18100
+ if (o.pnlTier) result.pnlTier = o.pnlTier;
18101
+ if (o.winRateTier) result.winRateTier = o.winRateTier;
18102
+ if (o.maxDrawdownTier) result.maxDrawdownTier = o.maxDrawdownTier;
18103
+ if (o.aumTier) result.aumTier = o.aumTier;
16916
18104
  return result;
16917
18105
  }
16918
- async function cmdSmartmoneyOverview(run, opts) {
16919
- const result = await run("smartmoney_get_overview", {
16920
- dataVersion: opts.dataVersion,
16921
- ts: opts.ts,
16922
- instType: opts.instType,
16923
- ...poolFilterArgs(opts),
16924
- lmtNum: opts.lmtNum,
16925
- instCcyList: opts.instCcyList,
16926
- instCcy: opts.instCcy,
16927
- topInstruments: opts.topInstruments
18106
+ function leaderboardPoolFilterArgs(o) {
18107
+ const result = {};
18108
+ if (o.sortBy) result.sortBy = o.sortBy;
18109
+ if (o.period) result.period = o.period;
18110
+ if (o.minPnl) result.minPnl = o.minPnl;
18111
+ if (o.minWinRate) result.minWinRate = o.minWinRate;
18112
+ if (o.maxDrawdown) result.maxDrawdown = o.maxDrawdown;
18113
+ if (o.minAum) result.minAum = o.minAum;
18114
+ return result;
18115
+ }
18116
+ async function cmdSmartmoneyTradersByFilter(run, opts) {
18117
+ const result = await run("smartmoney_get_traders_by_filter", {
18118
+ updateTime: opts.updateTime,
18119
+ ...leaderboardPoolFilterArgs(opts),
18120
+ after: opts.after,
18121
+ before: opts.before,
18122
+ limit: opts.limit
16928
18123
  });
18124
+ if (opts.json) {
18125
+ printJson(result);
18126
+ return;
18127
+ }
16929
18128
  const data = extractData(result);
16930
- printDataList2(data, opts.json, "No overview data", (r) => ({
16931
- instId: r["instId"],
16932
- tradersWithPosition: r["tradersWithPosition"],
16933
- longRatio: r["longRatio"],
16934
- weightedLongRatio: r["weightedLongRatio"],
16935
- netNotionalUsdt: r["netNotionalUsdt"],
16936
- vs24h: r["vs24h"]
18129
+ printDataTable(data, "No traders found", (r) => ({
18130
+ authorId: r["authorId"],
18131
+ nickName: r["nickName"],
18132
+ pnl: r["pnl"],
18133
+ pnlRatio: r["pnlRatio"],
18134
+ winRate: r["winRate"],
18135
+ maxDrawdown: r["maxDrawdown"],
18136
+ asset: r["asset"]
16937
18137
  }));
18138
+ printPaginationHint(result);
16938
18139
  }
16939
- async function cmdSmartmoneySignal(run, opts) {
16940
- const result = await run("smartmoney_get_signal", {
16941
- instId: opts.instId,
16942
- instCcy: opts.instCcy,
16943
- dataVersion: opts.dataVersion,
16944
- ts: opts.ts,
16945
- ...poolFilterArgs(opts),
16946
- lmtNum: opts.lmtNum,
16947
- authorIds: opts.authorIds
18140
+ async function cmdSmartmoneyPerformanceByTrader(run, opts) {
18141
+ const result = await run("smartmoney_get_performance_by_trader", {
18142
+ authorIds: csvToArray(opts.authorIds),
18143
+ period: opts.period
16948
18144
  });
16949
- const data = extractData(result);
16950
- const signal = data[0];
16951
18145
  if (opts.json) {
16952
- printJson(signal ?? {});
18146
+ printJson(result);
16953
18147
  return;
16954
18148
  }
16955
- if (!signal) {
16956
- outputLine("No signal data");
18149
+ const data = extractData(result);
18150
+ printDataTable(data, "No traders found", (r) => ({
18151
+ authorId: r["authorId"],
18152
+ nickName: r["nickName"],
18153
+ pnl: r["pnl"],
18154
+ pnlRatio: r["pnlRatio"],
18155
+ winRate: r["winRate"],
18156
+ maxDrawdown: r["maxDrawdown"],
18157
+ asset: r["asset"]
18158
+ }));
18159
+ }
18160
+ async function cmdSmartmoneyTraderPositions(run, opts) {
18161
+ const result = await run("smartmoney_get_trader_positions", {
18162
+ authorId: opts.authorId,
18163
+ instId: opts.instId
18164
+ });
18165
+ if (opts.json) {
18166
+ printJson(result);
16957
18167
  return;
16958
18168
  }
16959
- printKv({
16960
- instId: signal["instId"],
16961
- instType: signal["instType"],
16962
- tradersWithPosition: signal["tradersWithPosition"],
16963
- tradersTotal: signal["tradersTotal"],
16964
- longRatio: signal["longRatio"],
16965
- weightedLongRatio: signal["weightedLongRatio"],
16966
- avgLongWinRatio: signal["avgLongWinRatio"],
16967
- avgShortWinRatio: signal["avgShortWinRatio"],
16968
- longNotionalUsdt: signal["longNotionalUsdt"],
16969
- shortNotionalUsdt: signal["shortNotionalUsdt"],
16970
- netNotionalUsdt: signal["netNotionalUsdt"],
16971
- longTraders: signal["longTraders"],
16972
- shortTraders: signal["shortTraders"],
16973
- vs1h: signal["vs1h"],
16974
- vs24h: signal["vs24h"],
16975
- vs7d: signal["vs7d"],
16976
- smartMoneyLongAvgEntry: signal["smartMoneyLongAvgEntry"],
16977
- smartMoneyShortAvgEntry: signal["smartMoneyShortAvgEntry"],
16978
- totalNotionalVs24h: signal["totalNotionalVs24h"],
16979
- currentPrice: signal["currentPrice"],
16980
- priceChange24h: signal["priceChange24h"],
16981
- fundingRate: signal["fundingRate"],
16982
- openInterest: signal["openInterest"],
16983
- longShortAccountRatio: signal["longShortAccountRatio"]
16984
- });
18169
+ const data = extractData(result);
18170
+ printDataTable(data, "No open positions", (r) => ({
18171
+ instId: r["instId"],
18172
+ direction: r["direction"] ?? r["posSide"],
18173
+ pos: r["pos"],
18174
+ lever: r["lever"],
18175
+ avgPx: r["avgPx"],
18176
+ last: r["last"],
18177
+ notionalUsd: r["notionalUsd"],
18178
+ upl: r["upl"],
18179
+ pnl: r["pnl"],
18180
+ positionIntensity: r["positionIntensity"]
18181
+ }));
16985
18182
  }
16986
- async function cmdSmartmoneySignalHistory(run, opts) {
16987
- const result = await run("smartmoney_get_signal_history", {
18183
+ async function cmdSmartmoneyTraderPositionsHistory(run, opts) {
18184
+ const result = await run("smartmoney_get_trader_positions_history", {
18185
+ authorId: opts.authorId,
16988
18186
  instId: opts.instId,
16989
- dataVersion: opts.dataVersion,
16990
- ts: opts.ts,
16991
- granularity: opts.granularity,
16992
- limit: opts.limit,
16993
- ...poolFilterArgs(opts)
18187
+ after: opts.after,
18188
+ before: opts.before,
18189
+ limit: opts.limit
16994
18190
  });
18191
+ if (opts.json) {
18192
+ printJson(result);
18193
+ return;
18194
+ }
16995
18195
  const data = extractData(result);
16996
- printDataList2(data, opts.json, "No signal history data", (r) => ({
16997
- ts: r["ts"],
16998
- longRatio: r["longRatio"],
16999
- weightedLongRatio: r["weightedLongRatio"],
17000
- tradersWithPosition: r["tradersWithPosition"],
17001
- netNotionalUsdt: r["netNotionalUsdt"],
17002
- totalNotionalUsdt: r["totalNotionalUsdt"],
17003
- tradersQualified: r["tradersQualified"]
18196
+ printDataTable(data, "No closed positions", (r) => ({
18197
+ cTime: r["cTime"],
18198
+ uTime: r["uTime"],
18199
+ instId: r["instId"],
18200
+ posSide: r["posSide"],
18201
+ openAvgPx: r["openAvgPx"],
18202
+ closeAvgPx: r["closeAvgPx"],
18203
+ pnl: r["pnl"],
18204
+ pnlRatio: r["pnlRatio"],
18205
+ closeType: r["closeType"]
17004
18206
  }));
18207
+ printPaginationHint(result);
17005
18208
  }
17006
- async function cmdSmartmoneyTraders(run, opts) {
17007
- const data = extractData(await run("smartmoney_get_traders", {
17008
- dataVersion: opts.dataVersion,
17009
- ...poolFilterArgs(opts),
17010
- authorIds: opts.authorIds,
18209
+ async function cmdSmartmoneyTraderOrdersHistory(run, opts) {
18210
+ const result = await run("smartmoney_get_trader_orders_history", {
18211
+ authorId: opts.authorId,
18212
+ instId: opts.instId,
17011
18213
  after: opts.after,
17012
18214
  before: opts.before,
17013
18215
  limit: opts.limit
18216
+ });
18217
+ if (opts.json) {
18218
+ printJson(result);
18219
+ return;
18220
+ }
18221
+ const data = extractData(result);
18222
+ printDataTable(data, "No order history", (r) => ({
18223
+ cTime: r["cTime"],
18224
+ instId: r["instId"],
18225
+ side: r["side"],
18226
+ posSide: r["posSide"],
18227
+ ordType: r["ordType"],
18228
+ avgPx: r["avgPx"],
18229
+ sz: r["sz"],
18230
+ value: r["value"]
17014
18231
  }));
17015
- printDataList2(data, opts.json, "No traders found", (r) => ({
18232
+ printPaginationHint(result);
18233
+ }
18234
+ async function cmdSmartmoneySearchTrader(run, opts) {
18235
+ const result = await run("smartmoney_search_trader", {
18236
+ keyword: opts.keyword
18237
+ });
18238
+ if (opts.json) {
18239
+ printJson(result);
18240
+ return;
18241
+ }
18242
+ const data = extractData(result);
18243
+ printDataTable(data, "No matching top traders", (r) => ({
17016
18244
  authorId: r["authorId"],
17017
18245
  nickName: r["nickName"],
17018
- pnl: r["pnl"],
17019
- pnlRatio: r["pnlRatio"],
17020
- winRatio: r["winRatio"],
17021
- asset: r["asset"]
18246
+ followerCount: r["followerCount"]
17022
18247
  }));
17023
18248
  }
17024
- async function cmdSmartmoneyTraderDetail(run, opts) {
17025
- const result = await run("smartmoney_get_trader_detail", {
17026
- authorId: opts.authorId,
17027
- period: opts.period,
17028
- instCcy: opts.instCcy,
17029
- tradeLimit: opts.tradeLimit
18249
+ var signalRowMapper = (r) => {
18250
+ const lsr = r["longShortRatio"] ?? {};
18251
+ const notional = r["notional"] ?? {};
18252
+ return {
18253
+ ccy: r["ccy"],
18254
+ tradersWithPosition: r["tradersWithPosition"],
18255
+ longRatio: lsr["longRatio"],
18256
+ weightedLongRatio: lsr["weightedLongRatio"],
18257
+ longTraders: r["longTraders"],
18258
+ shortTraders: r["shortTraders"],
18259
+ netNotionalUsdt: notional["netNotionalUsdt"],
18260
+ longRatioVs1h: lsr["longRatioVs1h"],
18261
+ longRatioVs24h: lsr["longRatioVs24h"],
18262
+ longRatioVs7d: lsr["longRatioVs7d"]
18263
+ };
18264
+ };
18265
+ async function cmdSmartmoneySignalOverviewByFilter(run, opts) {
18266
+ const result = await run("smartmoney_get_signal_overview_by_filter", {
18267
+ topInstruments: opts.topInstruments,
18268
+ instCcyList: csvToArray(opts.instCcyList),
18269
+ ...signalPoolFilterArgs(opts),
18270
+ lmtNum: opts.lmtNum
17030
18271
  });
17031
- const data = result;
17032
- const inner = data["data"];
17033
18272
  if (opts.json) {
17034
- printJson(inner ?? {});
18273
+ printJson(result);
17035
18274
  return;
17036
18275
  }
17037
- if (!inner) {
17038
- outputLine("No data");
18276
+ const data = extractData(result);
18277
+ printDataTable(data, "No signal data", signalRowMapper);
18278
+ }
18279
+ async function cmdSmartmoneySignalOverviewByTrader(run, opts) {
18280
+ const result = await run("smartmoney_get_signal_overview_by_trader", {
18281
+ authorIds: csvToArray(opts.authorIds),
18282
+ topInstruments: opts.topInstruments,
18283
+ instCcyList: csvToArray(opts.instCcyList)
18284
+ });
18285
+ if (opts.json) {
18286
+ printJson(result);
17039
18287
  return;
17040
18288
  }
17041
- const profileArr = inner["profile"];
17042
- if (Array.isArray(profileArr) && profileArr.length > 0) {
17043
- const p = profileArr[0];
17044
- outputLine("=== Profile ===");
17045
- printKv({
17046
- authorId: p["authorId"],
17047
- nickName: p["nickName"],
17048
- pnl: p["pnl"],
17049
- pnlRatio: p["pnlRatio"],
17050
- winRatio: p["winRatio"],
17051
- maxRetreat: p["maxRetreat"],
17052
- asset: p["asset"],
17053
- onboardDuration: p["onboardDuration"]
17054
- });
17055
- }
17056
- const posArr = inner["positions"];
17057
- if (Array.isArray(posArr) && posArr.length > 0) {
17058
- outputLine("\n=== Current Positions ===");
17059
- printJson(posArr);
18289
+ const data = extractData(result);
18290
+ printDataTable(data, "No signal data", signalRowMapper);
18291
+ }
18292
+ var trendRowMapper = (r) => ({
18293
+ dataVersion: r["dataVersion"],
18294
+ ccy: r["ccy"],
18295
+ longRatio: r["longRatio"],
18296
+ shortRatio: r["shortRatio"],
18297
+ weightedLongRatio: r["weightedLongRatio"],
18298
+ weightedShortRatio: r["weightedShortRatio"],
18299
+ longTraders: r["longTraders"],
18300
+ shortTraders: r["shortTraders"],
18301
+ tradersWithPosition: r["tradersWithPosition"],
18302
+ tradersQualified: r["tradersQualified"],
18303
+ netNotionalUsdt: r["netNotionalUsdt"],
18304
+ totalNotionalUsdt: r["totalNotionalUsdt"]
18305
+ });
18306
+ async function cmdSmartmoneySignalTrendByFilter(run, opts) {
18307
+ const result = await run("smartmoney_get_signal_trend_by_filter", {
18308
+ instCcy: opts.instCcy,
18309
+ asOfTime: opts.asOfTime,
18310
+ granularity: opts.granularity,
18311
+ limit: opts.limit,
18312
+ ...signalPoolFilterArgs(opts),
18313
+ lmtNum: opts.lmtNum
18314
+ });
18315
+ if (opts.json) {
18316
+ printJson(result);
18317
+ return;
17060
18318
  }
17061
- const tradeArr = inner["trades"];
17062
- if (Array.isArray(tradeArr) && tradeArr.length > 0) {
17063
- outputLine("\n=== Recent Trades ===");
17064
- printJson(tradeArr);
18319
+ const data = extractData(result);
18320
+ printDataTable(data, "No signal trend data", trendRowMapper);
18321
+ }
18322
+ async function cmdSmartmoneySignalTrendByTrader(run, opts) {
18323
+ const result = await run("smartmoney_get_signal_trend_by_trader", {
18324
+ authorIds: csvToArray(opts.authorIds),
18325
+ instCcy: opts.instCcy,
18326
+ asOfTime: opts.asOfTime,
18327
+ granularity: opts.granularity,
18328
+ limit: opts.limit
18329
+ });
18330
+ if (opts.json) {
18331
+ printJson(result);
18332
+ return;
17065
18333
  }
18334
+ const data = extractData(result);
18335
+ printDataTable(data, "No signal trend data", trendRowMapper);
17066
18336
  }
17067
18337
 
17068
18338
  // src/commands/auto-earn.ts
@@ -18579,7 +19849,7 @@ async function cmdEventCancel(run, opts) {
18579
19849
  // src/index.ts
18580
19850
  var _require3 = createRequire3(import.meta.url);
18581
19851
  var CLI_VERSION2 = _require3("../package.json").version;
18582
- var GIT_HASH2 = true ? "7705482" : "dev";
19852
+ var GIT_HASH2 = true ? "d0f8ad6" : "dev";
18583
19853
  function handlePilotCommand(action, json, force, binaryPath) {
18584
19854
  if (action === "status") return cmdPilotStatus(json, binaryPath);
18585
19855
  if (action === "install") return cmdPilotInstall(json, binaryPath);
@@ -18809,7 +20079,8 @@ function handleSpotAlgoCommand(run, subAction, v, json) {
18809
20079
  tdMode: v.tdMode,
18810
20080
  json
18811
20081
  });
18812
- if (subAction === "place")
20082
+ if (subAction === "place") {
20083
+ assertNoTpConflict(v.tpLevel, { tpTriggerPx: v.tpTriggerPx, tpOrdPx: v.tpOrdPx });
18813
20084
  return cmdSpotAlgoPlace(run, {
18814
20085
  instId: v.instId,
18815
20086
  tdMode: v.tdMode,
@@ -18820,13 +20091,38 @@ function handleSpotAlgoCommand(run, subAction, v, json) {
18820
20091
  tgtCcy: v.tgtCcy,
18821
20092
  tpTriggerPx: v.tpTriggerPx,
18822
20093
  tpOrdPx: v.tpOrdPx,
20094
+ tpOrdKind: v.tpOrdKind,
20095
+ tpTriggerPxType: v.tpTriggerPxType,
18823
20096
  slTriggerPx: v.slTriggerPx,
18824
20097
  slOrdPx: v.slOrdPx,
20098
+ slTriggerPxType: v.slTriggerPxType,
20099
+ stpMode: v.stpMode,
18825
20100
  callbackRatio: v.callbackRatio,
18826
20101
  callbackSpread: v.callbackSpread,
18827
20102
  activePx: v.activePx,
20103
+ triggerPx: v.triggerPx,
20104
+ orderPx: v.orderPx,
20105
+ advanceOrdType: v.advanceOrdType,
20106
+ triggerPxType: v.triggerPxType,
20107
+ chaseType: v.chaseType,
20108
+ chaseVal: v.chaseVal,
20109
+ maxChaseType: v.maxChaseType,
20110
+ maxChaseVal: v.maxChaseVal,
20111
+ pxVar: v.pxVar,
20112
+ pxSpread: v.pxSpread,
20113
+ szLimit: v.szLimit,
20114
+ pxLimit: v.pxLimit,
20115
+ timeInterval: v.timeInterval,
20116
+ // Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
20117
+ tpTriggerRatio: v.tpTriggerRatio,
20118
+ slTriggerRatio: v.slTriggerRatio,
20119
+ closeFraction: v.closeFraction,
20120
+ pxAmendType: v.pxAmendType,
20121
+ // Phase 3b CLI power-user flag (issue #183, CLI-only no MCP/skill exposure)
20122
+ tpLevels: v.tpLevel?.map(parseTpLevel),
18828
20123
  json
18829
20124
  });
20125
+ }
18830
20126
  if (subAction === "amend")
18831
20127
  return cmdSpotAlgoAmend(run, {
18832
20128
  instId: v.instId,
@@ -18849,6 +20145,16 @@ function handleSpotAlgoCommand(run, subAction, v, json) {
18849
20145
  });
18850
20146
  unknownSubcommand("spot algo", subAction, ["trail", "place", "amend", "cancel", "orders"]);
18851
20147
  }
20148
+ function assertNoTpConflict(tpLevel, singleFields) {
20149
+ if (!tpLevel || tpLevel.length === 0) return;
20150
+ const conflicting = ["tpTriggerPx", "tpOrdPx"].filter((k) => singleFields[k] !== void 0);
20151
+ if (conflicting.length > 0) {
20152
+ const flagNames = conflicting.map((k) => `--${k}`).join(", ");
20153
+ throw new Error(
20154
+ `Cannot use --tpLevel together with ${flagNames}. Use --tpLevel for split multi-tier take-profit, or single-TP flags for a single TP \u2014 not both.`
20155
+ );
20156
+ }
20157
+ }
18852
20158
  function handleSpotCommand(run, action, rest, v, json) {
18853
20159
  if (action === "orders")
18854
20160
  return cmdSpotOrders(run, {
@@ -18869,7 +20175,8 @@ function handleSpotCommand(run, action, rest, v, json) {
18869
20175
  newPx: v.newPx,
18870
20176
  json
18871
20177
  });
18872
- if (action === "place")
20178
+ if (action === "place") {
20179
+ assertNoTpConflict(v.tpLevel, { tpTriggerPx: v.tpTriggerPx, tpOrdPx: v.tpOrdPx });
18873
20180
  return cmdSpotPlace(run, {
18874
20181
  instId: v.instId,
18875
20182
  tdMode: v.tdMode,
@@ -18881,10 +20188,21 @@ function handleSpotCommand(run, action, rest, v, json) {
18881
20188
  clOrdId: v.clOrdId,
18882
20189
  tpTriggerPx: v.tpTriggerPx,
18883
20190
  tpOrdPx: v.tpOrdPx,
20191
+ tpOrdKind: v.tpOrdKind,
20192
+ tpTriggerPxType: v.tpTriggerPxType,
18884
20193
  slTriggerPx: v.slTriggerPx,
18885
20194
  slOrdPx: v.slOrdPx,
20195
+ slTriggerPxType: v.slTriggerPxType,
20196
+ stpMode: v.stpMode,
20197
+ // Phase 3c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
20198
+ tradeQuoteCcy: v.tradeQuoteCcy,
20199
+ banAmend: v.banAmend,
20200
+ pxAmendType: v.pxAmendType,
20201
+ // Phase 3b CLI power-user flag (issue #183, CLI-only no MCP/skill exposure)
20202
+ tpLevels: v.tpLevel?.map(parseTpLevel),
18886
20203
  json
18887
20204
  });
20205
+ }
18888
20206
  if (action === "cancel")
18889
20207
  return cmdSpotCancel(run, { instId: v.instId ?? rest[0], ordId: v.ordId, clOrdId: v.clOrdId, json });
18890
20208
  if (action === "algo")
@@ -18925,7 +20243,8 @@ function handleSwapAlgoCommand(run, subAction, v, json) {
18925
20243
  reduceOnly: v.reduceOnly,
18926
20244
  json
18927
20245
  });
18928
- if (subAction === "place")
20246
+ if (subAction === "place") {
20247
+ assertNoTpConflict(v.tpLevel, { tpTriggerPx: v.tpTriggerPx, tpOrdPx: v.tpOrdPx });
18929
20248
  return cmdSwapAlgoPlace(run, {
18930
20249
  instId: v.instId,
18931
20250
  side: v.side,
@@ -18937,14 +20256,40 @@ function handleSwapAlgoCommand(run, subAction, v, json) {
18937
20256
  tgtCcy: v.tgtCcy,
18938
20257
  tpTriggerPx: v.tpTriggerPx,
18939
20258
  tpOrdPx: v.tpOrdPx,
20259
+ tpOrdKind: v.tpOrdKind,
20260
+ tpTriggerPxType: v.tpTriggerPxType,
18940
20261
  slTriggerPx: v.slTriggerPx,
18941
20262
  slOrdPx: v.slOrdPx,
20263
+ slTriggerPxType: v.slTriggerPxType,
20264
+ stpMode: v.stpMode,
20265
+ cxlOnClosePos: v.cxlOnClosePos,
18942
20266
  reduceOnly: v.reduceOnly,
18943
20267
  callbackRatio: v.callbackRatio,
18944
20268
  callbackSpread: v.callbackSpread,
18945
20269
  activePx: v.activePx,
20270
+ triggerPx: v.triggerPx,
20271
+ orderPx: v.orderPx,
20272
+ advanceOrdType: v.advanceOrdType,
20273
+ triggerPxType: v.triggerPxType,
20274
+ chaseType: v.chaseType,
20275
+ chaseVal: v.chaseVal,
20276
+ maxChaseType: v.maxChaseType,
20277
+ maxChaseVal: v.maxChaseVal,
20278
+ pxVar: v.pxVar,
20279
+ pxSpread: v.pxSpread,
20280
+ szLimit: v.szLimit,
20281
+ pxLimit: v.pxLimit,
20282
+ timeInterval: v.timeInterval,
20283
+ // Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
20284
+ tpTriggerRatio: v.tpTriggerRatio,
20285
+ slTriggerRatio: v.slTriggerRatio,
20286
+ closeFraction: v.closeFraction,
20287
+ pxAmendType: v.pxAmendType,
20288
+ // Phase 3b CLI power-user flag (issue #183, CLI-only no MCP/skill exposure)
20289
+ tpLevels: v.tpLevel?.map(parseTpLevel),
18946
20290
  json
18947
20291
  });
20292
+ }
18948
20293
  if (subAction === "amend")
18949
20294
  return cmdSwapAlgoAmend(run, {
18950
20295
  instId: v.instId,
@@ -19000,7 +20345,8 @@ function handleSwapCommand(run, action, rest, v, json) {
19000
20345
  autoCxl: v.autoCxl,
19001
20346
  json
19002
20347
  });
19003
- if (action === "place")
20348
+ if (action === "place") {
20349
+ assertNoTpConflict(v.tpLevel, { tpTriggerPx: v.tpTriggerPx, tpOrdPx: v.tpOrdPx });
19004
20350
  return cmdSwapPlace(run, {
19005
20351
  instId: v.instId,
19006
20352
  side: v.side,
@@ -19014,10 +20360,19 @@ function handleSwapCommand(run, action, rest, v, json) {
19014
20360
  clOrdId: v.clOrdId,
19015
20361
  tpTriggerPx: v.tpTriggerPx,
19016
20362
  tpOrdPx: v.tpOrdPx,
20363
+ tpOrdKind: v.tpOrdKind,
20364
+ tpTriggerPxType: v.tpTriggerPxType,
19017
20365
  slTriggerPx: v.slTriggerPx,
19018
20366
  slOrdPx: v.slOrdPx,
20367
+ slTriggerPxType: v.slTriggerPxType,
20368
+ stpMode: v.stpMode,
20369
+ // Phase 3c CLI power-user flag (issue #182, CLI-only no MCP/skill exposure)
20370
+ pxAmendType: v.pxAmendType,
20371
+ // Phase 3b CLI power-user flag (issue #183, CLI-only no MCP/skill exposure)
20372
+ tpLevels: v.tpLevel?.map(parseTpLevel),
19019
20373
  json
19020
20374
  });
20375
+ }
19021
20376
  if (action === "cancel")
19022
20377
  return cmdSwapCancel(run, { instId: v.instId ?? rest[0], ordId: v.ordId, clOrdId: v.clOrdId, json });
19023
20378
  if (action === "amend")
@@ -19125,8 +20480,12 @@ function handleOptionCommand(run, action, rest, v, json) {
19125
20480
  clOrdId: v.clOrdId,
19126
20481
  tpTriggerPx: v.tpTriggerPx,
19127
20482
  tpOrdPx: v.tpOrdPx,
20483
+ tpOrdKind: v.tpOrdKind,
20484
+ tpTriggerPxType: v.tpTriggerPxType,
19128
20485
  slTriggerPx: v.slTriggerPx,
19129
20486
  slOrdPx: v.slOrdPx,
20487
+ slTriggerPxType: v.slTriggerPxType,
20488
+ stpMode: v.stpMode,
19130
20489
  json
19131
20490
  });
19132
20491
  if (action === "cancel")
@@ -19172,7 +20531,8 @@ function handleFuturesAlgoCommand(run, subAction, v, json) {
19172
20531
  reduceOnly: v.reduceOnly,
19173
20532
  json
19174
20533
  });
19175
- if (subAction === "place")
20534
+ if (subAction === "place") {
20535
+ assertNoTpConflict(v.tpLevel, { tpTriggerPx: v.tpTriggerPx, tpOrdPx: v.tpOrdPx });
19176
20536
  return cmdFuturesAlgoPlace(run, {
19177
20537
  instId: v.instId,
19178
20538
  side: v.side,
@@ -19184,14 +20544,40 @@ function handleFuturesAlgoCommand(run, subAction, v, json) {
19184
20544
  tgtCcy: v.tgtCcy,
19185
20545
  tpTriggerPx: v.tpTriggerPx,
19186
20546
  tpOrdPx: v.tpOrdPx,
20547
+ tpOrdKind: v.tpOrdKind,
20548
+ tpTriggerPxType: v.tpTriggerPxType,
19187
20549
  slTriggerPx: v.slTriggerPx,
19188
20550
  slOrdPx: v.slOrdPx,
20551
+ slTriggerPxType: v.slTriggerPxType,
20552
+ stpMode: v.stpMode,
20553
+ cxlOnClosePos: v.cxlOnClosePos,
19189
20554
  reduceOnly: v.reduceOnly,
19190
20555
  callbackRatio: v.callbackRatio,
19191
20556
  callbackSpread: v.callbackSpread,
19192
20557
  activePx: v.activePx,
20558
+ triggerPx: v.triggerPx,
20559
+ orderPx: v.orderPx,
20560
+ advanceOrdType: v.advanceOrdType,
20561
+ triggerPxType: v.triggerPxType,
20562
+ chaseType: v.chaseType,
20563
+ chaseVal: v.chaseVal,
20564
+ maxChaseType: v.maxChaseType,
20565
+ maxChaseVal: v.maxChaseVal,
20566
+ pxVar: v.pxVar,
20567
+ pxSpread: v.pxSpread,
20568
+ szLimit: v.szLimit,
20569
+ pxLimit: v.pxLimit,
20570
+ timeInterval: v.timeInterval,
20571
+ // Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
20572
+ tpTriggerRatio: v.tpTriggerRatio,
20573
+ slTriggerRatio: v.slTriggerRatio,
20574
+ closeFraction: v.closeFraction,
20575
+ pxAmendType: v.pxAmendType,
20576
+ // Phase 3b CLI power-user flag (issue #183, CLI-only no MCP/skill exposure)
20577
+ tpLevels: v.tpLevel?.map(parseTpLevel),
19193
20578
  json
19194
20579
  });
20580
+ }
19195
20581
  if (subAction === "amend")
19196
20582
  return cmdFuturesAlgoAmend(run, {
19197
20583
  instId: v.instId,
@@ -19239,7 +20625,8 @@ function handleFuturesQuery(run, action, v, json) {
19239
20625
  function handleFuturesCommand(run, action, rest, v, json) {
19240
20626
  const queryResult = handleFuturesQuery(run, action, v, json);
19241
20627
  if (queryResult !== void 0) return queryResult;
19242
- if (action === "place")
20628
+ if (action === "place") {
20629
+ assertNoTpConflict(v.tpLevel, { tpTriggerPx: v.tpTriggerPx, tpOrdPx: v.tpOrdPx });
19243
20630
  return cmdFuturesPlace(run, {
19244
20631
  instId: v.instId,
19245
20632
  side: v.side,
@@ -19253,10 +20640,19 @@ function handleFuturesCommand(run, action, rest, v, json) {
19253
20640
  clOrdId: v.clOrdId,
19254
20641
  tpTriggerPx: v.tpTriggerPx,
19255
20642
  tpOrdPx: v.tpOrdPx,
20643
+ tpOrdKind: v.tpOrdKind,
20644
+ tpTriggerPxType: v.tpTriggerPxType,
19256
20645
  slTriggerPx: v.slTriggerPx,
19257
20646
  slOrdPx: v.slOrdPx,
20647
+ slTriggerPxType: v.slTriggerPxType,
20648
+ stpMode: v.stpMode,
20649
+ // Phase 3c CLI power-user flag (issue #182, CLI-only no MCP/skill exposure)
20650
+ pxAmendType: v.pxAmendType,
20651
+ // Phase 3b CLI power-user flag (issue #183, CLI-only no MCP/skill exposure)
20652
+ tpLevels: v.tpLevel?.map(parseTpLevel),
19258
20653
  json
19259
20654
  });
20655
+ }
19260
20656
  if (action === "cancel")
19261
20657
  return cmdFuturesCancel(run, { instId: v.instId ?? rest[0], ordId: v.ordId, clOrdId: v.clOrdId, json });
19262
20658
  if (action === "amend")
@@ -19457,79 +20853,171 @@ function handleEarnFlashEarnCommand(run, action, v, json) {
19457
20853
  process.exitCode = 1;
19458
20854
  }
19459
20855
  function handleSmartmoneyCommand(run, action, rest, v, json) {
19460
- const poolFilters = {
19461
- sortType: v.sortType,
20856
+ void rest;
20857
+ const signalPoolFilters = {
20858
+ sortBy: v.sortBy,
19462
20859
  period: v.period,
19463
- pnl: v.pnl,
19464
- winRatio: v.winRatio,
19465
- maxRetreat: v.maxRetreat,
19466
- asset: v.asset
20860
+ pnlTier: v.pnlTier,
20861
+ winRateTier: v.winRateTier,
20862
+ maxDrawdownTier: v.maxDrawdownTier,
20863
+ aumTier: v.aumTier
19467
20864
  };
19468
- if (action === "overview")
19469
- return cmdSmartmoneyOverview(run, {
19470
- dataVersion: v.dataVersion,
19471
- ts: v.ts,
19472
- instType: v.instType,
19473
- ...poolFilters,
19474
- lmtNum: v.lmtNum,
19475
- instCcyList: v.instCcyList,
19476
- instCcy: v.instCcy,
19477
- topInstruments: v.topInstruments,
20865
+ const leaderboardPoolFilters = {
20866
+ sortBy: v.sortBy,
20867
+ period: v.period,
20868
+ minPnl: v.minPnl,
20869
+ minWinRate: v.minWinRate,
20870
+ maxDrawdown: v.maxDrawdown,
20871
+ minAum: v.minAum
20872
+ };
20873
+ if (action === "traders-by-filter")
20874
+ return cmdSmartmoneyTradersByFilter(run, {
20875
+ updateTime: v.updateTime,
20876
+ ...leaderboardPoolFilters,
20877
+ after: v.after,
20878
+ before: v.before,
20879
+ limit: v.limit,
19478
20880
  json
19479
20881
  });
19480
- if (action === "signal")
19481
- return cmdSmartmoneySignal(run, {
19482
- instId: v.instId,
19483
- dataVersion: v.dataVersion,
19484
- ts: v.ts,
19485
- ...poolFilters,
19486
- instCcy: v.instCcy,
19487
- lmtNum: v.lmtNum,
20882
+ if (action === "performance-by-trader") {
20883
+ if (!v.authorIds) {
20884
+ errorLine("Missing required --authorIds: okx smartmoney performance-by-trader --authorIds <id1,id2> [--period <3|7|30|90>]");
20885
+ process.exitCode = 1;
20886
+ return;
20887
+ }
20888
+ return cmdSmartmoneyPerformanceByTrader(run, {
19488
20889
  authorIds: v.authorIds,
20890
+ period: v.period,
19489
20891
  json
19490
20892
  });
19491
- if (action === "signal-history") {
19492
- if (!v.instId) {
19493
- errorLine("Missing required --instId: okx smartmoney signal-history --instId <id>");
20893
+ }
20894
+ if (action === "trader-positions") {
20895
+ if (!v.authorId) {
20896
+ errorLine("Missing required --authorId: okx smartmoney trader-positions --authorId <id> [--instId <id>]");
19494
20897
  process.exitCode = 1;
19495
20898
  return;
19496
20899
  }
19497
- return cmdSmartmoneySignalHistory(run, {
20900
+ return cmdSmartmoneyTraderPositions(run, {
20901
+ authorId: v.authorId,
19498
20902
  instId: v.instId,
19499
- dataVersion: v.dataVersion,
19500
- ts: v.ts,
19501
- granularity: v.granularity,
19502
- limit: v.limit,
19503
- ...poolFilters,
19504
20903
  json
19505
20904
  });
19506
20905
  }
19507
- if (action === "traders")
19508
- return cmdSmartmoneyTraders(run, {
19509
- dataVersion: v.dataVersion,
19510
- ...poolFilters,
19511
- authorIds: v.authorIds,
20906
+ if (action === "trader-positions-history") {
20907
+ if (!v.authorId) {
20908
+ errorLine("Missing required --authorId: okx smartmoney trader-positions-history --authorId <id> [--instId <id>] [--limit <n>]");
20909
+ process.exitCode = 1;
20910
+ return;
20911
+ }
20912
+ return cmdSmartmoneyTraderPositionsHistory(run, {
20913
+ authorId: v.authorId,
20914
+ instId: v.instId,
19512
20915
  after: v.after,
19513
20916
  before: v.before,
19514
20917
  limit: v.limit,
19515
20918
  json
19516
20919
  });
19517
- if (action === "trader") {
20920
+ }
20921
+ if (action === "trader-orders-history") {
19518
20922
  if (!v.authorId) {
19519
- errorLine("Missing required --authorId: okx smartmoney trader --authorId <id>");
20923
+ errorLine("Missing required --authorId: okx smartmoney trader-orders-history --authorId <id> [--instId <id>] [--limit <n>]");
19520
20924
  process.exitCode = 1;
19521
20925
  return;
19522
20926
  }
19523
- return cmdSmartmoneyTraderDetail(run, {
20927
+ return cmdSmartmoneyTraderOrdersHistory(run, {
19524
20928
  authorId: v.authorId,
19525
- period: v.period,
20929
+ instId: v.instId,
20930
+ after: v.after,
20931
+ before: v.before,
20932
+ limit: v.limit,
20933
+ json
20934
+ });
20935
+ }
20936
+ if (action === "search-trader") {
20937
+ if (!v.keyword || v.keyword.trim() === "") {
20938
+ errorLine("Missing required --keyword: okx smartmoney search-trader --keyword <name>");
20939
+ process.exitCode = 1;
20940
+ return;
20941
+ }
20942
+ return cmdSmartmoneySearchTrader(run, {
20943
+ keyword: v.keyword,
20944
+ json
20945
+ });
20946
+ }
20947
+ if (action === "signal-overview-by-filter") {
20948
+ if (v.topInstruments && v.instCcyList) {
20949
+ errorLine(
20950
+ "--topInstruments and --instCcyList are mutually exclusive. Pass exactly one \u2014 `--topInstruments` for top-N hottest coins, or `--instCcyList` for specific coins."
20951
+ );
20952
+ process.exitCode = 1;
20953
+ return;
20954
+ }
20955
+ return cmdSmartmoneySignalOverviewByFilter(run, {
20956
+ topInstruments: v.topInstruments,
20957
+ instCcyList: v.instCcyList,
20958
+ ...signalPoolFilters,
20959
+ lmtNum: v.lmtNum,
20960
+ json
20961
+ });
20962
+ }
20963
+ if (action === "signal-overview-by-trader") {
20964
+ if (!v.authorIds) {
20965
+ errorLine("Missing required --authorIds: okx smartmoney signal-overview-by-trader --authorIds <id1,id2> [--topInstruments <n> | --instCcyList <BTC,ETH>]");
20966
+ process.exitCode = 1;
20967
+ return;
20968
+ }
20969
+ if (v.topInstruments && v.instCcyList) {
20970
+ errorLine(
20971
+ "--topInstruments and --instCcyList are mutually exclusive. Pass exactly one \u2014 `--topInstruments` for top-N hottest coins, or `--instCcyList` for specific coins."
20972
+ );
20973
+ process.exitCode = 1;
20974
+ return;
20975
+ }
20976
+ return cmdSmartmoneySignalOverviewByTrader(run, {
20977
+ authorIds: v.authorIds,
20978
+ topInstruments: v.topInstruments,
20979
+ instCcyList: v.instCcyList,
20980
+ json
20981
+ });
20982
+ }
20983
+ if (action === "signal-trend-by-filter") {
20984
+ if (!v.instCcy) {
20985
+ errorLine("Missing required --instCcy: okx smartmoney signal-trend-by-filter --instCcy <ccy> [--asOfTime <yyyyMMddHH>] [--granularity <1h|1d>] [--limit <n>]");
20986
+ process.exitCode = 1;
20987
+ return;
20988
+ }
20989
+ return cmdSmartmoneySignalTrendByFilter(run, {
19526
20990
  instCcy: v.instCcy,
19527
- tradeLimit: v.tradeLimit,
20991
+ asOfTime: v.asOfTime,
20992
+ granularity: v.granularity,
20993
+ limit: v.limit,
20994
+ ...signalPoolFilters,
20995
+ lmtNum: v.lmtNum,
20996
+ json
20997
+ });
20998
+ }
20999
+ if (action === "signal-trend-by-trader") {
21000
+ if (!v.authorIds) {
21001
+ errorLine("Missing required --authorIds: okx smartmoney signal-trend-by-trader --authorIds <id1,id2> --instCcy <ccy> [--asOfTime <yyyyMMddHH>]");
21002
+ process.exitCode = 1;
21003
+ return;
21004
+ }
21005
+ if (!v.instCcy) {
21006
+ errorLine("Missing required --instCcy: okx smartmoney signal-trend-by-trader --authorIds <id1,id2> --instCcy <ccy> [--asOfTime <yyyyMMddHH>]");
21007
+ process.exitCode = 1;
21008
+ return;
21009
+ }
21010
+ return cmdSmartmoneySignalTrendByTrader(run, {
21011
+ authorIds: v.authorIds,
21012
+ instCcy: v.instCcy,
21013
+ asOfTime: v.asOfTime,
21014
+ granularity: v.granularity,
21015
+ limit: v.limit,
19528
21016
  json
19529
21017
  });
19530
21018
  }
19531
21019
  errorLine(`Unknown smartmoney command: ${action}`);
19532
- errorLine("Valid: overview, signal, signal-history, traders, trader");
21020
+ errorLine("Valid: traders-by-filter, performance-by-trader, trader-positions, trader-positions-history, trader-orders-history, search-trader, signal-overview-by-filter, signal-overview-by-trader, signal-trend-by-filter, signal-trend-by-trader");
19533
21021
  process.exitCode = 1;
19534
21022
  }
19535
21023
  function handleEarnSavingsCommand(run, action, rest, v, json) {
@@ -19860,6 +21348,7 @@ main().catch((error) => {
19860
21348
  process.exitCode = 1;
19861
21349
  });
19862
21350
  export {
21351
+ assertNoTpConflict,
19863
21352
  handleAccountWriteCommand,
19864
21353
  handleBotCommand,
19865
21354
  handleBotDcaCommand,