@okx_ai/okx-trade-mcp 1.3.2 → 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 +1296 -301
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +1 -1
package/dist/index.js
CHANGED
|
@@ -1816,6 +1816,16 @@ function readBoolean(args, key) {
|
|
|
1816
1816
|
}
|
|
1817
1817
|
return value;
|
|
1818
1818
|
}
|
|
1819
|
+
function readStringArray(args, key) {
|
|
1820
|
+
const value = args[key];
|
|
1821
|
+
if (value === void 0 || value === null) {
|
|
1822
|
+
return void 0;
|
|
1823
|
+
}
|
|
1824
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
1825
|
+
throw new ValidationError(`Parameter "${key}" must be an array of strings.`);
|
|
1826
|
+
}
|
|
1827
|
+
return value;
|
|
1828
|
+
}
|
|
1819
1829
|
function requireString(args, key) {
|
|
1820
1830
|
const value = readString(args, key);
|
|
1821
1831
|
if (!value || value.length === 0) {
|
|
@@ -1856,12 +1866,170 @@ function validateSwapInstId(instId) {
|
|
|
1856
1866
|
);
|
|
1857
1867
|
}
|
|
1858
1868
|
}
|
|
1869
|
+
var TP_ORD_KIND_SCHEMA = {
|
|
1870
|
+
type: "string",
|
|
1871
|
+
enum: ["condition", "limit"],
|
|
1872
|
+
description: "condition(default)=trigger-based TP; limit=immediate limit order (no trigger phase)"
|
|
1873
|
+
};
|
|
1874
|
+
var TP_TRIGGER_PX_TYPE_SCHEMA = {
|
|
1875
|
+
type: "string",
|
|
1876
|
+
enum: ["last", "index", "mark"],
|
|
1877
|
+
description: "TP trigger price source: last(default)|index|mark"
|
|
1878
|
+
};
|
|
1879
|
+
var SL_TRIGGER_PX_TYPE_SCHEMA = {
|
|
1880
|
+
type: "string",
|
|
1881
|
+
enum: ["last", "index", "mark"],
|
|
1882
|
+
description: "SL trigger price source: last(default)|index|mark"
|
|
1883
|
+
};
|
|
1884
|
+
var STP_MODE_SCHEMA = {
|
|
1885
|
+
type: "string",
|
|
1886
|
+
enum: ["cancel_maker", "cancel_taker", "cancel_both"],
|
|
1887
|
+
description: "Self-trade prevention: cancel_maker|cancel_taker|cancel_both"
|
|
1888
|
+
};
|
|
1889
|
+
var CXL_ON_CLOSE_POS_SCHEMA = {
|
|
1890
|
+
type: "boolean",
|
|
1891
|
+
description: "Auto-cancel TP/SL when associated position closes (algo only)"
|
|
1892
|
+
};
|
|
1893
|
+
var PHASE1_PLACE_FLAGS_SCHEMA = {
|
|
1894
|
+
tpOrdKind: TP_ORD_KIND_SCHEMA,
|
|
1895
|
+
tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
|
|
1896
|
+
slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
|
|
1897
|
+
stpMode: STP_MODE_SCHEMA
|
|
1898
|
+
};
|
|
1899
|
+
var PHASE1_ALGO_FLAGS_SCHEMA = {
|
|
1900
|
+
...PHASE1_PLACE_FLAGS_SCHEMA,
|
|
1901
|
+
cxlOnClosePos: CXL_ON_CLOSE_POS_SCHEMA
|
|
1902
|
+
};
|
|
1903
|
+
var TRIGGER_FLAGS_SCHEMA = {
|
|
1904
|
+
triggerPx: {
|
|
1905
|
+
type: "string",
|
|
1906
|
+
description: "Activation price; order submits when market hits this level (trigger only)"
|
|
1907
|
+
},
|
|
1908
|
+
orderPx: {
|
|
1909
|
+
type: "string",
|
|
1910
|
+
description: "Order price submitted when trigger fires; -1=market (trigger only)"
|
|
1911
|
+
},
|
|
1912
|
+
advanceOrdType: {
|
|
1913
|
+
type: "string",
|
|
1914
|
+
enum: ["fok", "ioc"],
|
|
1915
|
+
description: "Execution qualifier for triggered order: fok=fill-or-kill, ioc=immediate-or-cancel (trigger only)"
|
|
1916
|
+
},
|
|
1917
|
+
triggerPxType: {
|
|
1918
|
+
type: "string",
|
|
1919
|
+
enum: ["last", "index", "mark"],
|
|
1920
|
+
description: "Price type used to evaluate trigger: last(default)|index|mark (trigger only)"
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
var CHASE_FLAGS_SCHEMA = {
|
|
1924
|
+
chaseType: {
|
|
1925
|
+
type: "string",
|
|
1926
|
+
enum: ["distance", "ratio"],
|
|
1927
|
+
description: "Chase unit: distance=price ticks, ratio=proportion of price (chase only, default distance)"
|
|
1928
|
+
},
|
|
1929
|
+
chaseVal: {
|
|
1930
|
+
type: "string",
|
|
1931
|
+
description: "Chase amount matching chaseType (e.g. 0.5 ticks for distance, 0.001 for ratio) (chase only)"
|
|
1932
|
+
},
|
|
1933
|
+
maxChaseType: {
|
|
1934
|
+
type: "string",
|
|
1935
|
+
enum: ["distance", "ratio"],
|
|
1936
|
+
description: "Upper-bound unit for chase (chase only)"
|
|
1937
|
+
},
|
|
1938
|
+
maxChaseVal: {
|
|
1939
|
+
type: "string",
|
|
1940
|
+
description: "Upper-bound value for chase (chase only)"
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
var ICEBERG_TWAP_FLAGS_SCHEMA = {
|
|
1944
|
+
pxVar: {
|
|
1945
|
+
type: "string",
|
|
1946
|
+
description: "Price variance % [0.0001, 0.01]; provide pxVar OR pxSpread (iceberg/twap only)"
|
|
1947
|
+
},
|
|
1948
|
+
pxSpread: {
|
|
1949
|
+
type: "string",
|
|
1950
|
+
description: "Price variance constant >= 0; provide pxVar OR pxSpread (iceberg/twap only)"
|
|
1951
|
+
},
|
|
1952
|
+
szLimit: {
|
|
1953
|
+
type: "string",
|
|
1954
|
+
description: "Average per-child-order size (iceberg/twap only)"
|
|
1955
|
+
},
|
|
1956
|
+
pxLimit: {
|
|
1957
|
+
type: "string",
|
|
1958
|
+
description: "Order price ceiling >= 0 (iceberg/twap only)"
|
|
1959
|
+
},
|
|
1960
|
+
timeInterval: {
|
|
1961
|
+
type: "string",
|
|
1962
|
+
description: "Seconds between child orders (iceberg/twap only)"
|
|
1963
|
+
}
|
|
1964
|
+
};
|
|
1965
|
+
function buildTriggerOrdTypeBody(args) {
|
|
1966
|
+
return compactObject({
|
|
1967
|
+
triggerPx: readString(args, "triggerPx"),
|
|
1968
|
+
orderPx: readString(args, "orderPx"),
|
|
1969
|
+
advanceOrdType: readString(args, "advanceOrdType"),
|
|
1970
|
+
triggerPxType: readString(args, "triggerPxType"),
|
|
1971
|
+
attachAlgoOrds: buildAttachAlgoOrds(args)
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
function buildChaseOrdTypeBody(args) {
|
|
1975
|
+
return compactObject({
|
|
1976
|
+
chaseType: readString(args, "chaseType"),
|
|
1977
|
+
chaseVal: readString(args, "chaseVal"),
|
|
1978
|
+
maxChaseType: readString(args, "maxChaseType"),
|
|
1979
|
+
maxChaseVal: readString(args, "maxChaseVal")
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
function buildIcebergTwapOrdTypeBody(args) {
|
|
1983
|
+
return compactObject({
|
|
1984
|
+
pxVar: readString(args, "pxVar"),
|
|
1985
|
+
pxSpread: readString(args, "pxSpread"),
|
|
1986
|
+
szLimit: readString(args, "szLimit"),
|
|
1987
|
+
pxLimit: readString(args, "pxLimit"),
|
|
1988
|
+
timeInterval: readString(args, "timeInterval")
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
function buildAlgoConditionalCommonFields(args) {
|
|
1992
|
+
return {
|
|
1993
|
+
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
1994
|
+
tpOrdPx: readString(args, "tpOrdPx"),
|
|
1995
|
+
tpOrdKind: readString(args, "tpOrdKind"),
|
|
1996
|
+
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
1997
|
+
tpTriggerRatio: readString(args, "tpTriggerRatio"),
|
|
1998
|
+
slTriggerPx: readString(args, "slTriggerPx"),
|
|
1999
|
+
slOrdPx: readString(args, "slOrdPx"),
|
|
2000
|
+
slTriggerPxType: readString(args, "slTriggerPxType"),
|
|
2001
|
+
slTriggerRatio: readString(args, "slTriggerRatio"),
|
|
2002
|
+
closeFraction: readString(args, "closeFraction"),
|
|
2003
|
+
activePx: readString(args, "activePx")
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
1859
2006
|
function buildAttachAlgoOrds(source) {
|
|
2007
|
+
const tpLevels = source["tpLevels"];
|
|
2008
|
+
if (Array.isArray(tpLevels) && tpLevels.length > 0) {
|
|
2009
|
+
return tpLevels.map(
|
|
2010
|
+
(level) => compactObject(level)
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
1860
2013
|
const tpTriggerPx = readString(source, "tpTriggerPx");
|
|
1861
2014
|
const tpOrdPx = readString(source, "tpOrdPx");
|
|
1862
2015
|
const slTriggerPx = readString(source, "slTriggerPx");
|
|
1863
2016
|
const slOrdPx = readString(source, "slOrdPx");
|
|
1864
|
-
const
|
|
2017
|
+
const tpOrdKind = readString(source, "tpOrdKind");
|
|
2018
|
+
const tpTriggerPxType = readString(source, "tpTriggerPxType");
|
|
2019
|
+
const slTriggerPxType = readString(source, "slTriggerPxType");
|
|
2020
|
+
const tpTriggerRatio = readString(source, "tpTriggerRatio");
|
|
2021
|
+
const slTriggerRatio = readString(source, "slTriggerRatio");
|
|
2022
|
+
const entry = compactObject({
|
|
2023
|
+
tpTriggerPx,
|
|
2024
|
+
tpOrdPx,
|
|
2025
|
+
slTriggerPx,
|
|
2026
|
+
slOrdPx,
|
|
2027
|
+
tpOrdKind,
|
|
2028
|
+
tpTriggerPxType,
|
|
2029
|
+
slTriggerPxType,
|
|
2030
|
+
tpTriggerRatio,
|
|
2031
|
+
slTriggerRatio
|
|
2032
|
+
});
|
|
1865
2033
|
return Object.keys(entry).length > 0 ? [entry] : void 0;
|
|
1866
2034
|
}
|
|
1867
2035
|
var OKX_CANDLE_BARS = [
|
|
@@ -2908,7 +3076,7 @@ function registerAlgoTradeTools() {
|
|
|
2908
3076
|
{
|
|
2909
3077
|
name: "swap_place_algo_order",
|
|
2910
3078
|
module: "swap",
|
|
2911
|
-
description: "Place a SWAP/FUTURES algo order
|
|
3079
|
+
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.",
|
|
2912
3080
|
isWrite: true,
|
|
2913
3081
|
inputSchema: {
|
|
2914
3082
|
type: "object",
|
|
@@ -2934,8 +3102,8 @@ function registerAlgoTradeTools() {
|
|
|
2934
3102
|
},
|
|
2935
3103
|
ordType: {
|
|
2936
3104
|
type: "string",
|
|
2937
|
-
enum: ["conditional", "oco", "move_order_stop"],
|
|
2938
|
-
description: "conditional=single TP/SL or both; oco=TP+SL pair
|
|
3105
|
+
enum: ["conditional", "oco", "move_order_stop", "trigger", "chase", "iceberg", "twap"],
|
|
3106
|
+
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"
|
|
2939
3107
|
},
|
|
2940
3108
|
sz: {
|
|
2941
3109
|
type: "string",
|
|
@@ -2949,11 +3117,8 @@ function registerAlgoTradeTools() {
|
|
|
2949
3117
|
type: "string",
|
|
2950
3118
|
description: "TP order price; -1=market (conditional/oco only)"
|
|
2951
3119
|
},
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
enum: ["last", "index", "mark"],
|
|
2955
|
-
description: "last(default)|index|mark (conditional/oco only)"
|
|
2956
|
-
},
|
|
3120
|
+
tpOrdKind: TP_ORD_KIND_SCHEMA,
|
|
3121
|
+
tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
|
|
2957
3122
|
slTriggerPx: {
|
|
2958
3123
|
type: "string",
|
|
2959
3124
|
description: "SL trigger price (conditional/oco only)"
|
|
@@ -2962,11 +3127,9 @@ function registerAlgoTradeTools() {
|
|
|
2962
3127
|
type: "string",
|
|
2963
3128
|
description: "SL order price; -1=market (recommended) (conditional/oco only)"
|
|
2964
3129
|
},
|
|
2965
|
-
slTriggerPxType:
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
description: "last(default)|index|mark (conditional/oco only)"
|
|
2969
|
-
},
|
|
3130
|
+
slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
|
|
3131
|
+
stpMode: STP_MODE_SCHEMA,
|
|
3132
|
+
cxlOnClosePos: CXL_ON_CLOSE_POS_SCHEMA,
|
|
2970
3133
|
callbackRatio: {
|
|
2971
3134
|
type: "string",
|
|
2972
3135
|
description: "Callback ratio (e.g. '0.01'=1%); provide either ratio or spread (move_order_stop only)"
|
|
@@ -2979,6 +3142,9 @@ function registerAlgoTradeTools() {
|
|
|
2979
3142
|
type: "string",
|
|
2980
3143
|
description: "Activation price; tracking starts after market reaches this level (move_order_stop only)"
|
|
2981
3144
|
},
|
|
3145
|
+
...TRIGGER_FLAGS_SCHEMA,
|
|
3146
|
+
...CHASE_FLAGS_SCHEMA,
|
|
3147
|
+
...ICEBERG_TWAP_FLAGS_SCHEMA,
|
|
2982
3148
|
tgtCcy: {
|
|
2983
3149
|
type: "string",
|
|
2984
3150
|
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
@@ -2998,6 +3164,8 @@ function registerAlgoTradeTools() {
|
|
|
2998
3164
|
handler: async (rawArgs, context) => {
|
|
2999
3165
|
const args = asRecord(rawArgs);
|
|
3000
3166
|
const reduceOnly = args.reduceOnly;
|
|
3167
|
+
const cxlOnClosePos = args.cxlOnClosePos;
|
|
3168
|
+
const ordType = requireString(args, "ordType");
|
|
3001
3169
|
const resolved = await resolveQuoteCcySz(
|
|
3002
3170
|
requireString(args, "instId"),
|
|
3003
3171
|
requireString(args, "sz"),
|
|
@@ -3006,29 +3174,44 @@ function registerAlgoTradeTools() {
|
|
|
3006
3174
|
context.client,
|
|
3007
3175
|
readString(args, "tdMode")
|
|
3008
3176
|
);
|
|
3177
|
+
const base = compactObject({
|
|
3178
|
+
instId: requireString(args, "instId"),
|
|
3179
|
+
tdMode: requireString(args, "tdMode"),
|
|
3180
|
+
side: requireString(args, "side"),
|
|
3181
|
+
posSide: readString(args, "posSide"),
|
|
3182
|
+
ordType,
|
|
3183
|
+
sz: resolved.sz,
|
|
3184
|
+
tgtCcy: resolved.tgtCcy,
|
|
3185
|
+
stpMode: readString(args, "stpMode"),
|
|
3186
|
+
cxlOnClosePos: typeof cxlOnClosePos === "boolean" ? String(cxlOnClosePos) : void 0,
|
|
3187
|
+
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3188
|
+
clOrdId: readString(args, "clOrdId"),
|
|
3189
|
+
// Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
|
|
3190
|
+
pxAmendType: readString(args, "pxAmendType"),
|
|
3191
|
+
tag: context.config.sourceTag
|
|
3192
|
+
});
|
|
3193
|
+
switch (ordType) {
|
|
3194
|
+
case "trigger":
|
|
3195
|
+
Object.assign(base, buildTriggerOrdTypeBody(args));
|
|
3196
|
+
break;
|
|
3197
|
+
case "chase":
|
|
3198
|
+
Object.assign(base, buildChaseOrdTypeBody(args));
|
|
3199
|
+
break;
|
|
3200
|
+
case "iceberg":
|
|
3201
|
+
case "twap":
|
|
3202
|
+
Object.assign(base, buildIcebergTwapOrdTypeBody(args));
|
|
3203
|
+
break;
|
|
3204
|
+
default:
|
|
3205
|
+
Object.assign(base, compactObject({
|
|
3206
|
+
...buildAlgoConditionalCommonFields(args),
|
|
3207
|
+
callBackRatio: readString(args, "callbackRatio"),
|
|
3208
|
+
callBackSpread: readString(args, "callbackSpread")
|
|
3209
|
+
}));
|
|
3210
|
+
break;
|
|
3211
|
+
}
|
|
3009
3212
|
const response = await context.client.privatePost(
|
|
3010
3213
|
"/api/v5/trade/order-algo",
|
|
3011
|
-
|
|
3012
|
-
instId: requireString(args, "instId"),
|
|
3013
|
-
tdMode: requireString(args, "tdMode"),
|
|
3014
|
-
side: requireString(args, "side"),
|
|
3015
|
-
posSide: readString(args, "posSide"),
|
|
3016
|
-
ordType: requireString(args, "ordType"),
|
|
3017
|
-
sz: resolved.sz,
|
|
3018
|
-
tgtCcy: resolved.tgtCcy,
|
|
3019
|
-
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
3020
|
-
tpOrdPx: readString(args, "tpOrdPx"),
|
|
3021
|
-
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
3022
|
-
slTriggerPx: readString(args, "slTriggerPx"),
|
|
3023
|
-
slOrdPx: readString(args, "slOrdPx"),
|
|
3024
|
-
slTriggerPxType: readString(args, "slTriggerPxType"),
|
|
3025
|
-
callBackRatio: readString(args, "callbackRatio"),
|
|
3026
|
-
callBackSpread: readString(args, "callbackSpread"),
|
|
3027
|
-
activePx: readString(args, "activePx"),
|
|
3028
|
-
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3029
|
-
clOrdId: readString(args, "clOrdId"),
|
|
3030
|
-
tag: context.config.sourceTag
|
|
3031
|
-
}),
|
|
3214
|
+
base,
|
|
3032
3215
|
privateRateLimit("swap_place_algo_order", 20)
|
|
3033
3216
|
);
|
|
3034
3217
|
const result = normalizeResponse(response);
|
|
@@ -3253,7 +3436,7 @@ function registerFuturesAlgoTools() {
|
|
|
3253
3436
|
{
|
|
3254
3437
|
name: "futures_place_algo_order",
|
|
3255
3438
|
module: "futures",
|
|
3256
|
-
description: "Place a FUTURES delivery algo order
|
|
3439
|
+
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.",
|
|
3257
3440
|
isWrite: true,
|
|
3258
3441
|
inputSchema: {
|
|
3259
3442
|
type: "object",
|
|
@@ -3279,8 +3462,8 @@ function registerFuturesAlgoTools() {
|
|
|
3279
3462
|
},
|
|
3280
3463
|
ordType: {
|
|
3281
3464
|
type: "string",
|
|
3282
|
-
enum: ["conditional", "oco", "move_order_stop"],
|
|
3283
|
-
description: "conditional=single TP/SL or both; oco=TP+SL pair; move_order_stop=trailing stop"
|
|
3465
|
+
enum: ["conditional", "oco", "move_order_stop", "trigger", "chase", "iceberg", "twap"],
|
|
3466
|
+
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"
|
|
3284
3467
|
},
|
|
3285
3468
|
sz: {
|
|
3286
3469
|
type: "string",
|
|
@@ -3294,11 +3477,8 @@ function registerFuturesAlgoTools() {
|
|
|
3294
3477
|
type: "string",
|
|
3295
3478
|
description: "TP order price; -1=market (conditional/oco only)"
|
|
3296
3479
|
},
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
enum: ["last", "index", "mark"],
|
|
3300
|
-
description: "last(default)|index|mark (conditional/oco only)"
|
|
3301
|
-
},
|
|
3480
|
+
tpOrdKind: TP_ORD_KIND_SCHEMA,
|
|
3481
|
+
tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
|
|
3302
3482
|
slTriggerPx: {
|
|
3303
3483
|
type: "string",
|
|
3304
3484
|
description: "SL trigger price (conditional/oco only)"
|
|
@@ -3307,11 +3487,9 @@ function registerFuturesAlgoTools() {
|
|
|
3307
3487
|
type: "string",
|
|
3308
3488
|
description: "SL order price; -1=market (conditional/oco only)"
|
|
3309
3489
|
},
|
|
3310
|
-
slTriggerPxType:
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
description: "last(default)|index|mark (conditional/oco only)"
|
|
3314
|
-
},
|
|
3490
|
+
slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
|
|
3491
|
+
stpMode: STP_MODE_SCHEMA,
|
|
3492
|
+
cxlOnClosePos: CXL_ON_CLOSE_POS_SCHEMA,
|
|
3315
3493
|
callbackRatio: {
|
|
3316
3494
|
type: "string",
|
|
3317
3495
|
description: "Callback ratio (e.g. '0.01'=1%); provide either ratio or spread (move_order_stop only)"
|
|
@@ -3324,6 +3502,9 @@ function registerFuturesAlgoTools() {
|
|
|
3324
3502
|
type: "string",
|
|
3325
3503
|
description: "Activation price; tracking starts after market reaches this level (move_order_stop only)"
|
|
3326
3504
|
},
|
|
3505
|
+
...TRIGGER_FLAGS_SCHEMA,
|
|
3506
|
+
...CHASE_FLAGS_SCHEMA,
|
|
3507
|
+
...ICEBERG_TWAP_FLAGS_SCHEMA,
|
|
3327
3508
|
tgtCcy: {
|
|
3328
3509
|
type: "string",
|
|
3329
3510
|
enum: ["base_ccy", "quote_ccy", "margin"],
|
|
@@ -3343,6 +3524,8 @@ function registerFuturesAlgoTools() {
|
|
|
3343
3524
|
handler: async (rawArgs, context) => {
|
|
3344
3525
|
const args = asRecord(rawArgs);
|
|
3345
3526
|
const reduceOnly = args.reduceOnly;
|
|
3527
|
+
const cxlOnClosePos = args.cxlOnClosePos;
|
|
3528
|
+
const ordType = requireString(args, "ordType");
|
|
3346
3529
|
const resolved = await resolveQuoteCcySz(
|
|
3347
3530
|
requireString(args, "instId"),
|
|
3348
3531
|
requireString(args, "sz"),
|
|
@@ -3351,29 +3534,44 @@ function registerFuturesAlgoTools() {
|
|
|
3351
3534
|
context.client,
|
|
3352
3535
|
readString(args, "tdMode")
|
|
3353
3536
|
);
|
|
3537
|
+
const base = compactObject({
|
|
3538
|
+
instId: requireString(args, "instId"),
|
|
3539
|
+
tdMode: requireString(args, "tdMode"),
|
|
3540
|
+
side: requireString(args, "side"),
|
|
3541
|
+
posSide: readString(args, "posSide"),
|
|
3542
|
+
ordType,
|
|
3543
|
+
sz: resolved.sz,
|
|
3544
|
+
tgtCcy: resolved.tgtCcy,
|
|
3545
|
+
stpMode: readString(args, "stpMode"),
|
|
3546
|
+
cxlOnClosePos: typeof cxlOnClosePos === "boolean" ? String(cxlOnClosePos) : void 0,
|
|
3547
|
+
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3548
|
+
clOrdId: readString(args, "clOrdId"),
|
|
3549
|
+
// Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
|
|
3550
|
+
pxAmendType: readString(args, "pxAmendType"),
|
|
3551
|
+
tag: context.config.sourceTag
|
|
3552
|
+
});
|
|
3553
|
+
switch (ordType) {
|
|
3554
|
+
case "trigger":
|
|
3555
|
+
Object.assign(base, buildTriggerOrdTypeBody(args));
|
|
3556
|
+
break;
|
|
3557
|
+
case "chase":
|
|
3558
|
+
Object.assign(base, buildChaseOrdTypeBody(args));
|
|
3559
|
+
break;
|
|
3560
|
+
case "iceberg":
|
|
3561
|
+
case "twap":
|
|
3562
|
+
Object.assign(base, buildIcebergTwapOrdTypeBody(args));
|
|
3563
|
+
break;
|
|
3564
|
+
default:
|
|
3565
|
+
Object.assign(base, compactObject({
|
|
3566
|
+
...buildAlgoConditionalCommonFields(args),
|
|
3567
|
+
callBackRatio: readString(args, "callbackRatio"),
|
|
3568
|
+
callBackSpread: readString(args, "callbackSpread")
|
|
3569
|
+
}));
|
|
3570
|
+
break;
|
|
3571
|
+
}
|
|
3354
3572
|
const response = await context.client.privatePost(
|
|
3355
3573
|
"/api/v5/trade/order-algo",
|
|
3356
|
-
|
|
3357
|
-
instId: requireString(args, "instId"),
|
|
3358
|
-
tdMode: requireString(args, "tdMode"),
|
|
3359
|
-
side: requireString(args, "side"),
|
|
3360
|
-
posSide: readString(args, "posSide"),
|
|
3361
|
-
ordType: requireString(args, "ordType"),
|
|
3362
|
-
sz: resolved.sz,
|
|
3363
|
-
tgtCcy: resolved.tgtCcy,
|
|
3364
|
-
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
3365
|
-
tpOrdPx: readString(args, "tpOrdPx"),
|
|
3366
|
-
tpTriggerPxType: readString(args, "tpTriggerPxType"),
|
|
3367
|
-
slTriggerPx: readString(args, "slTriggerPx"),
|
|
3368
|
-
slOrdPx: readString(args, "slOrdPx"),
|
|
3369
|
-
slTriggerPxType: readString(args, "slTriggerPxType"),
|
|
3370
|
-
callBackRatio: readString(args, "callbackRatio"),
|
|
3371
|
-
callBackSpread: readString(args, "callbackSpread"),
|
|
3372
|
-
activePx: readString(args, "activePx"),
|
|
3373
|
-
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
3374
|
-
clOrdId: readString(args, "clOrdId"),
|
|
3375
|
-
tag: context.config.sourceTag
|
|
3376
|
-
}),
|
|
3574
|
+
base,
|
|
3377
3575
|
privateRateLimit("futures_place_algo_order", 20)
|
|
3378
3576
|
);
|
|
3379
3577
|
const result = normalizeResponse(response);
|
|
@@ -5800,6 +5998,23 @@ function resolveOutcome(value) {
|
|
|
5800
5998
|
}
|
|
5801
5999
|
return resolved;
|
|
5802
6000
|
}
|
|
6001
|
+
async function withConcurrency(items, maxConcurrency, fn) {
|
|
6002
|
+
const results = new Array(items.length);
|
|
6003
|
+
let nextIndex = 0;
|
|
6004
|
+
async function runNext() {
|
|
6005
|
+
while (nextIndex < items.length) {
|
|
6006
|
+
const idx = nextIndex++;
|
|
6007
|
+
try {
|
|
6008
|
+
results[idx] = { status: "fulfilled", value: await fn(items[idx]) };
|
|
6009
|
+
} catch (err) {
|
|
6010
|
+
results[idx] = { status: "rejected", reason: err };
|
|
6011
|
+
}
|
|
6012
|
+
}
|
|
6013
|
+
}
|
|
6014
|
+
const workers = Array.from({ length: Math.min(maxConcurrency, items.length) }, runNext);
|
|
6015
|
+
await Promise.all(workers);
|
|
6016
|
+
return results;
|
|
6017
|
+
}
|
|
5803
6018
|
function filterBrowseCandidates(allSeries, underlyingFilter) {
|
|
5804
6019
|
const isHumanReadable = (id) => /^(BTC|ETH|TRX|EOS|SOL|IOTA|KISHU|SUSHI|BTG|XTZ|SOLVU)-/.test(id);
|
|
5805
6020
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5940,6 +6155,7 @@ function handlePlaceOrderError(base, rawArgs, endpoint) {
|
|
|
5940
6155
|
}
|
|
5941
6156
|
throw new OkxApiError(`[${sCode}] ${sMsg}`, { code: sCode, endpoint });
|
|
5942
6157
|
}
|
|
6158
|
+
var MAX_CONCURRENT_MARKET_FETCHES = 8;
|
|
5943
6159
|
var OUTCOME_SCHEMA = {
|
|
5944
6160
|
type: "string",
|
|
5945
6161
|
enum: ["UP", "YES", "DOWN", "NO"],
|
|
@@ -5979,10 +6195,15 @@ function registerEventContractTools() {
|
|
|
5979
6195
|
const normalizedSeries = normalizeResponse(seriesResp);
|
|
5980
6196
|
const allSeries = Array.isArray(normalizedSeries["data"]) ? normalizedSeries["data"] : [];
|
|
5981
6197
|
const candidates = filterBrowseCandidates(allSeries, underlyingFilter);
|
|
5982
|
-
const
|
|
5983
|
-
candidates
|
|
6198
|
+
const settled = await withConcurrency(
|
|
6199
|
+
candidates,
|
|
6200
|
+
MAX_CONCURRENT_MARKET_FETCHES,
|
|
6201
|
+
(s) => fetchActiveContractsForSeries(context.client, s)
|
|
5984
6202
|
);
|
|
5985
|
-
const
|
|
6203
|
+
const fulfilled = settled.filter(
|
|
6204
|
+
(r) => r.status === "fulfilled" && r.value !== null
|
|
6205
|
+
);
|
|
6206
|
+
const results = fulfilled.map((r) => r.value);
|
|
5986
6207
|
return {
|
|
5987
6208
|
data: results,
|
|
5988
6209
|
total: results.reduce((n, r) => n + (r?.contracts?.length ?? 0), 0)
|
|
@@ -6402,379 +6623,1106 @@ function registerEventContractTools() {
|
|
|
6402
6623
|
}
|
|
6403
6624
|
];
|
|
6404
6625
|
}
|
|
6626
|
+
var SMARTMONEY_RPS = 5;
|
|
6405
6627
|
var PATH_LEADERBOARD = "/api/v5/orbit/public/leaderboard";
|
|
6406
6628
|
var PATH_POSITION_CURRENT = "/api/v5/orbit/public/position-current";
|
|
6629
|
+
var PATH_POSITION_HISTORY = "/api/v5/orbit/public/position-history";
|
|
6407
6630
|
var PATH_TRADE_RECORDS = "/api/v5/orbit/public/trade-records";
|
|
6631
|
+
var PATH_TOP_TRADER_SEARCH = "/api/v5/orbit/top-trader-search";
|
|
6408
6632
|
var PATH_OVERVIEW = "/api/v5/journal/smartmoney/overview";
|
|
6409
|
-
var PATH_SIGNAL = "/api/v5/journal/smartmoney/signal";
|
|
6410
6633
|
var PATH_SIGNAL_HISTORY = "/api/v5/journal/smartmoney/signal-history";
|
|
6634
|
+
var PERIOD_DAYS = ["3", "7", "30", "90"];
|
|
6411
6635
|
var SIGNAL_POOL_FILTER_PROPS = {
|
|
6412
|
-
|
|
6636
|
+
sortBy: {
|
|
6413
6637
|
type: "string",
|
|
6414
|
-
|
|
6638
|
+
enum: ["pnl", "pnlRatio"],
|
|
6639
|
+
default: "pnl",
|
|
6640
|
+
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)."
|
|
6415
6641
|
},
|
|
6416
6642
|
period: {
|
|
6417
6643
|
type: "string",
|
|
6418
|
-
|
|
6644
|
+
enum: PERIOD_DAYS,
|
|
6645
|
+
default: "7",
|
|
6646
|
+
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).'
|
|
6419
6647
|
},
|
|
6420
|
-
|
|
6648
|
+
pnlTier: {
|
|
6421
6649
|
type: "string",
|
|
6422
|
-
|
|
6650
|
+
enum: ["PNL_ANY", "PNL_TOP50", "PNL_TOP20", "PNL_TOP5"],
|
|
6651
|
+
default: "PNL_ANY",
|
|
6652
|
+
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."
|
|
6423
6653
|
},
|
|
6424
|
-
|
|
6654
|
+
winRateTier: {
|
|
6425
6655
|
type: "string",
|
|
6426
|
-
|
|
6656
|
+
enum: ["WR_ANY", "WR_GE_50", "WR_GE_80"],
|
|
6657
|
+
default: "WR_ANY",
|
|
6658
|
+
description: "Minimum win-rate gate (fixed thresholds). ANY = no filter; WR_GE_50 = \u2265 50%; WR_GE_80 = \u2265 80%."
|
|
6427
6659
|
},
|
|
6428
|
-
|
|
6660
|
+
maxDrawdownTier: {
|
|
6429
6661
|
type: "string",
|
|
6430
|
-
|
|
6662
|
+
enum: ["MR_ANY", "MR_LE_20", "MR_LE_50"],
|
|
6663
|
+
default: "MR_ANY",
|
|
6664
|
+
description: "Maximum-drawdown gate (fixed thresholds; smaller drawdown = lower risk). ANY = no filter; MR_LE_20 = drawdown \u2264 20%; MR_LE_50 = \u2264 50%."
|
|
6431
6665
|
},
|
|
6432
|
-
|
|
6666
|
+
aumTier: {
|
|
6433
6667
|
type: "string",
|
|
6434
|
-
|
|
6668
|
+
enum: ["AUM_ANY", "AUM_TOP50", "AUM_TOP20", "AUM_TOP5"],
|
|
6669
|
+
default: "AUM_ANY",
|
|
6670
|
+
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."
|
|
6435
6671
|
}
|
|
6436
6672
|
};
|
|
6437
6673
|
var LEADERBOARD_POOL_FILTER_PROPS = {
|
|
6438
|
-
|
|
6674
|
+
sortBy: {
|
|
6439
6675
|
type: "string",
|
|
6440
|
-
|
|
6676
|
+
enum: ["pnl", "pnlRatio"],
|
|
6677
|
+
default: "pnl",
|
|
6678
|
+
description: "Leaderboard sort key. `pnl` = absolute USD profit; `pnlRatio` = percentage return."
|
|
6441
6679
|
},
|
|
6442
6680
|
period: {
|
|
6443
6681
|
type: "string",
|
|
6444
|
-
|
|
6682
|
+
enum: PERIOD_DAYS,
|
|
6683
|
+
default: "90",
|
|
6684
|
+
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.'
|
|
6445
6685
|
},
|
|
6446
|
-
|
|
6686
|
+
minPnl: {
|
|
6447
6687
|
type: "string",
|
|
6448
|
-
description:
|
|
6688
|
+
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.'
|
|
6449
6689
|
},
|
|
6450
|
-
|
|
6690
|
+
minWinRate: {
|
|
6451
6691
|
type: "string",
|
|
6452
|
-
description: "
|
|
6692
|
+
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.'
|
|
6453
6693
|
},
|
|
6454
|
-
|
|
6694
|
+
maxDrawdown: {
|
|
6455
6695
|
type: "string",
|
|
6456
|
-
description: "
|
|
6696
|
+
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.'
|
|
6457
6697
|
},
|
|
6458
|
-
|
|
6698
|
+
minAum: {
|
|
6459
6699
|
type: "string",
|
|
6460
|
-
description:
|
|
6700
|
+
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.'
|
|
6461
6701
|
}
|
|
6462
6702
|
};
|
|
6463
|
-
var
|
|
6703
|
+
var LEADERBOARD_FILTER_UPSTREAM_NAMES = {
|
|
6704
|
+
sortBy: "sortBy",
|
|
6705
|
+
period: "period",
|
|
6706
|
+
minPnl: "pnl",
|
|
6707
|
+
minWinRate: "winRate",
|
|
6708
|
+
maxDrawdown: "maxDrawdown",
|
|
6709
|
+
minAum: "asset"
|
|
6710
|
+
};
|
|
6464
6711
|
function readPoolFilters(args) {
|
|
6465
6712
|
const result = {};
|
|
6466
|
-
for (const
|
|
6713
|
+
for (const [publicKey, upstreamKey] of Object.entries(LEADERBOARD_FILTER_UPSTREAM_NAMES)) {
|
|
6714
|
+
const val = readString(args, publicKey);
|
|
6715
|
+
if (val !== void 0 && val !== "") result[upstreamKey] = val;
|
|
6716
|
+
}
|
|
6717
|
+
return result;
|
|
6718
|
+
}
|
|
6719
|
+
function readSignalPoolFilters(args) {
|
|
6720
|
+
const result = {};
|
|
6721
|
+
for (const key of Object.keys(SIGNAL_POOL_FILTER_PROPS)) {
|
|
6467
6722
|
const val = readString(args, key);
|
|
6468
6723
|
if (val) result[key] = val;
|
|
6469
6724
|
}
|
|
6470
6725
|
return result;
|
|
6471
6726
|
}
|
|
6472
|
-
function
|
|
6473
|
-
if (Array.isArray(data)) return data;
|
|
6727
|
+
function extractLeaderboardEnvelope(data) {
|
|
6728
|
+
if (Array.isArray(data)) return { items: data };
|
|
6474
6729
|
if (data && typeof data === "object") {
|
|
6475
|
-
const
|
|
6476
|
-
|
|
6730
|
+
const obj = data;
|
|
6731
|
+
const inner = obj.data;
|
|
6732
|
+
const updateTime = typeof obj.updateTime === "string" ? obj.updateTime : void 0;
|
|
6733
|
+
if (Array.isArray(inner)) return { items: inner, updateTime };
|
|
6734
|
+
}
|
|
6735
|
+
return { items: [] };
|
|
6736
|
+
}
|
|
6737
|
+
function deriveDirection(posSide, pos) {
|
|
6738
|
+
if (posSide === "long") return "long";
|
|
6739
|
+
if (posSide === "short") return "short";
|
|
6740
|
+
if (posSide === "both" && typeof pos === "string" && pos !== "") {
|
|
6741
|
+
const n = Number(pos);
|
|
6742
|
+
if (Number.isFinite(n) && n !== 0) return n > 0 ? "long" : "short";
|
|
6743
|
+
}
|
|
6744
|
+
return void 0;
|
|
6745
|
+
}
|
|
6746
|
+
function extractPositionData(data) {
|
|
6747
|
+
if (!Array.isArray(data) || data.length === 0) return [];
|
|
6748
|
+
const first = data[0];
|
|
6749
|
+
if (first && typeof first === "object") {
|
|
6750
|
+
const posData = first.posData;
|
|
6751
|
+
if (Array.isArray(posData)) {
|
|
6752
|
+
return posData.map((row) => {
|
|
6753
|
+
if (!row || typeof row !== "object") return row;
|
|
6754
|
+
const r = row;
|
|
6755
|
+
const direction = deriveDirection(r.posSide, r.pos);
|
|
6756
|
+
return direction ? { ...r, direction } : r;
|
|
6757
|
+
});
|
|
6758
|
+
}
|
|
6477
6759
|
}
|
|
6478
6760
|
return [];
|
|
6479
6761
|
}
|
|
6762
|
+
function buildPagination(data, effectiveLimit, cursorField) {
|
|
6763
|
+
const hasMore = data.length >= effectiveLimit;
|
|
6764
|
+
const last = data.length > 0 ? data[data.length - 1] : void 0;
|
|
6765
|
+
const nextAfter = hasMore && last && typeof last === "object" && last !== null ? last[cursorField] : void 0;
|
|
6766
|
+
return nextAfter !== void 0 ? { hasMore, nextAfter } : { hasMore };
|
|
6767
|
+
}
|
|
6768
|
+
function extractBaseCcy(instId) {
|
|
6769
|
+
if (!instId) return void 0;
|
|
6770
|
+
const idx = instId.indexOf("-");
|
|
6771
|
+
const base = idx === -1 ? instId : instId.slice(0, idx);
|
|
6772
|
+
return base || void 0;
|
|
6773
|
+
}
|
|
6774
|
+
function actionableError(message, hint) {
|
|
6775
|
+
return new ValidationError(`${message} ${hint}`);
|
|
6776
|
+
}
|
|
6777
|
+
function readArrayAsCsv(args, key) {
|
|
6778
|
+
const arr = readStringArray(args, key);
|
|
6779
|
+
if (!arr || arr.length === 0) return void 0;
|
|
6780
|
+
return arr.join(",");
|
|
6781
|
+
}
|
|
6782
|
+
function envelope(dataSchema, extras = {}) {
|
|
6783
|
+
return {
|
|
6784
|
+
type: "object",
|
|
6785
|
+
properties: {
|
|
6786
|
+
endpoint: {
|
|
6787
|
+
type: "string",
|
|
6788
|
+
description: "Upstream API path that produced this payload (debug/audit aid)."
|
|
6789
|
+
},
|
|
6790
|
+
requestTime: { type: "string", description: "ISO-8601 timestamp when the request was issued." },
|
|
6791
|
+
data: dataSchema,
|
|
6792
|
+
...extras
|
|
6793
|
+
},
|
|
6794
|
+
required: ["endpoint", "data"]
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
var PAGINATION_PROP = {
|
|
6798
|
+
type: "object",
|
|
6799
|
+
description: "Cursor pagination metadata. Only emitted on list tools that support paging.",
|
|
6800
|
+
properties: {
|
|
6801
|
+
hasMore: {
|
|
6802
|
+
type: "boolean",
|
|
6803
|
+
description: "True when `data.length` reached the requested `limit` (more pages likely). False guarantees there are no more results after this page."
|
|
6804
|
+
},
|
|
6805
|
+
nextAfter: {
|
|
6806
|
+
type: "string",
|
|
6807
|
+
description: "Cursor to pass back as `after` on the next call to fetch the following page. Absent/empty when `hasMore` is false."
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
};
|
|
6811
|
+
var TRADER_ITEM_PROPS = {
|
|
6812
|
+
authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other smartmoney_get_trader_* tools." },
|
|
6813
|
+
nickName: { type: "string", description: "Display nickname." },
|
|
6814
|
+
pnl: { type: "string", description: "Absolute PnL in USD over the requested `period` (numeric string)." },
|
|
6815
|
+
pnlRatio: { type: "string", description: 'PnL as a decimal ratio over the requested `period` (e.g. "0.35" = +35%).' },
|
|
6816
|
+
asset: { type: "string", description: "AUM (Assets Under Management) in USD \u2014 same field that the input `minAum` filter applies to." },
|
|
6817
|
+
winRate: { type: "string", description: "Lifetime win-rate as a decimal (0~1)." },
|
|
6818
|
+
maxDrawdown: { type: "string", description: 'Max drawdown as a decimal (e.g. "0.2" = 20%). Lower = lower risk.' },
|
|
6819
|
+
onboardDuration: { type: "string", description: "Days the trader has been onboarded on the leaderboard (numeric string)." },
|
|
6820
|
+
portrait: { type: "string", description: "Avatar image URL." },
|
|
6821
|
+
rates: {
|
|
6822
|
+
type: "array",
|
|
6823
|
+
description: "Equity-curve / PnL-rate time series (one entry per day).",
|
|
6824
|
+
items: {
|
|
6825
|
+
type: "object",
|
|
6826
|
+
properties: {
|
|
6827
|
+
statTime: { type: "string", description: 'Date stamp in `YYMMDD` (e.g. "240726" = 2024-07-26).' },
|
|
6828
|
+
value: { type: "string", description: "Cumulative PnL ratio at that day (decimal)." }
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
};
|
|
6833
|
+
var SIGNAL_ITEM_PROPS = {
|
|
6834
|
+
ccy: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP (outer identifier; the field name is `ccy`, not `instId`)." },
|
|
6835
|
+
dataVersion: {
|
|
6836
|
+
type: "string",
|
|
6837
|
+
description: "Snapshot version key in `yyyyMMddHH` UTC (10 digits, e.g. `2026043014` = 2026-04-30 14:00 UTC; floored to the hour)."
|
|
6838
|
+
},
|
|
6839
|
+
tradersWithPosition: {
|
|
6840
|
+
type: "integer",
|
|
6841
|
+
description: "Pool traders holding this asset at latest_snap (double-sided counted once). Higher = stronger consensus."
|
|
6842
|
+
},
|
|
6843
|
+
tradersQualified: {
|
|
6844
|
+
type: "integer",
|
|
6845
|
+
description: "Pool size after applying tier filters (incl. those without positions on this instrument)."
|
|
6846
|
+
},
|
|
6847
|
+
longTraders: { type: "integer", description: "Number of pool traders currently long this asset (incl. double-sided)." },
|
|
6848
|
+
shortTraders: { type: "integer", description: "Number of pool traders currently short this asset (incl. double-sided)." },
|
|
6849
|
+
notional: {
|
|
6850
|
+
type: "object",
|
|
6851
|
+
description: "Notional / capital-flow group.",
|
|
6852
|
+
properties: {
|
|
6853
|
+
longNotionalUsdt: { type: "string", description: "Sum of long-side notional in USDT." },
|
|
6854
|
+
shortNotionalUsdt: { type: "string", description: "Sum of short-side notional in USDT." },
|
|
6855
|
+
netNotionalUsdt: { type: "string", description: "Net directional notional in USDT = long \u2212 short." },
|
|
6856
|
+
totalNotionalUsdt: { type: "string", description: "Gross notional in USDT = long + short." },
|
|
6857
|
+
totalNotionalVs24h: {
|
|
6858
|
+
type: "string",
|
|
6859
|
+
description: "Capital-flow change ratio vs 24h: (curr \u2212 hist_24h) / hist_24h. Positive = smart money adding exposure; negative = retreating. NULL when hist=0."
|
|
6860
|
+
},
|
|
6861
|
+
smartMoneyLongAvgEntry: {
|
|
6862
|
+
type: "string",
|
|
6863
|
+
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."
|
|
6864
|
+
},
|
|
6865
|
+
smartMoneyShortAvgEntry: {
|
|
6866
|
+
type: "string",
|
|
6867
|
+
description: "Short-side notional-weighted average entry price (USDT). NULL when no short."
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
},
|
|
6871
|
+
longShortRatio: {
|
|
6872
|
+
type: "object",
|
|
6873
|
+
description: "Long/short ratio + historical-delta group.",
|
|
6874
|
+
properties: {
|
|
6875
|
+
longRatioVs1h: { type: "string", description: "longRatio \u2212 hist_1h.longRatio. NULL when no hist." },
|
|
6876
|
+
longRatioVs24h: { type: "string", description: "longRatio \u2212 hist_24h.longRatio. NULL when no hist." },
|
|
6877
|
+
longRatioVs7d: { type: "string", description: "longRatio \u2212 hist_7d.longRatio. NULL when no hist." },
|
|
6878
|
+
longRatio: { type: "string", description: "Headcount long ratio = longTraders / tradersWithPosition. NULL when no traders." },
|
|
6879
|
+
shortRatio: { type: "string", description: "1 \u2212 longRatio. NULL when no traders." },
|
|
6880
|
+
weightedLongRatio: {
|
|
6881
|
+
type: "string",
|
|
6882
|
+
description: "Notional-weighted long ratio = \u03A3(long_notional) / \u03A3(notional). NULL when no notional."
|
|
6883
|
+
},
|
|
6884
|
+
weightedShortRatio: {
|
|
6885
|
+
type: "string",
|
|
6886
|
+
description: "Notional-weighted short ratio = \u03A3(short_notional) / \u03A3(notional). NULL when no notional."
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
},
|
|
6890
|
+
winRate: {
|
|
6891
|
+
type: "object",
|
|
6892
|
+
description: "Capability (historical performance) group; driven by the `period` window.",
|
|
6893
|
+
properties: {
|
|
6894
|
+
avgLongWinRate: {
|
|
6895
|
+
type: "string",
|
|
6896
|
+
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."
|
|
6897
|
+
},
|
|
6898
|
+
avgShortWinRate: {
|
|
6899
|
+
type: "string",
|
|
6900
|
+
description: "Mean closed-position win-rate (full-market) over `period` days for users currently short. NULL when sample is below threshold."
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
}
|
|
6904
|
+
};
|
|
6905
|
+
var SIGNAL_HISTORY_ITEM_PROPS = {
|
|
6906
|
+
ccy: { type: "string", description: "Base currency / instrument key for this bucket." },
|
|
6907
|
+
longRatio: {
|
|
6908
|
+
type: "string",
|
|
6909
|
+
description: "Headcount long ratio at this bucket. Decimal 0~1."
|
|
6910
|
+
},
|
|
6911
|
+
shortRatio: {
|
|
6912
|
+
type: "string",
|
|
6913
|
+
description: "Headcount short ratio at this bucket = 1 \u2212 longRatio. Decimal 0~1."
|
|
6914
|
+
},
|
|
6915
|
+
weightedLongRatio: {
|
|
6916
|
+
type: "string",
|
|
6917
|
+
description: "Notional-weighted long ratio at this bucket. Decimal 0~1."
|
|
6918
|
+
},
|
|
6919
|
+
weightedShortRatio: {
|
|
6920
|
+
type: "string",
|
|
6921
|
+
description: "Notional-weighted short ratio at this bucket. Decimal 0~1."
|
|
6922
|
+
},
|
|
6923
|
+
longTraders: {
|
|
6924
|
+
type: "integer",
|
|
6925
|
+
description: "Number of traders with long exposure at this bucket (includes dual-side traders)."
|
|
6926
|
+
},
|
|
6927
|
+
shortTraders: {
|
|
6928
|
+
type: "integer",
|
|
6929
|
+
description: "Number of traders with short exposure at this bucket (includes dual-side traders)."
|
|
6930
|
+
},
|
|
6931
|
+
tradersWithPosition: {
|
|
6932
|
+
type: "integer",
|
|
6933
|
+
description: "Pool traders holding this asset at this bucket. Few traders = unreliable signal."
|
|
6934
|
+
},
|
|
6935
|
+
netNotionalUsdt: {
|
|
6936
|
+
type: "string",
|
|
6937
|
+
description: "Net directional notional in USDT at this bucket = long notional \u2212 short notional."
|
|
6938
|
+
},
|
|
6939
|
+
totalNotionalUsdt: {
|
|
6940
|
+
type: "string",
|
|
6941
|
+
description: "Gross notional in USDT at this bucket = long notional + short notional. Tracks total capital deployed (rising = adding, falling = retreating)."
|
|
6942
|
+
},
|
|
6943
|
+
tradersQualified: { type: "integer", description: "Pool size after applying tier filters (includes traders without a position)." },
|
|
6944
|
+
dataVersion: { type: "string", description: "Snapshot version key in `yyyyMMddHH` UTC (10-digit, e.g. `2026042820`)." }
|
|
6945
|
+
};
|
|
6480
6946
|
function registerSmartmoneyTools() {
|
|
6481
6947
|
const tools = [
|
|
6482
|
-
/*
|
|
6948
|
+
/* ===================================================== */
|
|
6949
|
+
/* Trader family (6) */
|
|
6950
|
+
/* ===================================================== */
|
|
6951
|
+
/* ---------- T1. Top traders (leaderboard rank) ---------- */
|
|
6483
6952
|
{
|
|
6484
|
-
name: "
|
|
6953
|
+
name: "smartmoney_get_traders_by_filter",
|
|
6485
6954
|
module: "smartmoney",
|
|
6486
|
-
description: "
|
|
6955
|
+
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.",
|
|
6487
6956
|
isWrite: false,
|
|
6957
|
+
outputSchema: envelope(
|
|
6958
|
+
{ type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
|
|
6959
|
+
{
|
|
6960
|
+
updateTime: {
|
|
6961
|
+
type: "string",
|
|
6962
|
+
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."
|
|
6963
|
+
},
|
|
6964
|
+
pagination: PAGINATION_PROP
|
|
6965
|
+
}
|
|
6966
|
+
),
|
|
6488
6967
|
inputSchema: {
|
|
6489
6968
|
type: "object",
|
|
6490
6969
|
properties: {
|
|
6491
|
-
|
|
6492
|
-
type: "string",
|
|
6493
|
-
description: "Recommended. Timestamp ms \u2014 use Date.now() for latest."
|
|
6494
|
-
},
|
|
6495
|
-
dataVersion: {
|
|
6496
|
-
type: "string",
|
|
6497
|
-
description: "Alternative. yyyyMMddHHmm UTC for prior snapshot. If both sent, ts wins."
|
|
6498
|
-
},
|
|
6499
|
-
instType: {
|
|
6500
|
-
type: "string",
|
|
6501
|
-
description: "SPOT|MARGIN|FUTURES|SWAP|OPTION (default SWAP)"
|
|
6502
|
-
},
|
|
6503
|
-
...SIGNAL_POOL_FILTER_PROPS,
|
|
6504
|
-
lmtNum: {
|
|
6970
|
+
updateTime: {
|
|
6505
6971
|
type: "string",
|
|
6506
|
-
description:
|
|
6972
|
+
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).'
|
|
6507
6973
|
},
|
|
6508
|
-
|
|
6974
|
+
...LEADERBOARD_POOL_FILTER_PROPS,
|
|
6975
|
+
after: {
|
|
6509
6976
|
type: "string",
|
|
6510
|
-
description: "
|
|
6977
|
+
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."
|
|
6511
6978
|
},
|
|
6512
|
-
|
|
6979
|
+
before: {
|
|
6513
6980
|
type: "string",
|
|
6514
|
-
description: "
|
|
6981
|
+
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."
|
|
6515
6982
|
},
|
|
6516
|
-
|
|
6517
|
-
type: "
|
|
6518
|
-
|
|
6983
|
+
limit: {
|
|
6984
|
+
type: "integer",
|
|
6985
|
+
minimum: 1,
|
|
6986
|
+
maximum: 100,
|
|
6987
|
+
default: 10,
|
|
6988
|
+
description: "Max results per page (default 10, max 100)."
|
|
6519
6989
|
}
|
|
6520
6990
|
}
|
|
6521
6991
|
},
|
|
6522
6992
|
handler: async (rawArgs, context) => {
|
|
6523
6993
|
const args = asRecord(rawArgs);
|
|
6524
|
-
const
|
|
6525
|
-
const ts = readString(args, "ts");
|
|
6526
|
-
if (!dv && !ts) {
|
|
6527
|
-
throw new ValidationError('Either "dataVersion" or "ts" is required for smartmoney_get_overview.');
|
|
6528
|
-
}
|
|
6994
|
+
const limit = readNumber(args, "limit");
|
|
6529
6995
|
const response = await context.client.privateGet(
|
|
6530
|
-
|
|
6996
|
+
PATH_LEADERBOARD,
|
|
6531
6997
|
compactObject({
|
|
6532
|
-
|
|
6533
|
-
ts,
|
|
6534
|
-
instType: readString(args, "instType"),
|
|
6998
|
+
updateTime: readString(args, "updateTime"),
|
|
6535
6999
|
...readPoolFilters(args),
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
topInstruments: readString(args, "topInstruments")
|
|
7000
|
+
after: readString(args, "after"),
|
|
7001
|
+
before: readString(args, "before"),
|
|
7002
|
+
limit
|
|
6540
7003
|
}),
|
|
6541
|
-
publicRateLimit("
|
|
7004
|
+
publicRateLimit("smartmoney_get_traders_by_filter", SMARTMONEY_RPS)
|
|
6542
7005
|
);
|
|
6543
|
-
|
|
7006
|
+
const normalized = normalizeResponse(response);
|
|
7007
|
+
const { items, updateTime } = extractLeaderboardEnvelope(normalized.data);
|
|
7008
|
+
return {
|
|
7009
|
+
...normalized,
|
|
7010
|
+
data: items,
|
|
7011
|
+
...updateTime ? { updateTime } : {},
|
|
7012
|
+
pagination: buildPagination(items, limit ?? 10, "authorId")
|
|
7013
|
+
};
|
|
6544
7014
|
}
|
|
6545
7015
|
},
|
|
6546
|
-
/* ----------
|
|
7016
|
+
/* ---------- T2. Trader performance (by authorIds) ---------- */
|
|
6547
7017
|
{
|
|
6548
|
-
name: "
|
|
7018
|
+
name: "smartmoney_get_performance_by_trader",
|
|
6549
7019
|
module: "smartmoney",
|
|
6550
|
-
description: "
|
|
7020
|
+
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).",
|
|
6551
7021
|
isWrite: false,
|
|
7022
|
+
outputSchema: envelope(
|
|
7023
|
+
{ type: "array", items: { type: "object", properties: TRADER_ITEM_PROPS } },
|
|
7024
|
+
{
|
|
7025
|
+
updateTime: {
|
|
7026
|
+
type: "string",
|
|
7027
|
+
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."
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
),
|
|
6552
7031
|
inputSchema: {
|
|
6553
7032
|
type: "object",
|
|
6554
7033
|
properties: {
|
|
6555
|
-
instId: {
|
|
6556
|
-
type: "string",
|
|
6557
|
-
description: "Recommended. e.g. BTC-USDT-SWAP"
|
|
6558
|
-
},
|
|
6559
|
-
instCcy: {
|
|
6560
|
-
type: "string",
|
|
6561
|
-
description: "e.g. BTC (SPOT/SWAP only); instId takes precedence if both set"
|
|
6562
|
-
},
|
|
6563
|
-
ts: {
|
|
6564
|
-
type: "string",
|
|
6565
|
-
description: "Recommended. Timestamp ms \u2014 use Date.now() for latest."
|
|
6566
|
-
},
|
|
6567
|
-
dataVersion: {
|
|
6568
|
-
type: "string",
|
|
6569
|
-
description: "Alternative. yyyyMMddHHmm UTC for prior snapshot. If both sent, ts wins."
|
|
6570
|
-
},
|
|
6571
|
-
...SIGNAL_POOL_FILTER_PROPS,
|
|
6572
|
-
lmtNum: {
|
|
6573
|
-
type: "string",
|
|
6574
|
-
description: "Trader pool size 1-500 (default 100)"
|
|
6575
|
-
},
|
|
6576
7034
|
authorIds: {
|
|
7035
|
+
type: "array",
|
|
7036
|
+
items: { type: "string" },
|
|
7037
|
+
minItems: 1,
|
|
7038
|
+
description: 'Trader IDs to look up, e.g. `["1001", "1002"]`. Required.'
|
|
7039
|
+
},
|
|
7040
|
+
period: {
|
|
6577
7041
|
type: "string",
|
|
6578
|
-
|
|
7042
|
+
enum: PERIOD_DAYS,
|
|
7043
|
+
default: "90",
|
|
7044
|
+
description: 'Performance lookback window in days. Pass as a quoted string: `"3"` / `"7"` / `"30"` / `"90"` (NOT integer). Default `"90"`.'
|
|
6579
7045
|
}
|
|
6580
|
-
}
|
|
7046
|
+
},
|
|
7047
|
+
required: ["authorIds"]
|
|
6581
7048
|
},
|
|
6582
7049
|
handler: async (rawArgs, context) => {
|
|
6583
7050
|
const args = asRecord(rawArgs);
|
|
6584
|
-
const
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
const ts = readString(args, "ts");
|
|
6591
|
-
if (!dv && !ts) {
|
|
6592
|
-
throw new ValidationError('Either "dataVersion" or "ts" is required for smartmoney_get_signal.');
|
|
7051
|
+
const authorIds = readArrayAsCsv(args, "authorIds");
|
|
7052
|
+
if (!authorIds) {
|
|
7053
|
+
throw actionableError(
|
|
7054
|
+
'"authorIds" is required and must be a non-empty array.',
|
|
7055
|
+
'Discover trader IDs via `smartmoney_get_traders_by_filter`, then pass them as an array (e.g. ["1001", "1002"]).'
|
|
7056
|
+
);
|
|
6593
7057
|
}
|
|
6594
7058
|
const response = await context.client.privateGet(
|
|
6595
|
-
|
|
7059
|
+
PATH_LEADERBOARD,
|
|
6596
7060
|
compactObject({
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
dataVersion: dv,
|
|
6600
|
-
ts,
|
|
6601
|
-
...readPoolFilters(args),
|
|
6602
|
-
lmtNum: readString(args, "lmtNum"),
|
|
6603
|
-
authorIds: readString(args, "authorIds")
|
|
7061
|
+
authorIds,
|
|
7062
|
+
period: readString(args, "period")
|
|
6604
7063
|
}),
|
|
6605
|
-
publicRateLimit("
|
|
7064
|
+
publicRateLimit("smartmoney_get_performance_by_trader", SMARTMONEY_RPS)
|
|
6606
7065
|
);
|
|
6607
|
-
|
|
7066
|
+
const normalized = normalizeResponse(response);
|
|
7067
|
+
const { items, updateTime } = extractLeaderboardEnvelope(normalized.data);
|
|
7068
|
+
return {
|
|
7069
|
+
...normalized,
|
|
7070
|
+
data: items,
|
|
7071
|
+
...updateTime ? { updateTime } : {}
|
|
7072
|
+
};
|
|
6608
7073
|
}
|
|
6609
7074
|
},
|
|
6610
|
-
/* ----------
|
|
7075
|
+
/* ---------- T3. Trader current positions ---------- */
|
|
6611
7076
|
{
|
|
6612
|
-
name: "
|
|
7077
|
+
name: "smartmoney_get_trader_positions",
|
|
6613
7078
|
module: "smartmoney",
|
|
6614
|
-
description: "
|
|
7079
|
+
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).",
|
|
6615
7080
|
isWrite: false,
|
|
7081
|
+
outputSchema: envelope({
|
|
7082
|
+
type: "array",
|
|
7083
|
+
items: {
|
|
7084
|
+
type: "object",
|
|
7085
|
+
properties: {
|
|
7086
|
+
posId: { type: "string", description: "Unique position ID. Stable across the position's lifetime." },
|
|
7087
|
+
instId: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP." },
|
|
7088
|
+
instType: {
|
|
7089
|
+
type: "string",
|
|
7090
|
+
description: "Instrument business line: `SWAP` (perpetual) | `SPOT` | `FUTURES` (delivery) | `MARGIN` | `OPTION`."
|
|
7091
|
+
},
|
|
7092
|
+
posSide: {
|
|
7093
|
+
type: "string",
|
|
7094
|
+
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."
|
|
7095
|
+
},
|
|
7096
|
+
direction: {
|
|
7097
|
+
type: "string",
|
|
7098
|
+
enum: ["long", "short"],
|
|
7099
|
+
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.'
|
|
7100
|
+
},
|
|
7101
|
+
posCcy: { type: "string", description: 'Position currency \u2014 the asset being held, e.g. "BTC".' },
|
|
7102
|
+
quoteCcy: { type: "string", description: 'Quote currency the position is priced/settled in, e.g. "USDT".' },
|
|
7103
|
+
pos: {
|
|
7104
|
+
type: "string",
|
|
7105
|
+
description: "Position size (numeric string). Unit depends on instType: coins for SPOT/MARGIN, contracts (\u5F20) for SWAP/FUTURES/OPTION."
|
|
7106
|
+
},
|
|
7107
|
+
lever: { type: "string", description: 'Leverage multiplier (numeric string; "1" for spot).' },
|
|
7108
|
+
avgPx: { type: "string", description: "Volume-weighted average entry price (numeric string)." },
|
|
7109
|
+
last: { type: "string", description: "Latest market/mark price for the instrument (numeric string)." },
|
|
7110
|
+
notionalUsd: { type: "string", description: "Current position notional value in USD." },
|
|
7111
|
+
upl: { type: "string", description: "Unrealized (floating) PnL, denominated in `quoteCcy`." },
|
|
7112
|
+
pnl: { type: "string", description: "Realized PnL accrued on this position so far, denominated in `quoteCcy`." },
|
|
7113
|
+
cTime: { type: "string", description: "Position open time as Unix milliseconds (numeric string)." },
|
|
7114
|
+
positionIntensity: {
|
|
7115
|
+
type: "string",
|
|
7116
|
+
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."
|
|
7117
|
+
}
|
|
7118
|
+
}
|
|
7119
|
+
}
|
|
7120
|
+
}),
|
|
6616
7121
|
inputSchema: {
|
|
6617
7122
|
type: "object",
|
|
6618
7123
|
properties: {
|
|
6619
|
-
|
|
7124
|
+
authorId: {
|
|
6620
7125
|
type: "string",
|
|
6621
|
-
description: "
|
|
7126
|
+
description: "Trader's unique ID (obtain from `smartmoney_get_traders_by_filter`)."
|
|
6622
7127
|
},
|
|
6623
|
-
|
|
7128
|
+
instId: {
|
|
7129
|
+
type: "string",
|
|
7130
|
+
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.'
|
|
7131
|
+
}
|
|
7132
|
+
},
|
|
7133
|
+
required: ["authorId"]
|
|
7134
|
+
},
|
|
7135
|
+
handler: async (rawArgs, context) => {
|
|
7136
|
+
const args = asRecord(rawArgs);
|
|
7137
|
+
const authorId = readString(args, "authorId");
|
|
7138
|
+
if (!authorId) {
|
|
7139
|
+
throw actionableError(
|
|
7140
|
+
'"authorId" is required.',
|
|
7141
|
+
"Discover trader IDs via `smartmoney_get_traders_by_filter` and pass one ID here."
|
|
7142
|
+
);
|
|
7143
|
+
}
|
|
7144
|
+
const response = await context.client.privateGet(
|
|
7145
|
+
PATH_POSITION_CURRENT,
|
|
7146
|
+
compactObject({
|
|
7147
|
+
authorId,
|
|
7148
|
+
instCcy: extractBaseCcy(readString(args, "instId"))
|
|
7149
|
+
}),
|
|
7150
|
+
publicRateLimit("smartmoney_get_trader_positions", SMARTMONEY_RPS)
|
|
7151
|
+
);
|
|
7152
|
+
const normalized = normalizeResponse(response);
|
|
7153
|
+
return { ...normalized, data: extractPositionData(normalized.data) };
|
|
7154
|
+
}
|
|
7155
|
+
},
|
|
7156
|
+
/* ---------- T4. Trader closed-position history ---------- */
|
|
7157
|
+
{
|
|
7158
|
+
name: "smartmoney_get_trader_positions_history",
|
|
7159
|
+
module: "smartmoney",
|
|
7160
|
+
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).",
|
|
7161
|
+
isWrite: false,
|
|
7162
|
+
outputSchema: envelope(
|
|
7163
|
+
{
|
|
7164
|
+
type: "array",
|
|
7165
|
+
items: {
|
|
7166
|
+
type: "object",
|
|
7167
|
+
properties: {
|
|
7168
|
+
posId: { type: "string", description: "Unique closed-position ID. Use as the `after` / `before` cursor when paginating." },
|
|
7169
|
+
instId: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP." },
|
|
7170
|
+
instType: {
|
|
7171
|
+
type: "string",
|
|
7172
|
+
description: "Instrument business line: `SWAP` (perpetual) | `FUTURES` (delivery) | `MARGIN` | `SPOT`."
|
|
7173
|
+
},
|
|
7174
|
+
ctVal: {
|
|
7175
|
+
type: "string",
|
|
7176
|
+
description: "Contract face value \u2014 USD value of a single contract (\u5F20). Numeric string. Empty/0 for non-contract instruments."
|
|
7177
|
+
},
|
|
7178
|
+
posSide: {
|
|
7179
|
+
type: "string",
|
|
7180
|
+
description: "Position direction at close. `long` = was long-side; `short` = was short-side."
|
|
7181
|
+
},
|
|
7182
|
+
lever: { type: "string", description: "Leverage multiplier used for this position (numeric string)." },
|
|
7183
|
+
mgnMode: {
|
|
7184
|
+
type: "string",
|
|
7185
|
+
description: "Margin mode used for this position. `cross` = cross-margin (shared collateral pool); `isolated` = isolated-margin (per-position collateral)."
|
|
7186
|
+
},
|
|
7187
|
+
marginCcy: {
|
|
7188
|
+
type: "string",
|
|
7189
|
+
description: 'Margin currency held as collateral for this position, e.g. "BTC" or "USDT".'
|
|
7190
|
+
},
|
|
7191
|
+
quoteCcy: { type: "string", description: 'Quote currency the position settled in, e.g. "USDT".' },
|
|
7192
|
+
openAvgPx: { type: "string", description: "Volume-weighted average price across all open fills (numeric string)." },
|
|
7193
|
+
closeAvgPx: { type: "string", description: "Volume-weighted average price across all close fills (numeric string)." },
|
|
7194
|
+
openMaxAmount: {
|
|
7195
|
+
type: "string",
|
|
7196
|
+
description: "Peak position size held during the position's lifetime, in contracts (\u5F20)."
|
|
7197
|
+
},
|
|
7198
|
+
closeAmount: {
|
|
7199
|
+
type: "string",
|
|
7200
|
+
description: "Total amount closed across all close fills, in contracts for SWAP/FUTURES or in base currency for SPOT/MARGIN (numeric string)."
|
|
7201
|
+
},
|
|
7202
|
+
realizedPnl: { type: "string", description: "Cumulative realized PnL during the position's lifetime, in `quoteCcy` units." },
|
|
7203
|
+
pnl: {
|
|
7204
|
+
type: "string",
|
|
7205
|
+
description: "Total realized PnL for this position including fees and funding, denominated in quoteCcy (numeric string). Differs from `realizedPnl` which may exclude fees."
|
|
7206
|
+
},
|
|
7207
|
+
pnlRatio: {
|
|
7208
|
+
type: "string",
|
|
7209
|
+
description: 'Realized PnL as a decimal ratio of cost basis (e.g. "0.15" = +15%, "-0.20" = \u221220%).'
|
|
7210
|
+
},
|
|
7211
|
+
fee: {
|
|
7212
|
+
type: "string",
|
|
7213
|
+
description: "Cumulative trading fee paid over the position's lifetime, denominated in quoteCcy (numeric string, negative = cost)."
|
|
7214
|
+
},
|
|
7215
|
+
fundingFee: {
|
|
7216
|
+
type: "string",
|
|
7217
|
+
description: "Cumulative funding fee paid or received over the position's lifetime, denominated in quoteCcy (numeric string; negative = paid, positive = received)."
|
|
7218
|
+
},
|
|
7219
|
+
liquidationStatus: {
|
|
7220
|
+
type: "string",
|
|
7221
|
+
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.'
|
|
7222
|
+
},
|
|
7223
|
+
closeType: {
|
|
7224
|
+
type: "string",
|
|
7225
|
+
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."
|
|
7226
|
+
},
|
|
7227
|
+
cTime: { type: "string", description: "Position open time as Unix milliseconds (numeric string)." },
|
|
7228
|
+
uTime: { type: "string", description: "Position close time as Unix milliseconds (numeric string)." }
|
|
7229
|
+
}
|
|
7230
|
+
}
|
|
7231
|
+
},
|
|
7232
|
+
{ pagination: PAGINATION_PROP }
|
|
7233
|
+
),
|
|
7234
|
+
inputSchema: {
|
|
7235
|
+
type: "object",
|
|
7236
|
+
properties: {
|
|
7237
|
+
authorId: {
|
|
6624
7238
|
type: "string",
|
|
6625
|
-
description: "
|
|
7239
|
+
description: "Trader's unique ID (obtain from `smartmoney_get_traders_by_filter`)."
|
|
6626
7240
|
},
|
|
6627
|
-
|
|
7241
|
+
instId: {
|
|
6628
7242
|
type: "string",
|
|
6629
|
-
description:
|
|
7243
|
+
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.'
|
|
6630
7244
|
},
|
|
6631
|
-
|
|
7245
|
+
after: {
|
|
6632
7246
|
type: "string",
|
|
6633
|
-
description: "
|
|
7247
|
+
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)."
|
|
6634
7248
|
},
|
|
6635
|
-
|
|
7249
|
+
before: {
|
|
6636
7250
|
type: "string",
|
|
6637
|
-
description: "
|
|
7251
|
+
description: "Cursor: returns positions with `posId` greater than this value (newer \u2014 paginate forwards). Pass as quoted string."
|
|
6638
7252
|
},
|
|
6639
|
-
|
|
7253
|
+
limit: {
|
|
7254
|
+
type: "integer",
|
|
7255
|
+
minimum: 1,
|
|
7256
|
+
maximum: 100,
|
|
7257
|
+
default: 10,
|
|
7258
|
+
description: "Max positions per page (default 10, max 100)."
|
|
7259
|
+
}
|
|
6640
7260
|
},
|
|
6641
|
-
required: ["
|
|
7261
|
+
required: ["authorId"]
|
|
6642
7262
|
},
|
|
6643
7263
|
handler: async (rawArgs, context) => {
|
|
6644
7264
|
const args = asRecord(rawArgs);
|
|
6645
|
-
const
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
7265
|
+
const authorId = readString(args, "authorId");
|
|
7266
|
+
if (!authorId) {
|
|
7267
|
+
throw actionableError(
|
|
7268
|
+
'"authorId" is required.',
|
|
7269
|
+
"Discover trader IDs via `smartmoney_get_traders_by_filter`."
|
|
7270
|
+
);
|
|
6649
7271
|
}
|
|
7272
|
+
const limit = readNumber(args, "limit");
|
|
6650
7273
|
const response = await context.client.privateGet(
|
|
6651
|
-
|
|
7274
|
+
PATH_POSITION_HISTORY,
|
|
6652
7275
|
compactObject({
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
limit
|
|
6658
|
-
...readPoolFilters(args)
|
|
7276
|
+
authorId,
|
|
7277
|
+
instCcy: extractBaseCcy(readString(args, "instId")),
|
|
7278
|
+
after: readString(args, "after"),
|
|
7279
|
+
before: readString(args, "before"),
|
|
7280
|
+
limit
|
|
6659
7281
|
}),
|
|
6660
|
-
publicRateLimit("
|
|
7282
|
+
publicRateLimit("smartmoney_get_trader_positions_history", SMARTMONEY_RPS)
|
|
6661
7283
|
);
|
|
6662
|
-
|
|
7284
|
+
const normalized = normalizeResponse(response);
|
|
7285
|
+
const data = Array.isArray(normalized.data) ? normalized.data : [];
|
|
7286
|
+
return {
|
|
7287
|
+
...normalized,
|
|
7288
|
+
data,
|
|
7289
|
+
pagination: buildPagination(data, limit ?? 10, "posId")
|
|
7290
|
+
};
|
|
6663
7291
|
}
|
|
6664
7292
|
},
|
|
6665
|
-
/* ----------
|
|
7293
|
+
/* ---------- T5. Trader order history ---------- */
|
|
6666
7294
|
{
|
|
6667
|
-
name: "
|
|
7295
|
+
name: "smartmoney_get_trader_orders_history",
|
|
6668
7296
|
module: "smartmoney",
|
|
6669
|
-
description: "
|
|
7297
|
+
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).",
|
|
6670
7298
|
isWrite: false,
|
|
7299
|
+
outputSchema: envelope(
|
|
7300
|
+
{
|
|
7301
|
+
type: "array",
|
|
7302
|
+
items: {
|
|
7303
|
+
type: "object",
|
|
7304
|
+
properties: {
|
|
7305
|
+
ordId: { type: "string", description: "Unique order ID. Use as the `after` / `before` cursor when paginating." },
|
|
7306
|
+
instId: { type: "string", description: "Instrument ID e.g. BTC-USDT-SWAP." },
|
|
7307
|
+
displayId: { type: "string", description: "Display-form instrument ID used in OKX UI." },
|
|
7308
|
+
instType: {
|
|
7309
|
+
type: "string",
|
|
7310
|
+
description: "Instrument business line: `SWAP` (perpetual contract) | `SPOT`."
|
|
7311
|
+
},
|
|
7312
|
+
baseName: { type: "string", description: 'Base currency symbol, e.g. "BTC".' },
|
|
7313
|
+
quoteName: { type: "string", description: 'Quote currency symbol, e.g. "USD".' },
|
|
7314
|
+
tradeQuoteCcy: { type: "string", description: "Quote currency the fill actually settled in." },
|
|
7315
|
+
side: {
|
|
7316
|
+
type: "string",
|
|
7317
|
+
description: "Order side. `buy` = open long / close short; `sell` = open short / close long."
|
|
7318
|
+
},
|
|
7319
|
+
posSide: {
|
|
7320
|
+
type: "string",
|
|
7321
|
+
description: "Position direction the order applies to: `long` | `short`. Indicates whether the trader was opening/closing a long-side or short-side position."
|
|
7322
|
+
},
|
|
7323
|
+
ordType: {
|
|
7324
|
+
type: "string",
|
|
7325
|
+
description: "Order type. `limit` = price-protected limit order; `market` = immediate at best available price."
|
|
7326
|
+
},
|
|
7327
|
+
lever: { type: "string", description: 'Leverage multiplier used for this order (numeric string; "1" for spot).' },
|
|
7328
|
+
px: { type: "string", description: "Submitted order price (numeric string). For market orders this may be empty/0." },
|
|
7329
|
+
avgPx: { type: "string", description: "Volume-weighted average fill price (numeric string)." },
|
|
7330
|
+
sz: {
|
|
7331
|
+
type: "string",
|
|
7332
|
+
description: "Order size. Unit depends on instType: coins (\u5E01) for SPOT, contracts (\u5F20) for SWAP/FUTURES."
|
|
7333
|
+
},
|
|
7334
|
+
value: {
|
|
7335
|
+
type: "string",
|
|
7336
|
+
description: "Order notional value, denominated in `quoteName` units."
|
|
7337
|
+
},
|
|
7338
|
+
cTime: { type: "string", description: "Order creation time as Unix milliseconds (numeric string)." },
|
|
7339
|
+
fillTime: {
|
|
7340
|
+
type: "string",
|
|
7341
|
+
description: "Timestamp when the order was last filled, as Unix milliseconds (numeric string)."
|
|
7342
|
+
},
|
|
7343
|
+
uTime: {
|
|
7344
|
+
type: "string",
|
|
7345
|
+
description: "Timestamp when the order record was last updated, as Unix milliseconds (numeric string)."
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
}
|
|
7349
|
+
},
|
|
7350
|
+
{ pagination: PAGINATION_PROP }
|
|
7351
|
+
),
|
|
6671
7352
|
inputSchema: {
|
|
6672
7353
|
type: "object",
|
|
6673
7354
|
properties: {
|
|
6674
|
-
|
|
7355
|
+
authorId: {
|
|
6675
7356
|
type: "string",
|
|
6676
|
-
description: "
|
|
7357
|
+
description: "Trader's unique ID (obtain from `smartmoney_get_traders_by_filter`)."
|
|
6677
7358
|
},
|
|
6678
|
-
|
|
6679
|
-
authorIds: {
|
|
7359
|
+
instId: {
|
|
6680
7360
|
type: "string",
|
|
6681
|
-
description: "
|
|
7361
|
+
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.'
|
|
6682
7362
|
},
|
|
6683
7363
|
after: {
|
|
6684
7364
|
type: "string",
|
|
6685
|
-
description: "Cursor
|
|
7365
|
+
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)."
|
|
6686
7366
|
},
|
|
6687
7367
|
before: {
|
|
6688
7368
|
type: "string",
|
|
6689
|
-
description: "Cursor
|
|
7369
|
+
description: "Cursor: returns trades with `ordId` greater than this value (newer \u2014 paginate forwards). Pass as quoted string."
|
|
6690
7370
|
},
|
|
6691
7371
|
limit: {
|
|
6692
|
-
type: "
|
|
6693
|
-
|
|
7372
|
+
type: "integer",
|
|
7373
|
+
minimum: 1,
|
|
7374
|
+
maximum: 100,
|
|
7375
|
+
default: 10,
|
|
7376
|
+
description: "Max trades per page (default 10, max 100)."
|
|
6694
7377
|
}
|
|
6695
|
-
}
|
|
7378
|
+
},
|
|
7379
|
+
required: ["authorId"]
|
|
6696
7380
|
},
|
|
6697
7381
|
handler: async (rawArgs, context) => {
|
|
6698
7382
|
const args = asRecord(rawArgs);
|
|
7383
|
+
const authorId = readString(args, "authorId");
|
|
7384
|
+
if (!authorId) {
|
|
7385
|
+
throw actionableError(
|
|
7386
|
+
'"authorId" is required.',
|
|
7387
|
+
"Discover trader IDs via `smartmoney_get_traders_by_filter`."
|
|
7388
|
+
);
|
|
7389
|
+
}
|
|
7390
|
+
const limit = readNumber(args, "limit");
|
|
6699
7391
|
const response = await context.client.privateGet(
|
|
6700
|
-
|
|
7392
|
+
PATH_TRADE_RECORDS,
|
|
6701
7393
|
compactObject({
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
authorIds: readString(args, "authorIds"),
|
|
7394
|
+
authorId,
|
|
7395
|
+
instCcy: extractBaseCcy(readString(args, "instId")),
|
|
6705
7396
|
after: readString(args, "after"),
|
|
6706
7397
|
before: readString(args, "before"),
|
|
6707
|
-
limit
|
|
7398
|
+
limit
|
|
6708
7399
|
}),
|
|
6709
|
-
publicRateLimit("
|
|
7400
|
+
publicRateLimit("smartmoney_get_trader_orders_history", SMARTMONEY_RPS)
|
|
6710
7401
|
);
|
|
6711
7402
|
const normalized = normalizeResponse(response);
|
|
6712
|
-
|
|
7403
|
+
const data = Array.isArray(normalized.data) ? normalized.data : [];
|
|
7404
|
+
return {
|
|
7405
|
+
...normalized,
|
|
7406
|
+
data,
|
|
7407
|
+
pagination: buildPagination(data, limit ?? 10, "ordId")
|
|
7408
|
+
};
|
|
6713
7409
|
}
|
|
6714
7410
|
},
|
|
6715
|
-
/* ----------
|
|
7411
|
+
/* ---------- T6. Search top traders by nickname keyword ---------- */
|
|
6716
7412
|
{
|
|
6717
|
-
name: "
|
|
7413
|
+
name: "smartmoney_search_trader",
|
|
6718
7414
|
module: "smartmoney",
|
|
6719
|
-
description: "Trader
|
|
7415
|
+
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).",
|
|
6720
7416
|
isWrite: false,
|
|
7417
|
+
outputSchema: envelope({
|
|
7418
|
+
type: "array",
|
|
7419
|
+
description: "Matched Top Traders (\u226410), sorted by `followerCount` DESC. Empty array when no recall intersects the Top Trader set.",
|
|
7420
|
+
items: {
|
|
7421
|
+
type: "object",
|
|
7422
|
+
properties: {
|
|
7423
|
+
authorId: { type: "string", description: "Trader's unique ID \u2014 pass to other `smartmoney_get_trader_*` tools." },
|
|
7424
|
+
nickName: { type: "string", description: "Display nickname matched against the keyword." },
|
|
7425
|
+
followerCount: { type: "string", description: "OKX-platform follower count (numeric string; Twitter followers excluded). Sort key." }
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
}),
|
|
6721
7429
|
inputSchema: {
|
|
6722
7430
|
type: "object",
|
|
6723
7431
|
properties: {
|
|
6724
|
-
|
|
7432
|
+
keyword: {
|
|
6725
7433
|
type: "string",
|
|
6726
|
-
description: "Trader
|
|
7434
|
+
description: "Nickname search keyword. Required, must be non-empty / non-whitespace. Matched candidates are intersected with the Top Trader set."
|
|
7435
|
+
}
|
|
7436
|
+
},
|
|
7437
|
+
required: ["keyword"]
|
|
7438
|
+
},
|
|
7439
|
+
handler: async (rawArgs, context) => {
|
|
7440
|
+
const args = asRecord(rawArgs);
|
|
7441
|
+
const keyword = readString(args, "keyword");
|
|
7442
|
+
if (!keyword || keyword.trim() === "") {
|
|
7443
|
+
throw actionableError(
|
|
7444
|
+
'"keyword" is required and must be non-empty.',
|
|
7445
|
+
'Pass a nickname fragment (e.g. "alice", "\u5C0F\u660E"). For known author IDs use `smartmoney_get_performance_by_trader` instead.'
|
|
7446
|
+
);
|
|
7447
|
+
}
|
|
7448
|
+
const response = await context.client.privateGet(
|
|
7449
|
+
PATH_TOP_TRADER_SEARCH,
|
|
7450
|
+
compactObject({ keyword }),
|
|
7451
|
+
publicRateLimit("smartmoney_search_trader", SMARTMONEY_RPS)
|
|
7452
|
+
);
|
|
7453
|
+
const normalized = normalizeResponse(response);
|
|
7454
|
+
const data = Array.isArray(normalized.data) ? normalized.data : [];
|
|
7455
|
+
return { ...normalized, data };
|
|
7456
|
+
}
|
|
7457
|
+
},
|
|
7458
|
+
/* ===================================================== */
|
|
7459
|
+
/* Signal/Coin family (4) */
|
|
7460
|
+
/* ===================================================== */
|
|
7461
|
+
/* ---------- S1. Signal overview by filter (multi-asset, tier-filtered pool) ---------- */
|
|
7462
|
+
{
|
|
7463
|
+
name: "smartmoney_get_signal_overview_by_filter",
|
|
7464
|
+
module: "smartmoney",
|
|
7465
|
+
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).",
|
|
7466
|
+
isWrite: false,
|
|
7467
|
+
outputSchema: envelope({
|
|
7468
|
+
type: "array",
|
|
7469
|
+
description: "Per-instrument snapshot, one element per requested coin.",
|
|
7470
|
+
items: { type: "object", properties: SIGNAL_ITEM_PROPS }
|
|
7471
|
+
}),
|
|
7472
|
+
inputSchema: {
|
|
7473
|
+
type: "object",
|
|
7474
|
+
properties: {
|
|
7475
|
+
topInstruments: {
|
|
7476
|
+
type: "integer",
|
|
7477
|
+
minimum: 1,
|
|
7478
|
+
maximum: 100,
|
|
7479
|
+
default: 20,
|
|
7480
|
+
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)."
|
|
6727
7481
|
},
|
|
6728
|
-
|
|
6729
|
-
type: "
|
|
6730
|
-
|
|
7482
|
+
instCcyList: {
|
|
7483
|
+
type: "array",
|
|
7484
|
+
items: { type: "string" },
|
|
7485
|
+
minItems: 1,
|
|
7486
|
+
description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`.'
|
|
6731
7487
|
},
|
|
7488
|
+
...SIGNAL_POOL_FILTER_PROPS,
|
|
7489
|
+
lmtNum: {
|
|
7490
|
+
type: "integer",
|
|
7491
|
+
minimum: 1,
|
|
7492
|
+
maximum: 2e3,
|
|
7493
|
+
default: 100,
|
|
7494
|
+
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."
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7497
|
+
},
|
|
7498
|
+
handler: async (rawArgs, context) => {
|
|
7499
|
+
const args = asRecord(rawArgs);
|
|
7500
|
+
const instCcyList = readArrayAsCsv(args, "instCcyList");
|
|
7501
|
+
const topInstrumentsRaw = readNumber(args, "topInstruments");
|
|
7502
|
+
if (instCcyList && topInstrumentsRaw !== void 0) {
|
|
7503
|
+
throw actionableError(
|
|
7504
|
+
'"topInstruments" and "instCcyList" are mutually exclusive.',
|
|
7505
|
+
"Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
|
|
7506
|
+
);
|
|
7507
|
+
}
|
|
7508
|
+
const response = await context.client.privateGet(
|
|
7509
|
+
PATH_OVERVIEW,
|
|
7510
|
+
compactObject({
|
|
7511
|
+
...instCcyList ? { instCcyList } : { topInstruments: topInstrumentsRaw ?? 20 },
|
|
7512
|
+
...readSignalPoolFilters(args),
|
|
7513
|
+
lmtNum: readNumber(args, "lmtNum")
|
|
7514
|
+
}),
|
|
7515
|
+
publicRateLimit("smartmoney_get_signal_overview_by_filter", SMARTMONEY_RPS)
|
|
7516
|
+
);
|
|
7517
|
+
return normalizeResponse(response);
|
|
7518
|
+
}
|
|
7519
|
+
},
|
|
7520
|
+
/* ---------- S2. Signal overview by trader (multi-asset, authorIds-restricted) ---------- */
|
|
7521
|
+
{
|
|
7522
|
+
name: "smartmoney_get_signal_overview_by_trader",
|
|
7523
|
+
module: "smartmoney",
|
|
7524
|
+
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).",
|
|
7525
|
+
isWrite: false,
|
|
7526
|
+
outputSchema: envelope({
|
|
7527
|
+
type: "array",
|
|
7528
|
+
description: "Per-instrument snapshot, one element per requested coin.",
|
|
7529
|
+
items: { type: "object", properties: SIGNAL_ITEM_PROPS }
|
|
7530
|
+
}),
|
|
7531
|
+
inputSchema: {
|
|
7532
|
+
type: "object",
|
|
7533
|
+
properties: {
|
|
7534
|
+
authorIds: {
|
|
7535
|
+
type: "array",
|
|
7536
|
+
items: { type: "string" },
|
|
7537
|
+
minItems: 1,
|
|
7538
|
+
description: 'Trader IDs to aggregate over, e.g. `["1001", "1002"]`. Required.'
|
|
7539
|
+
},
|
|
7540
|
+
topInstruments: {
|
|
7541
|
+
type: "integer",
|
|
7542
|
+
minimum: 1,
|
|
7543
|
+
maximum: 100,
|
|
7544
|
+
default: 20,
|
|
7545
|
+
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)."
|
|
7546
|
+
},
|
|
7547
|
+
instCcyList: {
|
|
7548
|
+
type: "array",
|
|
7549
|
+
items: { type: "string" },
|
|
7550
|
+
minItems: 1,
|
|
7551
|
+
description: 'Base currencies to aggregate, e.g. `["BTC", "ETH", "SOL"]`. Mutually exclusive with `topInstruments`.'
|
|
7552
|
+
}
|
|
7553
|
+
},
|
|
7554
|
+
required: ["authorIds"]
|
|
7555
|
+
},
|
|
7556
|
+
handler: async (rawArgs, context) => {
|
|
7557
|
+
const args = asRecord(rawArgs);
|
|
7558
|
+
const authorIds = readArrayAsCsv(args, "authorIds");
|
|
7559
|
+
if (!authorIds) {
|
|
7560
|
+
throw actionableError(
|
|
7561
|
+
'"authorIds" is required and must be a non-empty array.',
|
|
7562
|
+
'Pass IDs from `smartmoney_get_traders_by_filter` as an array (e.g. ["1001", "1002"]).'
|
|
7563
|
+
);
|
|
7564
|
+
}
|
|
7565
|
+
const instCcyList = readArrayAsCsv(args, "instCcyList");
|
|
7566
|
+
const topInstrumentsRaw = readNumber(args, "topInstruments");
|
|
7567
|
+
if (instCcyList && topInstrumentsRaw !== void 0) {
|
|
7568
|
+
throw actionableError(
|
|
7569
|
+
'"topInstruments" and "instCcyList" are mutually exclusive.',
|
|
7570
|
+
"Pass exactly one \u2014 `topInstruments` for top-N hottest coins, or `instCcyList` for specific coins."
|
|
7571
|
+
);
|
|
7572
|
+
}
|
|
7573
|
+
const response = await context.client.privateGet(
|
|
7574
|
+
PATH_OVERVIEW,
|
|
7575
|
+
compactObject({
|
|
7576
|
+
authorIds,
|
|
7577
|
+
...instCcyList ? { instCcyList } : { topInstruments: topInstrumentsRaw ?? 20 }
|
|
7578
|
+
}),
|
|
7579
|
+
publicRateLimit("smartmoney_get_signal_overview_by_trader", SMARTMONEY_RPS)
|
|
7580
|
+
);
|
|
7581
|
+
return normalizeResponse(response);
|
|
7582
|
+
}
|
|
7583
|
+
},
|
|
7584
|
+
/* ---------- S3. Signal trend by filter (single-asset, tier-filtered pool, asOfTime anchor) ---------- */
|
|
7585
|
+
{
|
|
7586
|
+
name: "smartmoney_get_signal_trend_by_filter",
|
|
7587
|
+
module: "smartmoney",
|
|
7588
|
+
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.",
|
|
7589
|
+
isWrite: false,
|
|
7590
|
+
outputSchema: envelope({
|
|
7591
|
+
type: "array",
|
|
7592
|
+
description: "Time-bucket series for the requested instrument, sorted by time DESC (newest first).",
|
|
7593
|
+
items: { type: "object", properties: SIGNAL_HISTORY_ITEM_PROPS }
|
|
7594
|
+
}),
|
|
7595
|
+
inputSchema: {
|
|
7596
|
+
type: "object",
|
|
7597
|
+
properties: {
|
|
6732
7598
|
instCcy: {
|
|
6733
7599
|
type: "string",
|
|
6734
|
-
description:
|
|
7600
|
+
description: 'Base currency to scope the time-series, e.g. "BTC". Required.'
|
|
6735
7601
|
},
|
|
6736
|
-
|
|
7602
|
+
asOfTime: {
|
|
6737
7603
|
type: "string",
|
|
6738
|
-
description:
|
|
7604
|
+
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.'
|
|
7605
|
+
},
|
|
7606
|
+
granularity: {
|
|
7607
|
+
type: "string",
|
|
7608
|
+
enum: ["1h", "1d"],
|
|
7609
|
+
default: "1h",
|
|
7610
|
+
description: "Time-bucket size. `1h` = hourly snapshots (intraday/short-term trend), `1d` = daily snapshots (multi-day trend)."
|
|
7611
|
+
},
|
|
7612
|
+
limit: {
|
|
7613
|
+
type: "integer",
|
|
7614
|
+
minimum: 1,
|
|
7615
|
+
maximum: 500,
|
|
7616
|
+
default: 24,
|
|
7617
|
+
description: "Number of buckets to return (newest first), ending at `asOfTime`. Default 24, max 500."
|
|
7618
|
+
},
|
|
7619
|
+
...SIGNAL_POOL_FILTER_PROPS,
|
|
7620
|
+
lmtNum: {
|
|
7621
|
+
type: "integer",
|
|
7622
|
+
minimum: 1,
|
|
7623
|
+
maximum: 2e3,
|
|
7624
|
+
default: 100,
|
|
7625
|
+
description: "Top-N traders to pull into the aggregation pool, ranked by `sortBy` (DESC). Default 100, max 2000."
|
|
6739
7626
|
}
|
|
6740
7627
|
},
|
|
6741
|
-
required: ["
|
|
7628
|
+
required: ["instCcy"]
|
|
6742
7629
|
},
|
|
6743
7630
|
handler: async (rawArgs, context) => {
|
|
6744
7631
|
const args = asRecord(rawArgs);
|
|
6745
|
-
const authorId = requireString(args, "authorId");
|
|
6746
|
-
const period = readString(args, "period");
|
|
6747
7632
|
const instCcy = readString(args, "instCcy");
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
)
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
7633
|
+
if (!instCcy) {
|
|
7634
|
+
throw actionableError(
|
|
7635
|
+
'"instCcy" is required.',
|
|
7636
|
+
'Pass a base currency, e.g. "BTC".'
|
|
7637
|
+
);
|
|
7638
|
+
}
|
|
7639
|
+
const response = await context.client.privateGet(
|
|
7640
|
+
PATH_SIGNAL_HISTORY,
|
|
7641
|
+
compactObject({
|
|
7642
|
+
instCcy,
|
|
7643
|
+
asOfTime: readString(args, "asOfTime"),
|
|
7644
|
+
granularity: readString(args, "granularity"),
|
|
7645
|
+
limit: readNumber(args, "limit"),
|
|
7646
|
+
...readSignalPoolFilters(args),
|
|
7647
|
+
lmtNum: readNumber(args, "lmtNum")
|
|
7648
|
+
}),
|
|
7649
|
+
publicRateLimit("smartmoney_get_signal_trend_by_filter", SMARTMONEY_RPS)
|
|
7650
|
+
);
|
|
7651
|
+
return normalizeResponse(response);
|
|
7652
|
+
}
|
|
7653
|
+
},
|
|
7654
|
+
/* ---------- S4. Signal trend by trader (single-asset, authorIds-restricted) ---------- */
|
|
7655
|
+
{
|
|
7656
|
+
name: "smartmoney_get_signal_trend_by_trader",
|
|
7657
|
+
module: "smartmoney",
|
|
7658
|
+
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.",
|
|
7659
|
+
isWrite: false,
|
|
7660
|
+
outputSchema: envelope({
|
|
7661
|
+
type: "array",
|
|
7662
|
+
description: "Time-bucket series for the requested instrument, sorted by time DESC (newest first).",
|
|
7663
|
+
items: { type: "object", properties: SIGNAL_HISTORY_ITEM_PROPS }
|
|
7664
|
+
}),
|
|
7665
|
+
inputSchema: {
|
|
7666
|
+
type: "object",
|
|
7667
|
+
properties: {
|
|
7668
|
+
authorIds: {
|
|
7669
|
+
type: "array",
|
|
7670
|
+
items: { type: "string" },
|
|
7671
|
+
minItems: 1,
|
|
7672
|
+
description: 'Trader IDs to aggregate over, e.g. `["1001", "1002"]`. Required.'
|
|
7673
|
+
},
|
|
7674
|
+
instCcy: {
|
|
7675
|
+
type: "string",
|
|
7676
|
+
description: 'Base currency to scope the time-series, e.g. "BTC". Required.'
|
|
7677
|
+
},
|
|
7678
|
+
asOfTime: {
|
|
7679
|
+
type: "string",
|
|
7680
|
+
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.'
|
|
7681
|
+
},
|
|
7682
|
+
granularity: {
|
|
7683
|
+
type: "string",
|
|
7684
|
+
enum: ["1h", "1d"],
|
|
7685
|
+
default: "1h",
|
|
7686
|
+
description: "Time-bucket size. `1h` = hourly snapshots (intraday/short-term trend), `1d` = daily snapshots (multi-day trend)."
|
|
7687
|
+
},
|
|
7688
|
+
limit: {
|
|
7689
|
+
type: "integer",
|
|
7690
|
+
minimum: 1,
|
|
7691
|
+
maximum: 500,
|
|
7692
|
+
default: 24,
|
|
7693
|
+
description: "Number of buckets to return (newest first), ending at `asOfTime`. Default 24, max 500."
|
|
6776
7694
|
}
|
|
6777
|
-
}
|
|
7695
|
+
},
|
|
7696
|
+
required: ["authorIds", "instCcy"]
|
|
7697
|
+
},
|
|
7698
|
+
handler: async (rawArgs, context) => {
|
|
7699
|
+
const args = asRecord(rawArgs);
|
|
7700
|
+
const authorIds = readArrayAsCsv(args, "authorIds");
|
|
7701
|
+
const instCcy = readString(args, "instCcy");
|
|
7702
|
+
if (!authorIds) {
|
|
7703
|
+
throw actionableError(
|
|
7704
|
+
'"authorIds" is required and must be a non-empty array.',
|
|
7705
|
+
'Pass IDs from `smartmoney_get_traders_by_filter` as an array (e.g. ["1001", "1002"]).'
|
|
7706
|
+
);
|
|
7707
|
+
}
|
|
7708
|
+
if (!instCcy) {
|
|
7709
|
+
throw actionableError(
|
|
7710
|
+
'"instCcy" is required.',
|
|
7711
|
+
'Pass a base currency, e.g. "BTC".'
|
|
7712
|
+
);
|
|
7713
|
+
}
|
|
7714
|
+
const response = await context.client.privateGet(
|
|
7715
|
+
PATH_SIGNAL_HISTORY,
|
|
7716
|
+
compactObject({
|
|
7717
|
+
authorIds,
|
|
7718
|
+
instCcy,
|
|
7719
|
+
asOfTime: readString(args, "asOfTime"),
|
|
7720
|
+
granularity: readString(args, "granularity"),
|
|
7721
|
+
limit: readNumber(args, "limit")
|
|
7722
|
+
}),
|
|
7723
|
+
publicRateLimit("smartmoney_get_signal_trend_by_trader", SMARTMONEY_RPS)
|
|
7724
|
+
);
|
|
7725
|
+
return normalizeResponse(response);
|
|
6778
7726
|
}
|
|
6779
7727
|
}
|
|
6780
7728
|
];
|
|
@@ -6833,8 +7781,12 @@ function buildContractTradeTools(cfg) {
|
|
|
6833
7781
|
clOrdId: { type: "string", description: "Client order ID (max 32 chars)" },
|
|
6834
7782
|
tpTriggerPx: { type: "string", description: "TP trigger price" },
|
|
6835
7783
|
tpOrdPx: { type: "string", description: "TP order price; -1=market" },
|
|
7784
|
+
tpOrdKind: TP_ORD_KIND_SCHEMA,
|
|
7785
|
+
tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
|
|
6836
7786
|
slTriggerPx: { type: "string", description: "SL trigger price" },
|
|
6837
|
-
slOrdPx: { type: "string", description: "SL order price; -1=market" }
|
|
7787
|
+
slOrdPx: { type: "string", description: "SL order price; -1=market" },
|
|
7788
|
+
slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
|
|
7789
|
+
stpMode: STP_MODE_SCHEMA
|
|
6838
7790
|
},
|
|
6839
7791
|
required: ["instId", "tdMode", "side", "ordType", "sz"]
|
|
6840
7792
|
},
|
|
@@ -6863,6 +7815,9 @@ function buildContractTradeTools(cfg) {
|
|
|
6863
7815
|
px: readString(args, "px"),
|
|
6864
7816
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
6865
7817
|
clOrdId: readString(args, "clOrdId"),
|
|
7818
|
+
stpMode: readString(args, "stpMode"),
|
|
7819
|
+
// Phase 3c CLI power-user flag (issue #182, CLI-only no MCP/skill exposure)
|
|
7820
|
+
pxAmendType: readString(args, "pxAmendType"),
|
|
6866
7821
|
tag: context.config.sourceTag,
|
|
6867
7822
|
attachAlgoOrds
|
|
6868
7823
|
}),
|
|
@@ -8862,6 +9817,8 @@ function registerOptionTools() {
|
|
|
8862
9817
|
type: "string",
|
|
8863
9818
|
description: "TP order price; -1=market"
|
|
8864
9819
|
},
|
|
9820
|
+
tpOrdKind: TP_ORD_KIND_SCHEMA,
|
|
9821
|
+
tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
|
|
8865
9822
|
slTriggerPx: {
|
|
8866
9823
|
type: "string",
|
|
8867
9824
|
description: "SL trigger price"
|
|
@@ -8869,7 +9826,9 @@ function registerOptionTools() {
|
|
|
8869
9826
|
slOrdPx: {
|
|
8870
9827
|
type: "string",
|
|
8871
9828
|
description: "SL order price; -1=market"
|
|
8872
|
-
}
|
|
9829
|
+
},
|
|
9830
|
+
slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
|
|
9831
|
+
stpMode: STP_MODE_SCHEMA
|
|
8873
9832
|
},
|
|
8874
9833
|
required: ["instId", "tdMode", "side", "ordType", "sz"]
|
|
8875
9834
|
},
|
|
@@ -8897,6 +9856,7 @@ function registerOptionTools() {
|
|
|
8897
9856
|
px: readString(args, "px"),
|
|
8898
9857
|
reduceOnly: typeof reduceOnly === "boolean" ? String(reduceOnly) : void 0,
|
|
8899
9858
|
clOrdId: readString(args, "clOrdId"),
|
|
9859
|
+
stpMode: readString(args, "stpMode"),
|
|
8900
9860
|
tag: context.config.sourceTag,
|
|
8901
9861
|
attachAlgoOrds
|
|
8902
9862
|
}),
|
|
@@ -9265,6 +10225,8 @@ function registerSpotTradeTools() {
|
|
|
9265
10225
|
type: "string",
|
|
9266
10226
|
description: "TP order price, -1=market"
|
|
9267
10227
|
},
|
|
10228
|
+
tpOrdKind: TP_ORD_KIND_SCHEMA,
|
|
10229
|
+
tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
|
|
9268
10230
|
slTriggerPx: {
|
|
9269
10231
|
type: "string",
|
|
9270
10232
|
description: "SL trigger price"
|
|
@@ -9272,13 +10234,16 @@ function registerSpotTradeTools() {
|
|
|
9272
10234
|
slOrdPx: {
|
|
9273
10235
|
type: "string",
|
|
9274
10236
|
description: "SL order price, -1=market"
|
|
9275
|
-
}
|
|
10237
|
+
},
|
|
10238
|
+
slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
|
|
10239
|
+
stpMode: STP_MODE_SCHEMA
|
|
9276
10240
|
},
|
|
9277
10241
|
required: ["instId", "tdMode", "side", "ordType", "sz"]
|
|
9278
10242
|
},
|
|
9279
10243
|
handler: async (rawArgs, context) => {
|
|
9280
10244
|
const args = asRecord(rawArgs);
|
|
9281
10245
|
const attachAlgoOrds = buildAttachAlgoOrds(args);
|
|
10246
|
+
const banAmend = args.banAmend;
|
|
9282
10247
|
const response = await context.client.privatePost(
|
|
9283
10248
|
"/api/v5/trade/order",
|
|
9284
10249
|
compactObject({
|
|
@@ -9290,6 +10255,11 @@ function registerSpotTradeTools() {
|
|
|
9290
10255
|
tgtCcy: readString(args, "tgtCcy"),
|
|
9291
10256
|
px: readString(args, "px"),
|
|
9292
10257
|
clOrdId: readString(args, "clOrdId"),
|
|
10258
|
+
stpMode: readString(args, "stpMode"),
|
|
10259
|
+
// Phase 3c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
|
|
10260
|
+
tradeQuoteCcy: readString(args, "tradeQuoteCcy"),
|
|
10261
|
+
banAmend: typeof banAmend === "boolean" ? String(banAmend) : void 0,
|
|
10262
|
+
pxAmendType: readString(args, "pxAmendType"),
|
|
9293
10263
|
tag: context.config.sourceTag,
|
|
9294
10264
|
attachAlgoOrds
|
|
9295
10265
|
}),
|
|
@@ -9454,7 +10424,7 @@ function registerSpotTradeTools() {
|
|
|
9454
10424
|
{
|
|
9455
10425
|
name: "spot_place_algo_order",
|
|
9456
10426
|
module: "spot",
|
|
9457
|
-
description: "Place a spot algo order: TP/SL
|
|
10427
|
+
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.",
|
|
9458
10428
|
isWrite: true,
|
|
9459
10429
|
inputSchema: {
|
|
9460
10430
|
type: "object",
|
|
@@ -9474,8 +10444,8 @@ function registerSpotTradeTools() {
|
|
|
9474
10444
|
},
|
|
9475
10445
|
ordType: {
|
|
9476
10446
|
type: "string",
|
|
9477
|
-
enum: ["conditional", "oco", "move_order_stop"],
|
|
9478
|
-
description: "conditional=single TP/SL, oco=TP+SL pair, move_order_stop=trailing stop"
|
|
10447
|
+
enum: ["conditional", "oco", "move_order_stop", "trigger", "chase", "iceberg", "twap"],
|
|
10448
|
+
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"
|
|
9479
10449
|
},
|
|
9480
10450
|
sz: {
|
|
9481
10451
|
type: "string",
|
|
@@ -9489,6 +10459,8 @@ function registerSpotTradeTools() {
|
|
|
9489
10459
|
type: "string",
|
|
9490
10460
|
description: "TP order price, -1=market (conditional/oco only)"
|
|
9491
10461
|
},
|
|
10462
|
+
tpOrdKind: TP_ORD_KIND_SCHEMA,
|
|
10463
|
+
tpTriggerPxType: TP_TRIGGER_PX_TYPE_SCHEMA,
|
|
9492
10464
|
slTriggerPx: {
|
|
9493
10465
|
type: "string",
|
|
9494
10466
|
description: "SL trigger price (conditional/oco only)"
|
|
@@ -9497,6 +10469,8 @@ function registerSpotTradeTools() {
|
|
|
9497
10469
|
type: "string",
|
|
9498
10470
|
description: "SL order price, -1=market (conditional/oco only)"
|
|
9499
10471
|
},
|
|
10472
|
+
slTriggerPxType: SL_TRIGGER_PX_TYPE_SCHEMA,
|
|
10473
|
+
stpMode: STP_MODE_SCHEMA,
|
|
9500
10474
|
tgtCcy: {
|
|
9501
10475
|
type: "string",
|
|
9502
10476
|
enum: ["base_ccy", "quote_ccy"],
|
|
@@ -9513,30 +10487,50 @@ function registerSpotTradeTools() {
|
|
|
9513
10487
|
activePx: {
|
|
9514
10488
|
type: "string",
|
|
9515
10489
|
description: "Activation price, trailing starts when market hits this (move_order_stop only)"
|
|
9516
|
-
}
|
|
10490
|
+
},
|
|
10491
|
+
...TRIGGER_FLAGS_SCHEMA,
|
|
10492
|
+
...CHASE_FLAGS_SCHEMA,
|
|
10493
|
+
...ICEBERG_TWAP_FLAGS_SCHEMA
|
|
9517
10494
|
},
|
|
9518
10495
|
required: ["instId", "side", "ordType", "sz"]
|
|
9519
10496
|
},
|
|
9520
10497
|
handler: async (rawArgs, context) => {
|
|
9521
10498
|
const args = asRecord(rawArgs);
|
|
10499
|
+
const ordType = requireString(args, "ordType");
|
|
10500
|
+
const base = compactObject({
|
|
10501
|
+
instId: requireString(args, "instId"),
|
|
10502
|
+
tdMode: readString(args, "tdMode") ?? "cash",
|
|
10503
|
+
side: requireString(args, "side"),
|
|
10504
|
+
ordType,
|
|
10505
|
+
sz: requireString(args, "sz"),
|
|
10506
|
+
tgtCcy: readString(args, "tgtCcy"),
|
|
10507
|
+
stpMode: readString(args, "stpMode"),
|
|
10508
|
+
// Phase 3a+c CLI power-user flags (issue #182, CLI-only no MCP/skill exposure)
|
|
10509
|
+
pxAmendType: readString(args, "pxAmendType"),
|
|
10510
|
+
tag: context.config.sourceTag
|
|
10511
|
+
});
|
|
10512
|
+
switch (ordType) {
|
|
10513
|
+
case "trigger":
|
|
10514
|
+
Object.assign(base, buildTriggerOrdTypeBody(args));
|
|
10515
|
+
break;
|
|
10516
|
+
case "chase":
|
|
10517
|
+
Object.assign(base, buildChaseOrdTypeBody(args));
|
|
10518
|
+
break;
|
|
10519
|
+
case "iceberg":
|
|
10520
|
+
case "twap":
|
|
10521
|
+
Object.assign(base, buildIcebergTwapOrdTypeBody(args));
|
|
10522
|
+
break;
|
|
10523
|
+
default:
|
|
10524
|
+
Object.assign(base, compactObject({
|
|
10525
|
+
...buildAlgoConditionalCommonFields(args),
|
|
10526
|
+
callbackRatio: readString(args, "callbackRatio"),
|
|
10527
|
+
callbackSpread: readString(args, "callbackSpread")
|
|
10528
|
+
}));
|
|
10529
|
+
break;
|
|
10530
|
+
}
|
|
9522
10531
|
const response = await context.client.privatePost(
|
|
9523
10532
|
"/api/v5/trade/order-algo",
|
|
9524
|
-
|
|
9525
|
-
instId: requireString(args, "instId"),
|
|
9526
|
-
tdMode: readString(args, "tdMode") ?? "cash",
|
|
9527
|
-
side: requireString(args, "side"),
|
|
9528
|
-
ordType: requireString(args, "ordType"),
|
|
9529
|
-
sz: requireString(args, "sz"),
|
|
9530
|
-
tgtCcy: readString(args, "tgtCcy"),
|
|
9531
|
-
tpTriggerPx: readString(args, "tpTriggerPx"),
|
|
9532
|
-
tpOrdPx: readString(args, "tpOrdPx"),
|
|
9533
|
-
slTriggerPx: readString(args, "slTriggerPx"),
|
|
9534
|
-
slOrdPx: readString(args, "slOrdPx"),
|
|
9535
|
-
callbackRatio: readString(args, "callbackRatio"),
|
|
9536
|
-
callbackSpread: readString(args, "callbackSpread"),
|
|
9537
|
-
activePx: readString(args, "activePx"),
|
|
9538
|
-
tag: context.config.sourceTag
|
|
9539
|
-
}),
|
|
10533
|
+
base,
|
|
9540
10534
|
privateRateLimit("spot_place_algo_order", 20)
|
|
9541
10535
|
);
|
|
9542
10536
|
return normalizeResponse(response);
|
|
@@ -10130,6 +11124,7 @@ function toMcpTool(tool) {
|
|
|
10130
11124
|
name: tool.name,
|
|
10131
11125
|
description: tool.description,
|
|
10132
11126
|
inputSchema: tool.inputSchema,
|
|
11127
|
+
...tool.outputSchema ? { outputSchema: tool.outputSchema } : {},
|
|
10133
11128
|
annotations: {
|
|
10134
11129
|
readOnlyHint: !tool.isWrite,
|
|
10135
11130
|
destructiveHint: tool.isWrite,
|
|
@@ -10563,7 +11558,7 @@ var _require = createRequire(import.meta.url);
|
|
|
10563
11558
|
var pkg = _require("../package.json");
|
|
10564
11559
|
var SERVER_NAME = "okx-trade-mcp";
|
|
10565
11560
|
var SERVER_VERSION = pkg.version;
|
|
10566
|
-
var GIT_HASH = true ? "
|
|
11561
|
+
var GIT_HASH = true ? "d0f8ad6" : "dev";
|
|
10567
11562
|
|
|
10568
11563
|
// src/server.ts
|
|
10569
11564
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|