@pionex/pionex-trade-mcp 0.2.44 → 0.2.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1777,23 +1777,6 @@ function asPositiveDecimalString2(value, field) {
1777
1777
  function normalizePerpBase(base) {
1778
1778
  return base.endsWith(".PERP") ? base : `${base}.PERP`;
1779
1779
  }
1780
- function parseSmartCopyBuOrderData(raw) {
1781
- const quoteInvestment = asPositiveDecimalString2(raw.quoteInvestment, "buOrderData.quoteInvestment");
1782
- const leverageType = asNonEmptyString3(raw.leverageType, "buOrderData.leverageType");
1783
- assertEnum3(leverageType, "buOrderData.leverageType", ["follow", "fixed"]);
1784
- if (leverageType === "fixed" && raw.leverage == null) {
1785
- throw new Error('Invalid "buOrderData.leverage": required when leverageType is "fixed".');
1786
- }
1787
- const out = { quoteInvestment, leverageType };
1788
- if (raw.leverage != null) out.leverage = asPositiveNumber3(raw.leverage, "buOrderData.leverage");
1789
- if (raw.maxInvestPerOrder != null) out.maxInvestPerOrder = asPositiveDecimalString2(raw.maxInvestPerOrder, "buOrderData.maxInvestPerOrder");
1790
- if (raw.copyMode != null) {
1791
- const copyMode = asNonEmptyString3(raw.copyMode, "buOrderData.copyMode");
1792
- assertEnum3(copyMode, "buOrderData.copyMode", ["fixed_amount", "fixed_ratio"]);
1793
- out.copyMode = copyMode;
1794
- }
1795
- return out;
1796
- }
1797
1780
  function registerBotTools() {
1798
1781
  return [
1799
1782
  {
@@ -2283,62 +2266,76 @@ function registerBotTools() {
2283
2266
  name: "pionex_bot_smart_copy_check_params",
2284
2267
  module: "bot",
2285
2268
  isWrite: false,
2286
- description: "Validate smart copy bot parameters before creating an order. Uses the same buOrderData structure as smart_copy_create. On FailedWithData error the response includes min_investment, max_investment. Endpoint: POST /api/v1/bot/orders/smartCopy/checkParams",
2269
+ description: "Validate smart copy bot parameters before creating an order. Pass quote_investment='0' to get the allowed range only (no investment estimate). Returns max_investment, max_leverage, available_limit (and notional_limit when invest>0). Endpoint: POST /api/v1/bot/orders/smartCopy/checkParams",
2287
2270
  inputSchema: {
2288
2271
  type: "object",
2289
2272
  additionalProperties: false,
2290
- required: ["base", "quote", "buOrderData"],
2273
+ required: ["base", "quote", "leverage", "quote_investment"],
2291
2274
  properties: {
2292
2275
  base: { type: "string", description: "Base currency (e.g. BTC)" },
2293
2276
  quote: { type: "string", description: "Quote currency (e.g. USDT)" },
2294
- buOrderData: {
2295
- type: "object",
2296
- additionalProperties: false,
2297
- required: ["quoteInvestment", "leverageType"],
2298
- properties: {
2299
- quoteInvestment: { type: "string", description: "Investment amount in quote currency." },
2300
- leverageType: { type: "string", enum: ["follow", "fixed"], description: "Follow signal provider's leverage or use fixed value." },
2301
- leverage: { type: "number", description: "Custom leverage (required when leverageType is 'fixed')." },
2302
- maxInvestPerOrder: { type: "string", description: "Maximum investment per replicated order." },
2303
- copyMode: { type: "string", enum: ["fixed_amount", "fixed_ratio"], description: "Copy mode." }
2304
- }
2305
- }
2277
+ leverage: { type: "integer", description: "Leverage multiplier (e.g. 2)" },
2278
+ quote_investment: { type: "string", description: "Investment amount in quote currency; use '0' to get range only" },
2279
+ signal_type: { type: "string", description: "Optional; signal provider UUID to scope the check" },
2280
+ signal_param: { type: "string", description: "Optional; signal parameters as a JSON string" }
2306
2281
  }
2307
2282
  },
2308
2283
  async handler(args, { client }) {
2309
2284
  const base = asNonEmptyString3(args.base, "base");
2310
2285
  const quote = asNonEmptyString3(args.quote, "quote");
2311
- const buOrderData = parseSmartCopyBuOrderData(asObject(args.buOrderData, "buOrderData"));
2312
- return (await client.signedPost("/api/v1/bot/orders/smartCopy/checkParams", { base, quote, buOrderData })).data;
2286
+ const leverage = asPositiveInteger3(args.leverage, "leverage");
2287
+ const quote_investment = asNonEmptyString3(args.quote_investment, "quote_investment");
2288
+ const body = { base, quote, leverage, quote_investment };
2289
+ if (args.signal_type != null) body.signal_type = String(args.signal_type);
2290
+ if (args.signal_param != null) body.signal_param = String(args.signal_param);
2291
+ return (await client.signedPost("/api/v1/bot/orders/smartCopy/checkParams", body)).data;
2313
2292
  }
2314
2293
  },
2315
2294
  {
2316
2295
  name: "pionex_bot_smart_copy_create",
2317
2296
  module: "bot",
2318
2297
  isWrite: true,
2319
- description: "Create a smart copy bot order. Required: base, quote, buOrderData (quoteInvestment, leverageType). Optional top-level: copyFrom (signal source ID), copyBotOrderId. buOrderData optional: leverage (required if leverageType=fixed), maxInvestPerOrder, copyMode.",
2298
+ description: "Create a smart copy bot order. Required: base, quote, bu_order_data.quote_total_investment, bu_order_data.portfolio (each portfolio item needs base, signal_type, leverage). Returns buOrderId on success. Endpoint: POST /api/v1/bot/orders/smartCopy/create",
2320
2299
  inputSchema: {
2321
2300
  type: "object",
2322
2301
  additionalProperties: false,
2323
- required: ["base", "quote", "buOrderData"],
2302
+ required: ["base", "quote", "bu_order_data"],
2324
2303
  properties: {
2325
2304
  base: { type: "string", description: "Base currency (e.g. BTC)" },
2326
2305
  quote: { type: "string", description: "Quote currency (e.g. USDT)" },
2327
- buOrderData: {
2306
+ bu_order_data: {
2328
2307
  type: "object",
2329
2308
  additionalProperties: false,
2330
- required: ["quoteInvestment", "leverageType"],
2309
+ required: ["quote_total_investment", "portfolio"],
2331
2310
  properties: {
2332
- quoteInvestment: { type: "string", description: "Investment amount in quote currency." },
2333
- leverageType: { type: "string", enum: ["follow", "fixed"], description: "Follow signal provider's leverage or use fixed value." },
2334
- leverage: { type: "number", description: "Custom leverage (required when leverageType is 'fixed')." },
2335
- maxInvestPerOrder: { type: "string", description: "Maximum investment per replicated order." },
2336
- copyMode: { type: "string", enum: ["fixed_amount", "fixed_ratio"], description: "Copy mode." }
2311
+ quote_total_investment: { type: "string", description: "Total investment in quote currency" },
2312
+ portfolio: {
2313
+ type: "array",
2314
+ items: {
2315
+ type: "object",
2316
+ additionalProperties: false,
2317
+ required: ["base", "signal_type", "leverage"],
2318
+ properties: {
2319
+ base: { type: "string", description: "Base currency for this signal" },
2320
+ signal_type: { type: "string", description: "Signal provider UUID" },
2321
+ leverage: { type: "integer", description: "Leverage multiplier" },
2322
+ percent: { type: "string", description: "Allocation fraction of total investment (e.g. '1' for 100%)" },
2323
+ signal_param: { type: "string", description: "Signal parameters as a JSON string" },
2324
+ profit_stop_ratio: { type: "string", description: "Take-profit ratio" },
2325
+ loss_stop_ratio: { type: "string", description: "Stop-loss ratio" }
2326
+ }
2327
+ },
2328
+ description: "Portfolio of signals to copy"
2329
+ },
2330
+ compound: { type: "boolean", description: "Enable compound reinvestment" },
2331
+ profit_stop_maker: { type: "boolean", description: "Enable profit stop maker" }
2337
2332
  }
2338
2333
  },
2339
- copyFrom: { type: "string", description: "Signal source / trader ID to copy from." },
2340
- copyBotOrderId: { type: "string", description: "Reference bot order ID for copying settings." },
2341
- __dryRun: { type: "boolean", description: "If true, validate and return resolved body without placing an order." }
2334
+ key_id: { type: "string", description: "Optional key ID" },
2335
+ note: { type: "string", description: "Optional note" },
2336
+ copy_from: { type: "string", description: "Source bot order ID to copy settings from" },
2337
+ copy_type: { type: "string", description: "Copy type" },
2338
+ __dryRun: { type: "boolean", description: "If true, return resolved body without placing an order." }
2342
2339
  }
2343
2340
  },
2344
2341
  async handler(args, { client, config }) {
@@ -2347,16 +2344,33 @@ function registerBotTools() {
2347
2344
  }
2348
2345
  const base = asNonEmptyString3(args.base, "base");
2349
2346
  const quote = asNonEmptyString3(args.quote, "quote");
2350
- const buOrderData = parseSmartCopyBuOrderData(asObject(args.buOrderData, "buOrderData"));
2351
- const body = { base, quote, buOrderData };
2352
- if (args.copyFrom != null) body.copyFrom = String(args.copyFrom);
2353
- if (args.copyBotOrderId != null) body.copyBotOrderId = String(args.copyBotOrderId);
2354
- if (args.__dryRun === true) {
2355
- return {
2356
- dryRun: true,
2357
- note: "No order was sent. Body matches smartCopy/create request.",
2358
- resolvedBody: body
2347
+ const rawBuOrderData = asObject(args.bu_order_data, "bu_order_data");
2348
+ const quote_total_investment = asNonEmptyString3(rawBuOrderData.quote_total_investment, "bu_order_data.quote_total_investment");
2349
+ if (!Array.isArray(rawBuOrderData.portfolio) || rawBuOrderData.portfolio.length === 0) {
2350
+ throw new Error('Invalid "bu_order_data.portfolio": expected non-empty array.');
2351
+ }
2352
+ const portfolio = rawBuOrderData.portfolio.map((item, i) => {
2353
+ const p = {
2354
+ base: asNonEmptyString3(item.base, `portfolio[${i}].base`),
2355
+ signal_type: asNonEmptyString3(item.signal_type, `portfolio[${i}].signal_type`),
2356
+ leverage: asPositiveInteger3(item.leverage, `portfolio[${i}].leverage`)
2359
2357
  };
2358
+ if (item.percent != null) p.percent = asNonEmptyString3(item.percent, `portfolio[${i}].percent`);
2359
+ if (item.signal_param != null) p.signal_param = String(item.signal_param);
2360
+ if (item.profit_stop_ratio != null) p.profit_stop_ratio = String(item.profit_stop_ratio);
2361
+ if (item.loss_stop_ratio != null) p.loss_stop_ratio = String(item.loss_stop_ratio);
2362
+ return p;
2363
+ });
2364
+ const buOrderData = { quote_total_investment, portfolio };
2365
+ if (rawBuOrderData.compound != null) buOrderData.compound = asBoolean2(rawBuOrderData.compound, "bu_order_data.compound");
2366
+ if (rawBuOrderData.profit_stop_maker != null) buOrderData.profit_stop_maker = asBoolean2(rawBuOrderData.profit_stop_maker, "bu_order_data.profit_stop_maker");
2367
+ const body = { base, quote, bu_order_data: buOrderData };
2368
+ if (args.key_id != null) body.key_id = String(args.key_id);
2369
+ if (args.note != null) body.note = String(args.note);
2370
+ if (args.copy_from != null) body.copy_from = String(args.copy_from);
2371
+ if (args.copy_type != null) body.copy_type = String(args.copy_type);
2372
+ if (args.__dryRun === true) {
2373
+ return { dryRun: true, note: "No order was sent.", resolvedBody: body };
2360
2374
  }
2361
2375
  return (await client.signedPost("/api/v1/bot/orders/smartCopy/create", body)).data;
2362
2376
  }
@@ -2369,23 +2383,21 @@ function registerBotTools() {
2369
2383
  inputSchema: {
2370
2384
  type: "object",
2371
2385
  additionalProperties: false,
2372
- required: ["buOrderId"],
2386
+ required: ["bu_order_id"],
2373
2387
  properties: {
2374
- buOrderId: { type: "string", description: "Smart copy bot order ID." },
2375
- closeSellModel: { type: "string", enum: ["NOT_SELL", "TO_QUOTE", "TO_USDT"], description: "How to handle the base asset on close." }
2388
+ bu_order_id: { type: "string", description: "Smart copy bot order ID." },
2389
+ close_note: { type: "string", description: "Optional close note." },
2390
+ convert_into_earn_coin: { type: "boolean", description: "Whether to convert remaining funds into earn coin." }
2376
2391
  }
2377
2392
  },
2378
2393
  async handler(args, { client, config }) {
2379
2394
  if (config.readOnly) {
2380
2395
  throw new Error("Server is running in --read-only mode; bot smart_copy cancel is disabled.");
2381
2396
  }
2382
- const buOrderId = asNonEmptyString3(args.buOrderId, "buOrderId");
2383
- const body = { buOrderId };
2384
- if (args.closeSellModel != null) {
2385
- const closeSellModel = asNonEmptyString3(args.closeSellModel, "closeSellModel");
2386
- assertEnum3(closeSellModel, "closeSellModel", ["NOT_SELL", "TO_QUOTE", "TO_USDT"]);
2387
- body.closeSellModel = closeSellModel;
2388
- }
2397
+ const bu_order_id = asNonEmptyString3(args.bu_order_id, "bu_order_id");
2398
+ const body = { bu_order_id };
2399
+ if (args.close_note != null) body.close_note = String(args.close_note);
2400
+ if (args.convert_into_earn_coin != null) body.convert_into_earn_coin = asBoolean2(args.convert_into_earn_coin, "convert_into_earn_coin");
2389
2401
  return (await client.signedPost("/api/v1/bot/orders/smartCopy/cancel", body)).data;
2390
2402
  }
2391
2403
  },
@@ -2394,24 +2406,49 @@ function registerBotTools() {
2394
2406
  name: "pionex_bot_signal_add_listener",
2395
2407
  module: "bot",
2396
2408
  isWrite: true,
2397
- description: "Subscribe to a signal provider / add a signal listener. Endpoint: POST /api/v1/bot/signal/listener",
2409
+ description: "Push a trading signal to the Pionex signal platform (signal provider use). The platform forwards the signal to all smart copy bots subscribed to the given signalType. Use action='buy' to open a position and action='sell' to close it. Endpoint: POST /api/v1/bot/signal/listener",
2398
2410
  inputSchema: {
2399
2411
  type: "object",
2400
2412
  additionalProperties: false,
2401
- required: ["signalSourceId"],
2413
+ required: ["signalType", "signalParam", "base", "quote", "time", "price", "data"],
2402
2414
  properties: {
2403
- signalSourceId: { type: "string", description: "Signal provider ID to subscribe to." },
2404
- listenMode: { type: "string", description: "Subscription mode." }
2415
+ signalType: { type: "string", description: "Signal provider UUID." },
2416
+ signalParam: { type: "string", description: "Signal parameters as a JSON string (use '{}' for no extra params)." },
2417
+ base: { type: "string", description: "Base currency (e.g. BTC)." },
2418
+ quote: { type: "string", description: "Quote currency (e.g. USDT)." },
2419
+ time: { type: "string", description: "Signal timestamp in RFC 3339 format (e.g. '2024-01-01T12:00:00Z')." },
2420
+ price: { type: "string", description: "Current price at time of signal (e.g. '85000')." },
2421
+ data: {
2422
+ type: "object",
2423
+ additionalProperties: false,
2424
+ required: ["action", "position_size", "contracts"],
2425
+ properties: {
2426
+ action: { type: "string", enum: ["buy", "sell"], description: "'buy' to open a position, 'sell' to close." },
2427
+ position_size: { type: "string", description: "Target position size as a fraction (e.g. '1' = 100%)." },
2428
+ contracts: { type: "string", description: "Number of contracts." },
2429
+ direction: { type: "string", description: "Optional trade direction." }
2430
+ }
2431
+ }
2405
2432
  }
2406
2433
  },
2407
2434
  async handler(args, { client, config }) {
2408
2435
  if (config.readOnly) {
2409
2436
  throw new Error("Server is running in --read-only mode; bot signal add_listener is disabled.");
2410
2437
  }
2411
- const signalSourceId = asNonEmptyString3(args.signalSourceId, "signalSourceId");
2412
- const body = { signalSourceId };
2413
- if (args.listenMode != null) body.listenMode = String(args.listenMode);
2414
- return (await client.signedPost("/api/v1/bot/signal/listener", body)).data;
2438
+ const signalType = asNonEmptyString3(args.signalType, "signalType");
2439
+ const signalParam = asNonEmptyString3(args.signalParam, "signalParam");
2440
+ const base = asNonEmptyString3(args.base, "base");
2441
+ const quote = asNonEmptyString3(args.quote, "quote");
2442
+ const time = asNonEmptyString3(args.time, "time");
2443
+ const price = asNonEmptyString3(args.price, "price");
2444
+ const rawData = asObject(args.data, "data");
2445
+ const action = asNonEmptyString3(rawData.action, "data.action");
2446
+ assertEnum3(action, "data.action", ["buy", "sell"]);
2447
+ const position_size = asNonEmptyString3(rawData.position_size, "data.position_size");
2448
+ const contracts = asNonEmptyString3(rawData.contracts, "data.contracts");
2449
+ const data = { action, position_size, contracts };
2450
+ if (rawData.direction != null) data.direction = String(rawData.direction);
2451
+ return (await client.signedPost("/api/v1/bot/signal/listener", { signalType, signalParam, base, quote, time, price, data })).data;
2415
2452
  }
2416
2453
  }
2417
2454
  ];