@okx_ai/okx-trade-cli 1.3.0 → 1.3.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1393,6 +1393,9 @@ function readNumber(args, key) {
1393
1393
  if (value === void 0 || value === null) {
1394
1394
  return void 0;
1395
1395
  }
1396
+ if (typeof value === "string" && /^-?\d+(\.\d+)?$/.test(value)) {
1397
+ return parseFloat(value);
1398
+ }
1396
1399
  if (typeof value !== "number" || Number.isNaN(value)) {
1397
1400
  throw new ValidationError(`Parameter "${key}" must be a number.`);
1398
1401
  }
@@ -1472,7 +1475,8 @@ var OKX_INST_TYPES = [
1472
1475
  "SWAP",
1473
1476
  "FUTURES",
1474
1477
  "OPTION",
1475
- "MARGIN"
1478
+ "MARGIN",
1479
+ "EVENTS"
1476
1480
  ];
1477
1481
  function publicRateLimit(key, rps = 20) {
1478
1482
  return {
@@ -1770,6 +1774,7 @@ var MODULES = [
1770
1774
  "futures",
1771
1775
  "option",
1772
1776
  "account",
1777
+ "event",
1773
1778
  ...EARN_SUB_MODULE_IDS,
1774
1779
  ...BOT_SUB_MODULE_IDS,
1775
1780
  "skills"
@@ -1963,7 +1968,7 @@ function registerAccountTools() {
1963
1968
  properties: {
1964
1969
  instType: {
1965
1970
  type: "string",
1966
- enum: ["SPOT", "MARGIN", "SWAP", "FUTURES", "OPTION"]
1971
+ enum: ["SPOT", "MARGIN", "SWAP", "FUTURES", "OPTION", "EVENTS"]
1967
1972
  },
1968
1973
  ccy: {
1969
1974
  type: "string",
@@ -2029,7 +2034,7 @@ function registerAccountTools() {
2029
2034
  properties: {
2030
2035
  instType: {
2031
2036
  type: "string",
2032
- enum: ["SWAP", "FUTURES", "MARGIN", "OPTION"],
2037
+ enum: ["SWAP", "FUTURES", "MARGIN", "OPTION", "EVENTS"],
2033
2038
  description: "Default SWAP"
2034
2039
  },
2035
2040
  instId: {
@@ -2090,7 +2095,7 @@ function registerAccountTools() {
2090
2095
  properties: {
2091
2096
  instType: {
2092
2097
  type: "string",
2093
- enum: ["SPOT", "MARGIN", "SWAP", "FUTURES", "OPTION"]
2098
+ enum: ["SPOT", "MARGIN", "SWAP", "FUTURES", "OPTION", "EVENTS"]
2094
2099
  },
2095
2100
  instId: {
2096
2101
  type: "string",
@@ -2201,14 +2206,14 @@ function registerAccountTools() {
2201
2206
  {
2202
2207
  name: "account_get_positions",
2203
2208
  module: "account",
2204
- description: "Get current open positions across all instrument types (MARGIN, SWAP, FUTURES, OPTION). Use swap_get_positions for SWAP/FUTURES-only queries.",
2209
+ description: "Get current open positions across all instrument types (MARGIN, SWAP, FUTURES, OPTION, EVENTS). Use swap_get_positions for SWAP/FUTURES-only queries.",
2205
2210
  isWrite: false,
2206
2211
  inputSchema: {
2207
2212
  type: "object",
2208
2213
  properties: {
2209
2214
  instType: {
2210
2215
  type: "string",
2211
- enum: ["MARGIN", "SWAP", "FUTURES", "OPTION"]
2216
+ enum: ["MARGIN", "SWAP", "FUTURES", "OPTION", "EVENTS"]
2212
2217
  },
2213
2218
  instId: {
2214
2219
  type: "string",
@@ -5118,6 +5123,765 @@ function registerAllEarnTools() {
5118
5123
  ];
5119
5124
  return tools.map(withDemoGuard);
5120
5125
  }
5126
+ function findDateIdx(parts) {
5127
+ for (let i = 1; i < parts.length; i++) {
5128
+ if (/^\d{6}$/.test(parts[i])) return i;
5129
+ }
5130
+ return -1;
5131
+ }
5132
+ function inferExpiryMsFromInstId(instId) {
5133
+ const parts = instId.split("-");
5134
+ const upper = instId.toUpperCase();
5135
+ const dateIdx = findDateIdx(parts);
5136
+ if (dateIdx < 0) return null;
5137
+ const dp = parts[dateIdx];
5138
+ const year = 2e3 + parseInt(dp.slice(0, 2), 10);
5139
+ const month = parseInt(dp.slice(2, 4), 10) - 1;
5140
+ const day = parseInt(dp.slice(4, 6), 10);
5141
+ const isUpDown = upper.includes("UPDOWN");
5142
+ let timePart;
5143
+ if (isUpDown) {
5144
+ const candidate = parts[dateIdx + 2];
5145
+ timePart = candidate && /^\d{4}$/.test(candidate) ? candidate : parts[dateIdx + 1];
5146
+ } else {
5147
+ timePart = parts[dateIdx + 1];
5148
+ }
5149
+ if (!timePart || !/^\d{4}$/.test(timePart)) return null;
5150
+ const hour = parseInt(timePart.slice(0, 2), 10);
5151
+ const min = parseInt(timePart.slice(2, 4), 10);
5152
+ return Date.UTC(year, month, day, hour - 8, min, 0, 0);
5153
+ }
5154
+ function extractSeriesId(instId) {
5155
+ const parts = instId.split("-");
5156
+ for (let i = 0; i < parts.length; i++) {
5157
+ if (/^\d{6}$/.test(parts[i])) {
5158
+ return parts.slice(0, i).join("-");
5159
+ }
5160
+ }
5161
+ return instId;
5162
+ }
5163
+ function fmtTimeToken(t) {
5164
+ return t.length === 4 ? `${t.slice(0, 2)}:${t.slice(2)}` : t;
5165
+ }
5166
+ function fmtUpDownName(seriesId, dateStr, parts, dateIdx) {
5167
+ const t1 = parts[dateIdx + 1] ?? "";
5168
+ const t2 = parts[dateIdx + 2] ?? "";
5169
+ const timeRange = t1 && t2 ? ` ${fmtTimeToken(t1)}-${fmtTimeToken(t2)}` : "";
5170
+ return `${seriesId} Up/Down \xB7 ${dateStr}${timeRange}`;
5171
+ }
5172
+ function fmtStrikeName(seriesId, dateStr, label, parts, dateIdx) {
5173
+ const strike = parts[dateIdx + 2] ?? "";
5174
+ const strikeStr = strike && /^\d+$/.test(strike) ? Number(strike).toLocaleString("en-US") : "";
5175
+ return strikeStr ? `${seriesId} ${label} ${strikeStr} \xB7 ${dateStr}` : `${seriesId} \xB7 ${dateStr}`;
5176
+ }
5177
+ function formatDisplayTitle(instId) {
5178
+ const parts = instId.split("-");
5179
+ const upper = instId.toUpperCase();
5180
+ const seriesId = parts[0] ?? instId;
5181
+ const dateIdx = findDateIdx(parts);
5182
+ if (dateIdx < 0) return instId;
5183
+ const d = parts[dateIdx];
5184
+ const month = parseInt(d.slice(2, 4), 10);
5185
+ const day = parseInt(d.slice(4, 6), 10);
5186
+ const dateStr = `${month}/${day}`;
5187
+ if (upper.includes("UPDOWN") || upper.includes("UP-DOWN")) {
5188
+ return fmtUpDownName(seriesId, dateStr, parts, dateIdx);
5189
+ }
5190
+ if (upper.includes("ABOVE")) {
5191
+ return fmtStrikeName(seriesId, dateStr, "above", parts, dateIdx);
5192
+ }
5193
+ if (upper.includes("TOUCH")) {
5194
+ return fmtStrikeName(seriesId, dateStr, "touch", parts, dateIdx);
5195
+ }
5196
+ return `${seriesId} \xB7 ${dateStr}`;
5197
+ }
5198
+ var OUTCOME_LABELS = {
5199
+ "0": "pending",
5200
+ "1": "YES",
5201
+ "2": "NO"
5202
+ };
5203
+ var ORDER_STATE_MAP = {
5204
+ live: "Unfilled",
5205
+ partially_filled: "Partially filled",
5206
+ filled: "Filled",
5207
+ canceled: "Canceled",
5208
+ mmp_canceled: "Canceled"
5209
+ };
5210
+ function mapOrderState(raw) {
5211
+ return ORDER_STATE_MAP[raw] ?? raw;
5212
+ }
5213
+ var TIMESTAMP_FIELDS = /* @__PURE__ */ new Set([
5214
+ "expTime",
5215
+ "settleTime",
5216
+ "listTime",
5217
+ "uTime",
5218
+ "cTime",
5219
+ "fixTime"
5220
+ ]);
5221
+ var DEFAULT_SETTLE_CCY = "USDT";
5222
+ function convertTimestamps(item) {
5223
+ const result = { ...item };
5224
+ for (const key of TIMESTAMP_FIELDS) {
5225
+ if (!(key in result)) continue;
5226
+ const v = Number(result[key]);
5227
+ if (v > 0) {
5228
+ const d = new Date(v + 8 * 60 * 60 * 1e3);
5229
+ const yyyy = d.getUTCFullYear();
5230
+ const mo = String(d.getUTCMonth() + 1).padStart(2, "0");
5231
+ const dd = String(d.getUTCDate()).padStart(2, "0");
5232
+ const hh = String(d.getUTCHours()).padStart(2, "0");
5233
+ const mi = String(d.getUTCMinutes()).padStart(2, "0");
5234
+ result[key] = `${yyyy}-${mo}-${dd} ${hh}:${mi} UTC+8`;
5235
+ } else {
5236
+ delete result[key];
5237
+ }
5238
+ }
5239
+ return result;
5240
+ }
5241
+ function normalizeWrite3(response) {
5242
+ const data = response.data;
5243
+ if (Array.isArray(data) && data.length > 0) {
5244
+ const failed = data.filter(
5245
+ (item) => item !== null && typeof item === "object" && "sCode" in item && item["sCode"] !== "0"
5246
+ );
5247
+ if (failed.length > 0) {
5248
+ const messages2 = failed.map(
5249
+ (item) => `[${item["sCode"]}] ${item["sMsg"] ?? "Operation failed"}`
5250
+ );
5251
+ throw new OkxApiError(messages2.join("; "), {
5252
+ code: String(failed[0]["sCode"] ?? ""),
5253
+ endpoint: response.endpoint
5254
+ });
5255
+ }
5256
+ }
5257
+ return { endpoint: response.endpoint, requestTime: response.requestTime, data };
5258
+ }
5259
+ function extractQuoteCcy(underlying) {
5260
+ if (!underlying) return DEFAULT_SETTLE_CCY;
5261
+ const parts = underlying.split("-");
5262
+ return parts.length >= 2 ? parts[parts.length - 1].toUpperCase() : DEFAULT_SETTLE_CCY;
5263
+ }
5264
+ async function fetchAvailableBalance(client, ccy = DEFAULT_SETTLE_CCY) {
5265
+ try {
5266
+ const r = await client.privateGet(
5267
+ "/api/v5/account/balance",
5268
+ { ccy }
5269
+ );
5270
+ const data = r["data"];
5271
+ if (Array.isArray(data) && data.length > 0) {
5272
+ const details = data[0]["details"];
5273
+ if (Array.isArray(details) && details.length > 0) {
5274
+ const entry = details.find(
5275
+ (d) => String(d["ccy"] ?? "").toUpperCase() === ccy.toUpperCase()
5276
+ );
5277
+ if (!entry) return { balance: null, ccy };
5278
+ const bal = String(entry["availBal"] ?? "") || null;
5279
+ return { balance: bal, ccy };
5280
+ }
5281
+ }
5282
+ } catch {
5283
+ }
5284
+ return { balance: null, ccy };
5285
+ }
5286
+ var KNOWN_UNDERLYINGS = /^(BTC|ETH|TRX|EOS|SOL|IOTA|KISHU|SUSHI|BTG|XTZ|SOLVU)/i;
5287
+ function extractUnderlying(seriesId) {
5288
+ const m = seriesId.match(KNOWN_UNDERLYINGS);
5289
+ return m ? m[1].toUpperCase() : null;
5290
+ }
5291
+ function resolveOutcome(value) {
5292
+ const map = {
5293
+ up: "yes",
5294
+ yes: "yes",
5295
+ down: "no",
5296
+ no: "no"
5297
+ };
5298
+ const resolved = map[value.toLowerCase()];
5299
+ if (!resolved) {
5300
+ throw new Error(
5301
+ `Invalid outcome "${value}". Use: UP or YES for Up/Yes, DOWN or NO for Down/No.`
5302
+ );
5303
+ }
5304
+ return resolved;
5305
+ }
5306
+ function filterBrowseCandidates(allSeries, underlyingFilter) {
5307
+ const isHumanReadable = (id) => /^(BTC|ETH|TRX|EOS|SOL|IOTA|KISHU|SUSHI|BTG|XTZ|SOLVU)-/.test(id);
5308
+ const seen = /* @__PURE__ */ new Set();
5309
+ const candidates = [];
5310
+ for (const s of allSeries) {
5311
+ const settlement = s["settlement"];
5312
+ const method = String(settlement?.["method"] ?? "");
5313
+ const uly = String(settlement?.["underlying"] ?? "");
5314
+ if (underlyingFilter && !uly.startsWith(underlyingFilter)) continue;
5315
+ const key = `${method}:${uly}`;
5316
+ if (!seen.has(key) && isHumanReadable(String(s["seriesId"] ?? ""))) {
5317
+ seen.add(key);
5318
+ candidates.push(s);
5319
+ }
5320
+ }
5321
+ return candidates;
5322
+ }
5323
+ function isActiveMarket(m, isUpDown, now) {
5324
+ if (!isUpDown && (!m["floorStrike"] || m["floorStrike"] === "")) return false;
5325
+ const expMs = Number(m["expTime"] ?? 0);
5326
+ return expMs <= 0 || expMs > now;
5327
+ }
5328
+ function toContractSummary(m) {
5329
+ const converted = convertTimestamps(m);
5330
+ const id = String(m["instId"] ?? "");
5331
+ return {
5332
+ instId: id,
5333
+ displayTitle: formatDisplayTitle(id),
5334
+ expTime: converted["expTime"],
5335
+ floorStrike: m["floorStrike"],
5336
+ px: m["px"],
5337
+ outcome: OUTCOME_LABELS[String(m["outcome"] ?? "")] ?? m["outcome"]
5338
+ };
5339
+ }
5340
+ async function fetchActiveContractsForSeries(client, s) {
5341
+ const seriesId = String(s["seriesId"] ?? "");
5342
+ const settlement = s["settlement"];
5343
+ const method = String(settlement?.["method"] ?? "");
5344
+ const isUpDown = method === "price_up_down";
5345
+ try {
5346
+ const r = await client.privateGet(
5347
+ "/api/v5/public/event-contract/markets",
5348
+ compactObject({ seriesId, state: "live" }),
5349
+ privateRateLimit("event_browse", 20)
5350
+ );
5351
+ const normalized = normalizeResponse(r);
5352
+ const markets = Array.isArray(normalized["data"]) ? normalized["data"] : [];
5353
+ const now = Date.now();
5354
+ const active = markets.filter((m) => isActiveMarket(m, isUpDown, now)).map(toContractSummary);
5355
+ if (active.length === 0) return null;
5356
+ return {
5357
+ seriesId,
5358
+ method: String(settlement?.["method"] ?? ""),
5359
+ underlying: String(settlement?.["underlying"] ?? ""),
5360
+ freq: String(s["freq"] ?? ""),
5361
+ contracts: active
5362
+ };
5363
+ } catch {
5364
+ return null;
5365
+ }
5366
+ }
5367
+ function resolveUnderlyingFromSeriesResp(seriesResp) {
5368
+ if (!seriesResp) return null;
5369
+ const sResp = seriesResp;
5370
+ const sData = Array.isArray(sResp["data"]) ? sResp["data"] : [];
5371
+ if (sData.length > 0) {
5372
+ const settlement = sData[0]["settlement"];
5373
+ return String(settlement?.["underlying"] ?? "") || null;
5374
+ }
5375
+ return null;
5376
+ }
5377
+ function translateAndSortMarkets(rawData, limit) {
5378
+ const sorted = [...rawData].sort((a, b) => {
5379
+ const tA = Number(a["expTime"] ?? 0);
5380
+ const tB = Number(b["expTime"] ?? 0);
5381
+ return tA - tB;
5382
+ });
5383
+ const sliced = limit && limit > 0 ? sorted.slice(0, limit) : sorted;
5384
+ return sliced.map((item) => {
5385
+ const converted = convertTimestamps(item);
5386
+ if (typeof converted["outcome"] === "string") {
5387
+ converted["outcome"] = OUTCOME_LABELS[converted["outcome"]] ?? converted["outcome"];
5388
+ }
5389
+ converted["displayTitle"] = formatDisplayTitle(String(item["instId"] ?? ""));
5390
+ return converted;
5391
+ });
5392
+ }
5393
+ function enrichFill(item) {
5394
+ const subType = String(item["subType"] ?? "");
5395
+ const isSettle = subType === "414" || subType === "415";
5396
+ const enriched = {
5397
+ ...item,
5398
+ displayTitle: formatDisplayTitle(String(item["instId"] ?? "")),
5399
+ outcome: OUTCOME_LABELS[String(item["outcome"] ?? "")] ?? item["outcome"],
5400
+ type: isSettle ? "settlement" : "fill"
5401
+ };
5402
+ if (isSettle) {
5403
+ const fillPnl = parseFloat(String(item["fillPnl"] ?? "NaN"));
5404
+ const isWin = subType === "414";
5405
+ enriched["settlementResult"] = isWin ? "win" : "loss";
5406
+ enriched["pnl"] = isNaN(fillPnl) ? void 0 : fillPnl;
5407
+ }
5408
+ return enriched;
5409
+ }
5410
+ async function fetchIdxPx(client, underlying) {
5411
+ try {
5412
+ const r = await client.publicGet(
5413
+ "/api/v5/market/index-tickers",
5414
+ { instId: underlying },
5415
+ publicRateLimit("fetchIdxPx", 20)
5416
+ );
5417
+ const data = r["data"];
5418
+ if (Array.isArray(data) && data.length > 0) {
5419
+ return String(data[0]["idxPx"] ?? "") || null;
5420
+ }
5421
+ } catch {
5422
+ }
5423
+ return null;
5424
+ }
5425
+ function handlePlaceOrderError(base, rawArgs, endpoint) {
5426
+ if (!Array.isArray(base["data"])) return;
5427
+ const item = base["data"][0];
5428
+ const sCode = item && String(item["sCode"] ?? "");
5429
+ if (!sCode || sCode === "0") return;
5430
+ const sMsg = String(item["sMsg"] ?? "Order failed");
5431
+ if (sCode === "51001") {
5432
+ const instId = requireString(asRecord(rawArgs), "instId");
5433
+ const seriesId = extractSeriesId(instId);
5434
+ const expiryMs = inferExpiryMsFromInstId(instId);
5435
+ const isExpired = expiryMs !== null && expiryMs < Date.now();
5436
+ const reason = isExpired ? `The contract (${instId}) has expired.` : `The contract (${instId}) was not found \u2014 it may not exist or has not started yet.`;
5437
+ throw new OkxApiError(
5438
+ `${reason} Ask the user if they'd like to place the same order on the next session. If yes, call event_get_markets with seriesId=${seriesId} and state=live to find available contracts.`,
5439
+ { code: sCode, endpoint }
5440
+ );
5441
+ }
5442
+ throw new OkxApiError(`[${sCode}] ${sMsg}`, { code: sCode, endpoint });
5443
+ }
5444
+ var OUTCOME_SCHEMA = {
5445
+ type: "string",
5446
+ enum: ["UP", "YES", "DOWN", "NO"],
5447
+ description: `Which outcome to trade.
5448
+ UP/DOWN direction contracts: UP (price rises during the period) or DOWN (price falls).
5449
+ YES/NO price-target or touch contracts: YES (condition met) or NO (condition not met).
5450
+ Check the series type from event_get_series to determine which applies.
5451
+ NOTE: px is the event contract price (0.01\u20130.99), NOT the underlying asset price. It reflects market-implied probability when actively trading.`
5452
+ };
5453
+ function registerEventContractTools() {
5454
+ return [
5455
+ // -----------------------------------------------------------------------
5456
+ // Read-only — browse (user-facing) + series / events / markets (internal)
5457
+ // -----------------------------------------------------------------------
5458
+ {
5459
+ name: "event_browse",
5460
+ module: "event",
5461
+ description: "Browse currently active (in-progress) event contracts. Call when user asks what event contracts are available to trade. Internally fetches series and live markets in parallel, returns only in-progress contracts (floorStrike set). If a live quote field px is present, it is the event contract price (0.01\u20130.99), not the underlying asset price; it reflects the market-implied probability when actively trading. Grouped by settlement type and underlying.",
5462
+ isWrite: false,
5463
+ inputSchema: {
5464
+ type: "object",
5465
+ properties: {
5466
+ underlying: {
5467
+ type: "string",
5468
+ description: "Filter by underlying asset, e.g. BTC-USD, ETH-USD. Omit for all."
5469
+ }
5470
+ }
5471
+ },
5472
+ handler: async (rawArgs, context) => {
5473
+ const args = asRecord(rawArgs);
5474
+ const underlyingFilter = readString(args, "underlying");
5475
+ const seriesResp = await context.client.privateGet(
5476
+ "/api/v5/public/event-contract/series",
5477
+ compactObject({}),
5478
+ privateRateLimit("event_browse", 10)
5479
+ );
5480
+ const normalizedSeries = normalizeResponse(seriesResp);
5481
+ const allSeries = Array.isArray(normalizedSeries["data"]) ? normalizedSeries["data"] : [];
5482
+ const candidates = filterBrowseCandidates(allSeries, underlyingFilter);
5483
+ const marketResults = await Promise.all(
5484
+ candidates.map((s) => fetchActiveContractsForSeries(context.client, s))
5485
+ );
5486
+ const results = marketResults.filter(Boolean);
5487
+ return {
5488
+ data: results,
5489
+ total: results.reduce((n, r) => n + (r?.contracts?.length ?? 0), 0)
5490
+ };
5491
+ }
5492
+ },
5493
+ {
5494
+ name: "event_get_series",
5495
+ module: "event",
5496
+ description: "List event contract series. Returns all available series with settlement type and underlying. Use event_browse to see currently active contracts.",
5497
+ isWrite: false,
5498
+ inputSchema: {
5499
+ type: "object",
5500
+ properties: {
5501
+ seriesId: {
5502
+ type: "string",
5503
+ description: "Filter by series ID, e.g. BTC-ABOVE-DAILY. Omit for all."
5504
+ }
5505
+ }
5506
+ },
5507
+ handler: async (rawArgs, context) => {
5508
+ const args = asRecord(rawArgs);
5509
+ const response = await context.client.privateGet(
5510
+ "/api/v5/public/event-contract/series",
5511
+ compactObject({ seriesId: readString(args, "seriesId") }),
5512
+ privateRateLimit("event_get_series", 20)
5513
+ );
5514
+ return normalizeResponse(response);
5515
+ }
5516
+ },
5517
+ {
5518
+ name: "event_get_events",
5519
+ module: "event",
5520
+ description: "List expiry periods within a series. state: preopen|live|settling|expired. expTime is pre-formatted UTC+8.",
5521
+ isWrite: false,
5522
+ inputSchema: {
5523
+ type: "object",
5524
+ properties: {
5525
+ seriesId: {
5526
+ type: "string",
5527
+ description: "Series ID, e.g. BTC-ABOVE-DAILY (required)"
5528
+ },
5529
+ eventId: {
5530
+ type: "string",
5531
+ description: "Filter by event ID, e.g. BTC-ABOVE-DAILY-260224-1600"
5532
+ },
5533
+ state: {
5534
+ type: "string",
5535
+ enum: ["preopen", "live", "settling", "expired"],
5536
+ description: "preopen=markets not yet trading; live=active; settling=awaiting settlement; expired=done"
5537
+ },
5538
+ limit: {
5539
+ type: "number",
5540
+ description: "Max results (default 100, max 100)"
5541
+ },
5542
+ before: {
5543
+ type: "string",
5544
+ description: "Pagination: return records newer than this expTime"
5545
+ },
5546
+ after: {
5547
+ type: "string",
5548
+ description: "Pagination: return records older than this expTime"
5549
+ }
5550
+ },
5551
+ required: ["seriesId"]
5552
+ },
5553
+ handler: async (rawArgs, context) => {
5554
+ const args = asRecord(rawArgs);
5555
+ const response = await context.client.privateGet(
5556
+ "/api/v5/public/event-contract/events",
5557
+ compactObject({
5558
+ seriesId: requireString(args, "seriesId"),
5559
+ eventId: readString(args, "eventId"),
5560
+ state: readString(args, "state"),
5561
+ limit: readNumber(args, "limit"),
5562
+ before: readString(args, "before"),
5563
+ after: readString(args, "after")
5564
+ }),
5565
+ privateRateLimit("event_get_events", 20)
5566
+ );
5567
+ const base = normalizeResponse(response);
5568
+ const data = Array.isArray(base["data"]) ? base["data"].map(convertTimestamps) : base["data"];
5569
+ return { ...base, data };
5570
+ }
5571
+ },
5572
+ {
5573
+ name: "event_get_markets",
5574
+ module: "event",
5575
+ description: "List tradeable contracts within a series. state=live for active contracts, state=expired for settlement results. floorStrike=strike price; px (when present) is the event contract price (0.01\u20130.99), not the underlying asset price \u2014 reflects the market-implied probability when actively trading; outcome pre-translated (pending/YES/NO/UP/DOWN); timestamps UTC+8.",
5576
+ isWrite: false,
5577
+ inputSchema: {
5578
+ type: "object",
5579
+ properties: {
5580
+ seriesId: {
5581
+ type: "string",
5582
+ description: "Series ID, e.g. BTC-ABOVE-DAILY (required)"
5583
+ },
5584
+ eventId: {
5585
+ type: "string",
5586
+ description: "Filter by event ID, e.g. BTC-ABOVE-DAILY-260224-1600"
5587
+ },
5588
+ instId: {
5589
+ type: "string",
5590
+ description: "Filter by instrument ID"
5591
+ },
5592
+ state: {
5593
+ type: "string",
5594
+ enum: ["preopen", "live", "settling", "expired"],
5595
+ description: "preopen=not yet trading; live=active; settling=awaiting settlement; expired=settled"
5596
+ },
5597
+ limit: {
5598
+ type: "number",
5599
+ description: "Max results (default 100, max 100)"
5600
+ },
5601
+ before: {
5602
+ type: "string",
5603
+ description: "Pagination: return records newer than this expTime"
5604
+ },
5605
+ after: {
5606
+ type: "string",
5607
+ description: "Pagination: return records older than this expTime"
5608
+ }
5609
+ },
5610
+ required: ["seriesId"]
5611
+ },
5612
+ handler: async (rawArgs, context) => {
5613
+ const args = asRecord(rawArgs);
5614
+ const seriesId = requireString(args, "seriesId");
5615
+ const knownUnderlying = extractUnderlying(seriesId);
5616
+ const [marketsResp, seriesResp, idxPxFromKnown] = await Promise.all([
5617
+ context.client.privateGet(
5618
+ "/api/v5/public/event-contract/markets",
5619
+ compactObject({
5620
+ seriesId,
5621
+ eventId: readString(args, "eventId"),
5622
+ instId: readString(args, "instId"),
5623
+ state: readString(args, "state"),
5624
+ before: readString(args, "before"),
5625
+ after: readString(args, "after")
5626
+ }),
5627
+ privateRateLimit("event_get_markets", 20)
5628
+ ),
5629
+ knownUnderlying ? Promise.resolve(null) : context.client.privateGet(
5630
+ "/api/v5/public/event-contract/series",
5631
+ compactObject({ seriesId }),
5632
+ privateRateLimit("event_get_series", 20)
5633
+ ),
5634
+ knownUnderlying ? fetchIdxPx(context.client, knownUnderlying + "-USDT") : Promise.resolve(null)
5635
+ ]);
5636
+ let underlying = knownUnderlying ? knownUnderlying + "-USDT" : null;
5637
+ if (!underlying) {
5638
+ underlying = resolveUnderlyingFromSeriesResp(seriesResp);
5639
+ }
5640
+ const idxPx = idxPxFromKnown ?? (underlying && !knownUnderlying ? await fetchIdxPx(context.client, underlying) : null);
5641
+ const base = normalizeResponse(marketsResp);
5642
+ const limit = readNumber(args, "limit");
5643
+ const rawData = Array.isArray(base["data"]) ? base["data"] : [];
5644
+ const translated = translateAndSortMarkets(rawData, limit);
5645
+ return {
5646
+ ...base,
5647
+ data: translated,
5648
+ currentIdxPx: idxPx,
5649
+ underlying
5650
+ };
5651
+ }
5652
+ },
5653
+ {
5654
+ name: "event_get_orders",
5655
+ module: "event",
5656
+ description: "Query event contract orders. state=live for open orders; omit for history. outcome pre-translated (YES/NO/UP/DOWN).",
5657
+ isWrite: false,
5658
+ inputSchema: {
5659
+ type: "object",
5660
+ properties: {
5661
+ instId: {
5662
+ type: "string",
5663
+ description: "Event contract instrument ID"
5664
+ },
5665
+ state: {
5666
+ type: "string",
5667
+ description: "live=pending orders; omit for history"
5668
+ },
5669
+ limit: {
5670
+ type: "number",
5671
+ description: "Max results (default 20)"
5672
+ }
5673
+ }
5674
+ },
5675
+ handler: async (rawArgs, context) => {
5676
+ const args = asRecord(rawArgs);
5677
+ const state = readString(args, "state");
5678
+ const isPending = state === "live";
5679
+ const endpoint = isPending ? "/api/v5/trade/orders-pending" : "/api/v5/trade/orders-history";
5680
+ const response = await context.client.privateGet(
5681
+ endpoint,
5682
+ compactObject({
5683
+ instType: "EVENTS",
5684
+ instId: readString(args, "instId"),
5685
+ limit: readNumber(args, "limit")
5686
+ }),
5687
+ privateRateLimit("event_get_orders", 20)
5688
+ );
5689
+ const base = normalizeResponse(response);
5690
+ const data = Array.isArray(base["data"]) ? base["data"].map((item) => ({
5691
+ ...item,
5692
+ displayTitle: formatDisplayTitle(String(item["instId"] ?? "")),
5693
+ outcome: OUTCOME_LABELS[String(item["outcome"] ?? "")] ?? item["outcome"],
5694
+ state: String(item["state"] ?? ""),
5695
+ stateLabel: mapOrderState(String(item["state"] ?? ""))
5696
+ })) : base["data"];
5697
+ return { ...base, data };
5698
+ }
5699
+ },
5700
+ {
5701
+ name: "event_get_fills",
5702
+ module: "event",
5703
+ description: "Get event contract fill history. outcome pre-translated (YES/NO/UP/DOWN). Each record includes a 'type' field: 'fill' (subType 410, opening trade) or 'settlement' (subType 414 win / subType 415 loss, contract expiry payout). Settlement records include 'settlementResult' (win/loss) and 'pnl' fields \u2014 no separate market lookup needed to determine outcome.",
5704
+ isWrite: false,
5705
+ inputSchema: {
5706
+ type: "object",
5707
+ properties: {
5708
+ instId: {
5709
+ type: "string",
5710
+ description: "Event contract instrument ID"
5711
+ },
5712
+ limit: {
5713
+ type: "number",
5714
+ description: "Max results (default 20)"
5715
+ }
5716
+ }
5717
+ },
5718
+ handler: async (rawArgs, context) => {
5719
+ const args = asRecord(rawArgs);
5720
+ const response = await context.client.privateGet(
5721
+ "/api/v5/trade/fills",
5722
+ compactObject({
5723
+ instType: "EVENTS",
5724
+ instId: readString(args, "instId"),
5725
+ limit: readNumber(args, "limit")
5726
+ }),
5727
+ privateRateLimit("event_get_fills", 20)
5728
+ );
5729
+ const base = normalizeResponse(response);
5730
+ const data = Array.isArray(base["data"]) ? base["data"].map(enrichFill) : base["data"];
5731
+ return { ...base, data };
5732
+ }
5733
+ },
5734
+ // -----------------------------------------------------------------------
5735
+ // Private — write
5736
+ // -----------------------------------------------------------------------
5737
+ {
5738
+ name: "event_place_order",
5739
+ module: "event",
5740
+ description: `Place an event contract order. [CAUTION] Places a real order.
5741
+ - outcome: UP/YES (bet price goes up/condition met) or DOWN/NO (bet price goes down/condition not met)
5742
+ - For limit orders: px is the event contract price (0.01\u20130.99), NOT the underlying asset price. It reflects market-implied probability when actively trading
5743
+ - tdMode is always isolated; speedBump is auto-set per exchange requirement \u2014 do not pass either`,
5744
+ isWrite: true,
5745
+ inputSchema: {
5746
+ type: "object",
5747
+ properties: {
5748
+ instId: {
5749
+ type: "string",
5750
+ description: "Event contract instrument ID, e.g. BTC-ABOVE-DAILY-260224-1600-120000"
5751
+ },
5752
+ side: {
5753
+ type: "string",
5754
+ enum: ["buy", "sell"],
5755
+ description: "buy=open position, sell=close position"
5756
+ },
5757
+ outcome: OUTCOME_SCHEMA,
5758
+ ordType: {
5759
+ type: "string",
5760
+ enum: ["market", "limit", "post_only"],
5761
+ description: "Order type (default market)"
5762
+ },
5763
+ sz: {
5764
+ type: "string",
5765
+ description: "For limit/post_only: number of contracts. For market: quote currency amount (server converts to contracts using best available price; actual fill count may differ)."
5766
+ },
5767
+ px: {
5768
+ type: "string",
5769
+ description: "Event contract price (0.01\u20130.99). Required when ordType=limit. Do NOT use for market orders."
5770
+ }
5771
+ },
5772
+ required: ["instId", "side", "outcome", "sz"]
5773
+ },
5774
+ handler: async (rawArgs, context) => {
5775
+ const args = asRecord(rawArgs);
5776
+ const ordType = readString(args, "ordType") ?? "market";
5777
+ const speedBump = ordType !== "post_only" ? "1" : void 0;
5778
+ const instId = requireString(args, "instId");
5779
+ const response = await context.client.privatePost(
5780
+ "/api/v5/trade/order",
5781
+ compactObject({
5782
+ instId,
5783
+ tdMode: "isolated",
5784
+ side: requireString(args, "side"),
5785
+ outcome: resolveOutcome(requireString(args, "outcome")),
5786
+ ordType,
5787
+ sz: requireString(args, "sz"),
5788
+ px: readString(args, "px"),
5789
+ speedBump,
5790
+ tag: context.config.sourceTag
5791
+ }),
5792
+ privateRateLimit("event_place_order", 60)
5793
+ );
5794
+ const base = normalizeResponse(response);
5795
+ handlePlaceOrderError(base, asRecord(rawArgs), response.endpoint);
5796
+ const data = Array.isArray(base["data"]) ? base["data"].map(({ tag: _t, ...rest }) => rest) : base["data"];
5797
+ const placeSeriesId = extractSeriesId(instId);
5798
+ const placeUnderlying = extractUnderlying(placeSeriesId);
5799
+ const placeCcy = extractQuoteCcy(placeUnderlying ? placeUnderlying + "-USDT" : null);
5800
+ const balResult = await fetchAvailableBalance(context.client, placeCcy);
5801
+ const result = { ...base, data };
5802
+ if (balResult.balance) {
5803
+ result["availableBalance"] = balResult.balance;
5804
+ result["availableBalanceCcy"] = balResult.ccy;
5805
+ }
5806
+ if (ordType === "market") {
5807
+ result["orderNote"] = "Market order: sz is a quote currency amount. The exchange converts it to contracts based on best available price.";
5808
+ }
5809
+ return result;
5810
+ }
5811
+ },
5812
+ {
5813
+ name: "event_amend_order",
5814
+ module: "event",
5815
+ description: "Amend a pending event contract order (change price or size). [CAUTION] Modifies a real order. Only limit/post_only orders can be amended.",
5816
+ isWrite: true,
5817
+ inputSchema: {
5818
+ type: "object",
5819
+ properties: {
5820
+ instId: { type: "string", description: "Event contract instrument ID" },
5821
+ ordId: { type: "string", description: "Order ID to amend" },
5822
+ newPx: { type: "string", description: "New event contract price (0.01\u20130.99). Omit to keep current." },
5823
+ newSz: { type: "string", description: "New size in contracts (omit to keep current)" }
5824
+ },
5825
+ required: ["instId", "ordId"]
5826
+ },
5827
+ handler: async (rawArgs, context) => {
5828
+ const args = asRecord(rawArgs);
5829
+ const response = await context.client.privatePost(
5830
+ "/api/v5/trade/amend-order",
5831
+ compactObject({
5832
+ instId: requireString(args, "instId"),
5833
+ ordId: requireString(args, "ordId"),
5834
+ newPx: readString(args, "newPx"),
5835
+ newSz: readString(args, "newSz"),
5836
+ speedBump: "1"
5837
+ }),
5838
+ privateRateLimit("event_amend_order", 60)
5839
+ );
5840
+ return normalizeWrite3(response);
5841
+ }
5842
+ },
5843
+ {
5844
+ name: "event_cancel_order",
5845
+ module: "event",
5846
+ description: "Cancel a pending event contract order. [CAUTION] Cancels a real order. instId must be the full event contract instrument ID (e.g. BTC-ABOVE-DAILY-260224-1600-69700), NOT a spot trading pair.",
5847
+ isWrite: true,
5848
+ inputSchema: {
5849
+ type: "object",
5850
+ properties: {
5851
+ instId: {
5852
+ type: "string",
5853
+ description: "Event contract instrument ID"
5854
+ },
5855
+ ordId: {
5856
+ type: "string",
5857
+ description: "Order ID to cancel"
5858
+ }
5859
+ },
5860
+ required: ["instId", "ordId"]
5861
+ },
5862
+ handler: async (rawArgs, context) => {
5863
+ const args = asRecord(rawArgs);
5864
+ const instId = requireString(args, "instId");
5865
+ const response = await context.client.privatePost(
5866
+ "/api/v5/trade/cancel-order",
5867
+ { instId, ordId: requireString(args, "ordId") },
5868
+ privateRateLimit("event_cancel_order", 60)
5869
+ );
5870
+ if (Array.isArray(response.data) && response.data.length > 0) {
5871
+ const item = response.data[0];
5872
+ const sCode = item && String(item["sCode"] ?? "");
5873
+ if (sCode === "51001") {
5874
+ const expiryMs = inferExpiryMsFromInstId(instId);
5875
+ const isExpired = expiryMs !== null && expiryMs < Date.now();
5876
+ const reason = isExpired ? `The contract (${instId}) has already expired \u2014 the order was auto-cancelled at settlement. Check event_get_fills to confirm the outcome.` : `Instrument (${instId}) not found. Verify the instId with event_get_markets before retrying.`;
5877
+ throw new OkxApiError(reason, { code: sCode, endpoint: response.endpoint });
5878
+ }
5879
+ }
5880
+ return normalizeWrite3(response);
5881
+ }
5882
+ }
5883
+ ];
5884
+ }
5121
5885
  function buildContractTradeTools(cfg) {
5122
5886
  const { prefix, module, label, instTypes, instIdExample } = cfg;
5123
5887
  const [defaultType, otherType] = instTypes;
@@ -7756,6 +8520,7 @@ function allToolSpecs() {
7756
8520
  ...registerOptionAlgoTools(),
7757
8521
  ...registerAlgoTradeTools(),
7758
8522
  ...registerAccountTools(),
8523
+ ...registerEventContractTools(),
7759
8524
  ...registerBotTools(),
7760
8525
  ...registerAllEarnTools(),
7761
8526
  ...registerAuditTools(),
@@ -8848,7 +9613,7 @@ async function cmdDiagnoseMcp(options = {}) {
8848
9613
 
8849
9614
  // src/commands/diagnose.ts
8850
9615
  var CLI_VERSION = readCliVersion();
8851
- var GIT_HASH = true ? "6d4d559" : "dev";
9616
+ var GIT_HASH = true ? "765bfa2" : "dev";
8852
9617
  function maskKey2(key) {
8853
9618
  if (!key) return "(not set)";
8854
9619
  if (key.length <= 8) return "****";
@@ -9837,6 +10602,47 @@ var HELP_TREE = {
9837
10602
  }
9838
10603
  }
9839
10604
  },
10605
+ event: {
10606
+ description: "Event contract trading \u2014 binary outcome prediction markets (Yes/No, Up/Down)",
10607
+ commands: {
10608
+ browse: {
10609
+ usage: "okx event browse [--underlying <asset>]",
10610
+ description: "Browse all active (in-progress) event contracts; px is event contract price (0.01\u20130.99), not the underlying asset price \u2014 reflects market-implied probability when actively trading"
10611
+ },
10612
+ series: {
10613
+ usage: "okx event series [--seriesId <id>]",
10614
+ description: "List event contract series grouped by type (UP/DOWN direction or YES/NO price-target)"
10615
+ },
10616
+ events: {
10617
+ usage: "okx event events <seriesId> [--state <preopen|live|settling|expired>] [--limit <n>]",
10618
+ description: "List events in a series (each event = one expiry)"
10619
+ },
10620
+ markets: {
10621
+ usage: "okx event markets <seriesId> [--eventId <id>] [--state <preopen|live|settling|expired>] [--limit <n>]",
10622
+ description: "List markets; px is event contract price (0.01\u20130.99), not the underlying asset price \u2014 reflects market-implied probability when actively trading; expired includes translated outcome and settleValue"
10623
+ },
10624
+ place: {
10625
+ usage: "okx event place <instId> <side> <outcome> <sz> [--px <price>] [--ordType <market|limit|post_only>]",
10626
+ description: "[CAUTION] Place an event contract order. outcome: UP/YES or DOWN/NO. limit: sz=contracts, px required. market: sz=quote currency amount, no px."
10627
+ },
10628
+ amend: {
10629
+ usage: "okx event amend <instId> <ordId> [--px <price>] [--sz <n>]",
10630
+ description: "[CAUTION] Amend a pending limit order (change price or size)"
10631
+ },
10632
+ cancel: {
10633
+ usage: "okx event cancel <instId> <ordId>",
10634
+ description: "Cancel a pending event contract order"
10635
+ },
10636
+ orders: {
10637
+ usage: "okx event orders [--instId <id>] [--state live] [--limit <n>]",
10638
+ description: "List event orders (state=live for pending; omit for history)"
10639
+ },
10640
+ fills: {
10641
+ usage: "okx event fills [--instId <id>] [--limit <n>]",
10642
+ description: "Get event contract fill history"
10643
+ }
10644
+ }
10645
+ },
9840
10646
  config: {
9841
10647
  description: "Manage CLI configuration profiles",
9842
10648
  commands: {
@@ -10195,6 +11001,11 @@ var CLI_OPTIONS = {
10195
11001
  dir: { type: "string" },
10196
11002
  page: { type: "string" },
10197
11003
  format: { type: "string" },
11004
+ // event contract
11005
+ underlying: { type: "string" },
11006
+ seriesId: { type: "string" },
11007
+ eventId: { type: "string" },
11008
+ outcome: { type: "string" },
10198
11009
  // diagnostics — cli/mcp/all/output are diagnose-specific; verbose is shared
10199
11010
  verbose: { type: "boolean", default: false },
10200
11011
  mcp: { type: "boolean", default: false },
@@ -13213,10 +14024,462 @@ function printSkillInstallResult(meta, json) {
13213
14024
  }
13214
14025
  }
13215
14026
 
14027
+ // src/commands/event-contract.ts
14028
+ function getData8(result) {
14029
+ return result.data;
14030
+ }
14031
+ function fmtMethod(raw) {
14032
+ const map = {
14033
+ // API returns values in various cases; normalise to lowercase for lookup
14034
+ price_above: "Price Above",
14035
+ price_up_down: "Up/Down",
14036
+ price_once_touch: "One Touch",
14037
+ // Uppercase variants (in case API returns them)
14038
+ PRICE_ABOVE: "Price Above",
14039
+ PRICE_UP_DOWN: "Up/Down",
14040
+ PRICE_ONCE_TOUCH: "One Touch"
14041
+ };
14042
+ return map[String(raw)] ?? String(raw ?? "");
14043
+ }
14044
+ function fmtFreq(raw) {
14045
+ const map = {
14046
+ fifteen_min: "15min",
14047
+ daily: "Daily",
14048
+ hourly: "1h",
14049
+ weekly: "Weekly",
14050
+ FIFTEEN_MIN: "15min",
14051
+ DAILY: "Daily",
14052
+ HOURLY: "1h",
14053
+ WEEKLY: "Weekly"
14054
+ };
14055
+ return map[String(raw)] ?? String(raw ?? "");
14056
+ }
14057
+ function fmtOutcome(raw) {
14058
+ const s = String(raw ?? "").toLowerCase();
14059
+ if (s === "" || s === "pending") return "";
14060
+ return String(raw ?? "");
14061
+ }
14062
+ function fmtMarketOutcome(instId, outcome) {
14063
+ const formatted = fmtOutcome(outcome);
14064
+ if (formatted === "") return "";
14065
+ return fmtOrderOutcome(instId, formatted);
14066
+ }
14067
+ function fmtOrderOutcome(instId, outcome) {
14068
+ const id = String(instId ?? "").toUpperCase();
14069
+ const isUpDown = id.includes("UPDOWN") || id.includes("UP-DOWN");
14070
+ const normalized = String(outcome ?? "").toLowerCase();
14071
+ if (outcome === "1" || outcome === 1 || normalized === "yes") return isUpDown ? "UP" : "YES";
14072
+ if (outcome === "2" || outcome === 2 || normalized === "no") return isUpDown ? "DOWN" : "NO";
14073
+ return String(outcome ?? "");
14074
+ }
14075
+ function fmtPeriodFromInstId(instId) {
14076
+ const parts = instId.split("-");
14077
+ const upper = instId.toUpperCase();
14078
+ const dateIdx = findDateIdx(parts);
14079
+ if (dateIdx < 0) return instId;
14080
+ const dp = parts[dateIdx];
14081
+ const date = `20${dp.slice(0, 2)}-${dp.slice(2, 4)}-${dp.slice(4, 6)}`;
14082
+ const fmt = (t) => t.length === 4 ? `${t.slice(0, 2)}:${t.slice(2)}` : t;
14083
+ const t1 = parts[dateIdx + 1] ?? "";
14084
+ const t2 = parts[dateIdx + 2] ?? "";
14085
+ const isUpDown = upper.includes("UPDOWN");
14086
+ if (isUpDown && /^\d{4}$/.test(t1) && /^\d{4}$/.test(t2)) {
14087
+ return `${date} ${fmt(t1)} ~ ${fmt(t2)} UTC+8`;
14088
+ }
14089
+ if (/^\d{4}$/.test(t1)) {
14090
+ return `${date} ${fmt(t1)} UTC+8`;
14091
+ }
14092
+ return instId;
14093
+ }
14094
+ function fmtTs(raw) {
14095
+ if (!raw) return "";
14096
+ const n = Number(raw);
14097
+ if (Number.isNaN(n)) return String(raw);
14098
+ return new Date(n).toISOString().replace("T", " ").replace(".000Z", " UTC");
14099
+ }
14100
+ function fmtProbability(raw) {
14101
+ if (raw === "" || raw == null) return "";
14102
+ const n = Number(raw);
14103
+ if (Number.isNaN(n)) return String(raw);
14104
+ return `${(n * 100).toFixed(1)}%`;
14105
+ }
14106
+ async function cmdEventBrowse(run, opts) {
14107
+ const result = await run("event_browse", { underlying: opts.underlying });
14108
+ const data = getData8(result);
14109
+ if (opts.json) return printJson(data);
14110
+ if (!data || data.length === 0) {
14111
+ process.stdout.write("No active event contracts found.\n");
14112
+ return;
14113
+ }
14114
+ for (const group of data) {
14115
+ const contracts = group["contracts"] ?? [];
14116
+ const methodLabel = fmtMethod(group["method"]);
14117
+ const freqLabel = fmtFreq(group["freq"]);
14118
+ process.stdout.write(`
14119
+ [${methodLabel}] ${group["underlying"]} (${freqLabel})
14120
+ `);
14121
+ printTable(
14122
+ contracts.map((c) => ({
14123
+ "Contract": formatDisplayTitle(String(c["instId"] ?? "")),
14124
+ "Expiry": c["expTime"] ?? "",
14125
+ "Target Price": c["floorStrike"] ? String(c["floorStrike"]) : "\u2014",
14126
+ "Probability": fmtProbability(c["px"]),
14127
+ "Outcome": fmtOutcome(c["outcome"]) || "\u2014",
14128
+ "instId": c["instId"]
14129
+ }))
14130
+ );
14131
+ }
14132
+ const total = data.reduce((n, g) => n + (g["contracts"]?.length ?? 0), 0);
14133
+ process.stdout.write(`
14134
+ ${total} active contract(s) across ${data.length} series.
14135
+ `);
14136
+ }
14137
+ var FEATURED_SERIES = /* @__PURE__ */ new Set([
14138
+ "BTC-UPDOWN-15MIN",
14139
+ "ETH-UPDOWN-15MIN",
14140
+ "TRX-UPDOWN-15MIN",
14141
+ "BTC-ABOVE-DAILY",
14142
+ "ETH-ABOVE-DAILY"
14143
+ ]);
14144
+ var KNOWN_PREFIXES = /^(BTC|ETH|TRX|SOL|EOS|BNB|XRP|ADA|DOGE|IOTA|SUSHI|KISHU|BTG|XTZ)-/i;
14145
+ function getSeriesMethod(s) {
14146
+ const settlement = s["settlement"];
14147
+ const method = settlement?.["method"] ?? s["method"];
14148
+ return String(method ?? "").toLowerCase();
14149
+ }
14150
+ function isUpDownSeries(s) {
14151
+ const m = getSeriesMethod(s);
14152
+ return m.includes("up_down") || m.includes("updown");
14153
+ }
14154
+ function isFeatured(s) {
14155
+ return FEATURED_SERIES.has(String(s["seriesId"] ?? ""));
14156
+ }
14157
+ function isKnownSeries(s) {
14158
+ return KNOWN_PREFIXES.test(String(s["seriesId"] ?? ""));
14159
+ }
14160
+ function classifySeries(all) {
14161
+ const featured = [];
14162
+ const standard = [];
14163
+ const testSeries = [];
14164
+ for (const s of all) {
14165
+ if (isFeatured(s)) featured.push(s);
14166
+ else if (isKnownSeries(s)) standard.push(s);
14167
+ else testSeries.push(s);
14168
+ }
14169
+ return { featured, standard, testSeries };
14170
+ }
14171
+ async function cmdEventSeries(run, opts) {
14172
+ const result = await run("event_get_series", { seriesId: opts.seriesId });
14173
+ const data = getData8(result);
14174
+ if (opts.json) return printJson(data);
14175
+ const toRow = (s, featured2) => {
14176
+ const settlement = s["settlement"];
14177
+ const method = settlement?.["method"] ?? s["method"];
14178
+ const underlying = settlement?.["underlying"] ?? s["underlying"] ?? s["baseCcy"];
14179
+ return {
14180
+ Series: `${featured2 ? "\u2B50 " : ""}${String(s["seriesId"] ?? "")}`,
14181
+ Type: fmtMethod(method),
14182
+ Freq: fmtFreq(s["freq"]),
14183
+ Underlying: String(underlying ?? ""),
14184
+ State: String(s["state"] ?? "")
14185
+ };
14186
+ };
14187
+ const all = data ?? [];
14188
+ const { featured, standard, testSeries } = classifySeries(all);
14189
+ const mainSeries = [...featured, ...standard];
14190
+ const updown = mainSeries.filter(isUpDownSeries);
14191
+ const above = mainSeries.filter((s) => !isUpDownSeries(s));
14192
+ if (updown.length > 0) {
14193
+ process.stdout.write("\n\u2500\u2500 Up/Down \u2500\u2500\n");
14194
+ printTable(updown.map((s) => toRow(s, isFeatured(s))));
14195
+ }
14196
+ if (above.length > 0) {
14197
+ process.stdout.write("\n\u2500\u2500 Price Above \u2500\u2500\n");
14198
+ printTable(above.map((s) => toRow(s, isFeatured(s))));
14199
+ }
14200
+ if (testSeries.length > 0) {
14201
+ if (opts.all) {
14202
+ process.stdout.write("\n\u2500\u2500 Test / Other \u2500\u2500\n");
14203
+ printTable(testSeries.map((s) => toRow(s, false)));
14204
+ } else {
14205
+ process.stdout.write(`
14206
+ ${testSeries.length} test series hidden (use --all to show)
14207
+ `);
14208
+ }
14209
+ }
14210
+ }
14211
+ async function cmdEventEvents(run, opts) {
14212
+ const result = await run("event_get_events", {
14213
+ seriesId: opts.seriesId,
14214
+ state: opts.state,
14215
+ limit: opts.limit
14216
+ });
14217
+ const data = getData8(result);
14218
+ if (opts.json) return printJson(data);
14219
+ printTable(
14220
+ (data ?? []).map((e) => ({
14221
+ eventId: e["eventId"],
14222
+ state: e["state"],
14223
+ expTime: fmtTs(e["expTime"]),
14224
+ settleTime: fmtTs(e["settleTime"])
14225
+ }))
14226
+ );
14227
+ }
14228
+ function sortByExpiry(data) {
14229
+ return [...data].sort((a, b) => {
14230
+ const ea = inferExpiryMsFromInstId(String(a["instId"] ?? "")) ?? Infinity;
14231
+ const eb = inferExpiryMsFromInstId(String(b["instId"] ?? "")) ?? Infinity;
14232
+ return ea - eb;
14233
+ });
14234
+ }
14235
+ async function cmdEventMarkets(run, opts) {
14236
+ const result = await run("event_get_markets", {
14237
+ seriesId: opts.seriesId,
14238
+ eventId: opts.eventId,
14239
+ instId: opts.instId,
14240
+ state: opts.state,
14241
+ limit: opts.limit
14242
+ });
14243
+ const data = getData8(result);
14244
+ if (opts.json) return printJson(data);
14245
+ const ext = result;
14246
+ const currentIdxPx = ext["currentIdxPx"];
14247
+ const underlying = ext["underlying"];
14248
+ if (currentIdxPx != null) {
14249
+ process.stdout.write(
14250
+ `${underlying ?? ""} current index price: $${currentIdxPx}
14251
+ `
14252
+ );
14253
+ }
14254
+ const sorted = sortByExpiry(data ?? []);
14255
+ printTable(
14256
+ sorted.map((m) => {
14257
+ const id = String(m["instId"] ?? "");
14258
+ const outcome = fmtMarketOutcome(m["instId"], m["outcome"]);
14259
+ return {
14260
+ contract: formatDisplayTitle(id),
14261
+ expTime: m["expTime"] ?? "",
14262
+ targetPrice: m["floorStrike"] ?? "",
14263
+ probability: fmtProbability(m["px"]),
14264
+ outcome: outcome.toLowerCase() === "pending" ? "\u2014" : outcome,
14265
+ settleValue: m["settleValue"] ?? "",
14266
+ instId: id
14267
+ };
14268
+ })
14269
+ );
14270
+ }
14271
+ async function cmdEventOrders(run, opts) {
14272
+ const result = await run("event_get_orders", {
14273
+ instId: opts.instId,
14274
+ state: opts.state,
14275
+ limit: opts.limit
14276
+ });
14277
+ const data = getData8(result);
14278
+ if (opts.json) return printJson(data);
14279
+ printTable(
14280
+ (data ?? []).map((o) => ({
14281
+ "Contract": formatDisplayTitle(String(o["instId"] ?? "")),
14282
+ "Time": fmtTs(o["cTime"]),
14283
+ "Direction": `${String(o["side"] ?? "").toUpperCase()} ${fmtOrderOutcome(o["instId"], o["outcome"]).toUpperCase()}`,
14284
+ "Price": o["px"],
14285
+ "Size": `${o["fillSz"] ?? 0} / ${o["sz"]}`,
14286
+ "Status": o["stateLabel"] ?? o["state"],
14287
+ "Order number": o["ordId"]
14288
+ }))
14289
+ );
14290
+ }
14291
+ async function cmdEventFills(run, opts) {
14292
+ const result = await run("event_get_fills", {
14293
+ instId: opts.instId,
14294
+ limit: opts.limit
14295
+ });
14296
+ const data = getData8(result);
14297
+ if (opts.json) return printJson(data);
14298
+ printTable(
14299
+ (data ?? []).map((f) => ({
14300
+ "Contract": formatDisplayTitle(String(f["instId"] ?? "")),
14301
+ "Direction": (() => {
14302
+ const side = String(f["side"] ?? "").toUpperCase();
14303
+ const outcome = fmtOrderOutcome(f["instId"], f["outcome"]).toUpperCase();
14304
+ const dir = `${side} ${outcome}`.trim();
14305
+ return dir || "\u2014";
14306
+ })(),
14307
+ "Fill Price": f["fillPx"],
14308
+ "Fill Size": f["fillSz"],
14309
+ "Time": fmtTs(f["ts"]),
14310
+ "Order number": f["ordId"]
14311
+ }))
14312
+ );
14313
+ }
14314
+ async function handleExpiredContractFallback(run, opts) {
14315
+ process.stdout.write(
14316
+ `Order failed: Contract ${opts.instId} has expired.
14317
+ Checking next available contracts in this series...
14318
+ `
14319
+ );
14320
+ const seriesId = extractSeriesId(opts.instId);
14321
+ try {
14322
+ const mkts = await run("event_get_markets", { seriesId, state: "live" });
14323
+ const mData = getData8(mkts) ?? [];
14324
+ const active = mData.filter((m) => m["floorStrike"] && m["floorStrike"] !== "");
14325
+ if (active.length > 0) {
14326
+ printTable(
14327
+ active.slice(0, 3).map((m) => ({
14328
+ instId: m["instId"],
14329
+ expTime: m["expTime"] ?? "",
14330
+ targetPrice: m["floorStrike"] ?? ""
14331
+ }))
14332
+ );
14333
+ const next = active[0];
14334
+ const nextInstId = String(next["instId"]);
14335
+ const pxFlag = opts.px ? ` --px ${opts.px}` : "";
14336
+ const ordTypeFlag = opts.ordType ? ` --ordType ${opts.ordType}` : "";
14337
+ process.stdout.write(
14338
+ `
14339
+ To place the same order on the next contract:
14340
+ okx event place ${nextInstId} ${opts.side} ${opts.outcome} ${opts.sz}${pxFlag}${ordTypeFlag}
14341
+ `
14342
+ );
14343
+ } else {
14344
+ process.stdout.write(`No active contracts found in this series.
14345
+ `);
14346
+ }
14347
+ } catch {
14348
+ }
14349
+ }
14350
+ function handlePlaceOrderError2(msg, instId) {
14351
+ if (msg.includes("not found") || msg.includes("51001")) {
14352
+ process.stdout.write(`Order failed: Contract ${instId} not found or not yet available.
14353
+ `);
14354
+ } else {
14355
+ process.stdout.write(`Order failed: ${msg}
14356
+ `);
14357
+ }
14358
+ }
14359
+ function buildPlaceConfirmation(opts, ordType) {
14360
+ const contractName = formatDisplayTitle(opts.instId);
14361
+ const direction = `${opts.side.toUpperCase()} ${opts.outcome.toUpperCase()}`;
14362
+ if (ordType === "market") {
14363
+ return `Placing: ${contractName} ${direction} sz=${opts.sz} (market order, exchange converts to contracts)
14364
+ `;
14365
+ }
14366
+ const px = parseFloat(opts.px ?? "0");
14367
+ const sz = parseFloat(opts.sz);
14368
+ const cost = (sz * px).toFixed(2);
14369
+ const maxGain = (sz * (1 - px)).toFixed(2);
14370
+ return `Placing: ${contractName} ${direction} ${opts.sz} contracts at px=${opts.px} (cost \u2248 ${cost}, max gain \u2248 ${maxGain})
14371
+ `;
14372
+ }
14373
+ async function cmdEventPlace(run, opts) {
14374
+ const ordType = opts.ordType ?? "market";
14375
+ if (ordType === "limit" && !opts.px) {
14376
+ process.stderr.write("Error: --px is required for limit orders.\n");
14377
+ process.exitCode = 1;
14378
+ return;
14379
+ }
14380
+ if (!opts.json) {
14381
+ process.stdout.write(buildPlaceConfirmation(opts, ordType));
14382
+ }
14383
+ let result;
14384
+ try {
14385
+ result = await run("event_place_order", {
14386
+ instId: opts.instId,
14387
+ side: opts.side,
14388
+ outcome: opts.outcome,
14389
+ sz: opts.sz,
14390
+ px: opts.px,
14391
+ ordType: opts.ordType
14392
+ });
14393
+ } catch (err) {
14394
+ const msg = err instanceof Error ? err.message : String(err);
14395
+ const expiryMs = inferExpiryMsFromInstId(opts.instId);
14396
+ const isExpired = expiryMs !== null && expiryMs < Date.now();
14397
+ if (isExpired) {
14398
+ await handleExpiredContractFallback(run, opts);
14399
+ } else {
14400
+ handlePlaceOrderError2(msg, opts.instId);
14401
+ }
14402
+ return;
14403
+ }
14404
+ const data = getData8(result);
14405
+ if (opts.json) return printJson(data);
14406
+ const order = data?.[0];
14407
+ const stateHint = ordType === "market" ? "market order \u2014 typically fills immediately" : `${ordType} order \u2014 may still be live; verify with: okx event orders --instId ${opts.instId} --state live`;
14408
+ const period = fmtPeriodFromInstId(opts.instId);
14409
+ const pxPart = opts.px ? ` px: ${opts.px}` : "";
14410
+ process.stdout.write(
14411
+ `Order submitted: ${order?.["ordId"]}
14412
+ Period: ${period}
14413
+ ${opts.side.toUpperCase()} ${opts.outcome.toUpperCase()} sz: ${opts.sz}${pxPart} type: ${ordType}
14414
+ (${stateHint})
14415
+ `
14416
+ );
14417
+ if (ordType === "market") {
14418
+ process.stdout.write(" Note: exchange converts sz (amount) to contracts based on best available price\n");
14419
+ }
14420
+ }
14421
+ async function cmdEventAmend(run, opts) {
14422
+ let result;
14423
+ try {
14424
+ result = await run("event_amend_order", {
14425
+ instId: opts.instId,
14426
+ ordId: opts.ordId,
14427
+ newPx: opts.px,
14428
+ newSz: opts.sz
14429
+ });
14430
+ } catch (err) {
14431
+ const msg = err instanceof Error ? err.message : String(err);
14432
+ process.stdout.write(`Failed to amend order ${opts.ordId}: ${msg}
14433
+ `);
14434
+ return;
14435
+ }
14436
+ const data = getData8(result);
14437
+ if (opts.json) return printJson(data);
14438
+ const r = data?.[0];
14439
+ const pxPart = opts.px ? ` new px: ${opts.px}` : "";
14440
+ const szPart = opts.sz ? ` new sz: ${opts.sz}` : "";
14441
+ process.stdout.write(
14442
+ `Amended: ${r?.["ordId"] ?? opts.ordId}${pxPart}${szPart}
14443
+ `
14444
+ );
14445
+ }
14446
+ function handleCancelCatchError(instId, ordId, err) {
14447
+ const msg = err instanceof Error ? err.message : String(err);
14448
+ const expiryMs = inferExpiryMsFromInstId(instId);
14449
+ const isExpired = expiryMs !== null && expiryMs < Date.now();
14450
+ if (isExpired) {
14451
+ process.stdout.write(
14452
+ `Cannot cancel: contract ${instId} has already expired.
14453
+ The order was auto-cancelled at settlement \u2014 no action needed.
14454
+ `
14455
+ );
14456
+ } else {
14457
+ process.stdout.write(`Failed to cancel order ${ordId}: ${msg}
14458
+ `);
14459
+ }
14460
+ }
14461
+ async function cmdEventCancel(run, opts) {
14462
+ let result;
14463
+ try {
14464
+ result = await run("event_cancel_order", {
14465
+ instId: opts.instId,
14466
+ ordId: opts.ordId
14467
+ });
14468
+ } catch (err) {
14469
+ handleCancelCatchError(opts.instId, opts.ordId, err);
14470
+ return;
14471
+ }
14472
+ const data = getData8(result);
14473
+ if (opts.json) return printJson(data);
14474
+ const r = data?.[0];
14475
+ process.stdout.write(`Cancelled: ${r?.["ordId"] ?? opts.ordId}
14476
+ `);
14477
+ }
14478
+
13216
14479
  // src/index.ts
13217
14480
  var _require3 = createRequire3(import.meta.url);
13218
14481
  var CLI_VERSION2 = _require3("../package.json").version;
13219
- var GIT_HASH2 = true ? "6d4d559" : "dev";
14482
+ var GIT_HASH2 = true ? "765bfa2" : "dev";
13220
14483
  function handleConfigCommand(action, rest, json, lang, force) {
13221
14484
  if (action === "init") return cmdConfigInit(lang === "zh" ? "zh" : "en");
13222
14485
  if (action === "show") return cmdConfigShow(json);
@@ -14026,6 +15289,33 @@ function handleSkillCommand(run, action, rest, v, json, config) {
14026
15289
  errorLine("Valid: search, categories, add, download, remove, check, list");
14027
15290
  process.exitCode = 1;
14028
15291
  }
15292
+ function handleEventCommand(run, action, rest, v, json) {
15293
+ const limit = v.limit !== void 0 ? Number(v.limit) : void 0;
15294
+ const handlers = {
15295
+ browse: () => cmdEventBrowse(run, { underlying: v.underlying ?? rest[0], json }),
15296
+ series: () => cmdEventSeries(run, { seriesId: v.seriesId, all: v.all, json }),
15297
+ events: () => cmdEventEvents(run, { seriesId: v.seriesId ?? rest[0], state: v.state, limit, json }),
15298
+ markets: () => cmdEventMarkets(run, { seriesId: v.seriesId ?? rest[0], eventId: v.eventId, instId: v.instId, state: v.state, limit, json }),
15299
+ place: () => cmdEventPlace(run, {
15300
+ instId: v.instId ?? rest[0],
15301
+ side: v.side ?? rest[1],
15302
+ outcome: v.outcome ?? rest[2],
15303
+ sz: v.sz ?? rest[3],
15304
+ px: v.px,
15305
+ ordType: v.ordType,
15306
+ json
15307
+ }),
15308
+ amend: () => cmdEventAmend(run, { instId: v.instId ?? rest[0], ordId: v.ordId ?? rest[1], px: v.px, sz: v.sz, json }),
15309
+ cancel: () => cmdEventCancel(run, { instId: v.instId ?? rest[0], ordId: v.ordId ?? rest[1], json }),
15310
+ orders: () => cmdEventOrders(run, { instId: v.instId, state: v.state, limit, json }),
15311
+ fills: () => cmdEventFills(run, { instId: v.instId, limit, json })
15312
+ };
15313
+ const handler = handlers[action];
15314
+ if (handler) return handler();
15315
+ process.stderr.write(`Unknown event command: ${action}
15316
+ `);
15317
+ process.exitCode = 1;
15318
+ }
14029
15319
  function outputResult(result, json) {
14030
15320
  if (json) {
14031
15321
  outputLine(JSON.stringify(result, null, 2));
@@ -14104,6 +15394,7 @@ async function main() {
14104
15394
  swap: () => handleSwapCommand(run, action, rest, v, json),
14105
15395
  futures: () => handleFuturesCommand(run, action, rest, v, json),
14106
15396
  option: () => handleOptionCommand(run, action, rest, v, json),
15397
+ event: () => handleEventCommand(run, action, rest, v, json),
14107
15398
  bot: () => handleBotCommand(run, action, rest, v, json),
14108
15399
  earn: () => handleEarnCommand(run, action, rest, v, json),
14109
15400
  skill: () => handleSkillCommand(run, action, rest, v, json, config)
@@ -14128,6 +15419,7 @@ export {
14128
15419
  handleBotGridCommand,
14129
15420
  handleConfigCommand,
14130
15421
  handleEarnCommand,
15422
+ handleEventCommand,
14131
15423
  handleFuturesAlgoCommand,
14132
15424
  handleFuturesCommand,
14133
15425
  handleMarketCommand,