@gbozee/ultimate 0.0.2-next.72 → 0.0.2-next.73
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.cjs +453 -7
- package/dist/index.d.ts +31 -0
- package/dist/index.js +453 -7
- package/dist/mcp-server.cjs +453 -7
- package/dist/mcp-server.js +453 -7
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -72035,6 +72035,12 @@ class BaseExchange {
|
|
|
72035
72035
|
});
|
|
72036
72036
|
}
|
|
72037
72037
|
}
|
|
72038
|
+
async previewSpotMarginHedge(_payload) {
|
|
72039
|
+
throw new Error("Spot margin hedge preview is not supported on this exchange");
|
|
72040
|
+
}
|
|
72041
|
+
async placeSpotMarginHedge(_payload) {
|
|
72042
|
+
throw new Error("Spot margin hedge placement is not supported on this exchange");
|
|
72043
|
+
}
|
|
72038
72044
|
async placeSpotLimitOrders(_payload) {}
|
|
72039
72045
|
async buildCumulative(payload) {
|
|
72040
72046
|
const {
|
|
@@ -72442,6 +72448,210 @@ class BaseExchange {
|
|
|
72442
72448
|
|
|
72443
72449
|
// src/exchanges/binance/index.ts
|
|
72444
72450
|
var import_p_limit = __toESM(require_p_limit(), 1);
|
|
72451
|
+
|
|
72452
|
+
// src/exchanges/binance/spot-margin-hedge.ts
|
|
72453
|
+
function roundNumber(value2, digits = 8) {
|
|
72454
|
+
return Number(value2.toFixed(digits));
|
|
72455
|
+
}
|
|
72456
|
+
var STOP_LIMIT_SPREAD_PERCENT = 0.0005;
|
|
72457
|
+
function normalizeOrderInput(order) {
|
|
72458
|
+
return {
|
|
72459
|
+
price: roundNumber(order.price),
|
|
72460
|
+
quantity: roundNumber(order.quantity)
|
|
72461
|
+
};
|
|
72462
|
+
}
|
|
72463
|
+
function sortOrderInputs(orders) {
|
|
72464
|
+
return [...orders].map(normalizeOrderInput).sort((left, right) => left.price - right.price);
|
|
72465
|
+
}
|
|
72466
|
+
function normalizeOrderForComparison(order) {
|
|
72467
|
+
const type = (order.type || "limit").toLowerCase();
|
|
72468
|
+
const raw_stop = "stop" in order && order.stop !== undefined ? order.stop : ("stopPrice" in order) ? order.stopPrice : undefined;
|
|
72469
|
+
const parsed_stop = raw_stop === undefined || raw_stop === null || raw_stop === "" ? undefined : Number(raw_stop);
|
|
72470
|
+
return {
|
|
72471
|
+
side: order.side,
|
|
72472
|
+
type,
|
|
72473
|
+
price: roundNumber(order.price),
|
|
72474
|
+
quantity: roundNumber(order.quantity),
|
|
72475
|
+
stop: parsed_stop !== undefined && Number.isFinite(parsed_stop) && parsed_stop > 0 ? roundNumber(parsed_stop) : undefined
|
|
72476
|
+
};
|
|
72477
|
+
}
|
|
72478
|
+
function normalizeOrders(orders) {
|
|
72479
|
+
return [...orders].map(normalizeOrderForComparison).sort((left, right) => {
|
|
72480
|
+
if (left.side !== right.side)
|
|
72481
|
+
return left.side.localeCompare(right.side);
|
|
72482
|
+
if (left.type !== right.type)
|
|
72483
|
+
return left.type.localeCompare(right.type);
|
|
72484
|
+
if (left.price !== right.price)
|
|
72485
|
+
return left.price - right.price;
|
|
72486
|
+
return left.quantity - right.quantity;
|
|
72487
|
+
});
|
|
72488
|
+
}
|
|
72489
|
+
function toPlannedOrders(orders) {
|
|
72490
|
+
return normalizeOrders(orders).map((order) => ({
|
|
72491
|
+
side: order.side,
|
|
72492
|
+
type: order.type,
|
|
72493
|
+
price: order.price,
|
|
72494
|
+
quantity: order.quantity,
|
|
72495
|
+
stop: order.stop
|
|
72496
|
+
}));
|
|
72497
|
+
}
|
|
72498
|
+
function buildOrderKey(order) {
|
|
72499
|
+
return [
|
|
72500
|
+
order.side,
|
|
72501
|
+
order.type,
|
|
72502
|
+
order.price,
|
|
72503
|
+
order.quantity,
|
|
72504
|
+
order.stop ?? ""
|
|
72505
|
+
].join(":");
|
|
72506
|
+
}
|
|
72507
|
+
function buildDiff(options) {
|
|
72508
|
+
const expectedKeys = options.expected.map(buildOrderKey);
|
|
72509
|
+
const actualKeys = options.actual.map(buildOrderKey);
|
|
72510
|
+
return {
|
|
72511
|
+
missing: expectedKeys.filter((key) => !actualKeys.includes(key)),
|
|
72512
|
+
extra: actualKeys.filter((key) => !expectedKeys.includes(key))
|
|
72513
|
+
};
|
|
72514
|
+
}
|
|
72515
|
+
function getQuoteAsset(symbol) {
|
|
72516
|
+
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
72517
|
+
const target = quoteAssets.find((asset) => symbol.endsWith(asset));
|
|
72518
|
+
return target || symbol.slice(-4);
|
|
72519
|
+
}
|
|
72520
|
+
function sumNotional(orders) {
|
|
72521
|
+
return roundNumber(orders.reduce((sum, order) => sum + order.price * order.quantity, 0), 8);
|
|
72522
|
+
}
|
|
72523
|
+
function sumQuantity(orders) {
|
|
72524
|
+
return roundNumber(orders.reduce((sum, order) => sum + order.quantity, 0), 8);
|
|
72525
|
+
}
|
|
72526
|
+
function buildLongOrderPlacement(options) {
|
|
72527
|
+
const { order, current_price } = options;
|
|
72528
|
+
if (order.price > current_price) {
|
|
72529
|
+
return {
|
|
72530
|
+
side: "buy",
|
|
72531
|
+
type: "stop_loss_limit",
|
|
72532
|
+
price: order.price,
|
|
72533
|
+
stop: roundNumber(order.price * (1 - STOP_LIMIT_SPREAD_PERCENT)),
|
|
72534
|
+
quantity: order.quantity
|
|
72535
|
+
};
|
|
72536
|
+
}
|
|
72537
|
+
return {
|
|
72538
|
+
side: "buy",
|
|
72539
|
+
type: "limit",
|
|
72540
|
+
price: order.price,
|
|
72541
|
+
quantity: order.quantity
|
|
72542
|
+
};
|
|
72543
|
+
}
|
|
72544
|
+
function inferSpotMarginHedgeInputFromLiveOrders(options) {
|
|
72545
|
+
const long_orders = sortOrderInputs([
|
|
72546
|
+
...options.spot_orders.filter((order) => order.side === "buy").map((order) => ({
|
|
72547
|
+
price: order.price,
|
|
72548
|
+
quantity: order.quantity
|
|
72549
|
+
})),
|
|
72550
|
+
...options.margin_orders.filter((order) => order.side === "buy").map((order) => ({
|
|
72551
|
+
price: order.price,
|
|
72552
|
+
quantity: order.quantity
|
|
72553
|
+
}))
|
|
72554
|
+
]);
|
|
72555
|
+
const short_orders = sortOrderInputs([
|
|
72556
|
+
...options.spot_orders.filter((order) => order.side === "sell").map((order) => ({
|
|
72557
|
+
price: order.price,
|
|
72558
|
+
quantity: order.quantity
|
|
72559
|
+
})),
|
|
72560
|
+
...options.margin_orders.filter((order) => order.side === "sell").map((order) => ({
|
|
72561
|
+
price: order.price,
|
|
72562
|
+
quantity: order.quantity
|
|
72563
|
+
}))
|
|
72564
|
+
]);
|
|
72565
|
+
return {
|
|
72566
|
+
symbol: options.symbol,
|
|
72567
|
+
margin_type: options.margin_type,
|
|
72568
|
+
borrow_amount: sumNotional(long_orders),
|
|
72569
|
+
long_orders,
|
|
72570
|
+
short_orders,
|
|
72571
|
+
placement_overrides: {
|
|
72572
|
+
spot: toPlannedOrders(options.spot_orders),
|
|
72573
|
+
margin: toPlannedOrders(options.margin_orders)
|
|
72574
|
+
}
|
|
72575
|
+
};
|
|
72576
|
+
}
|
|
72577
|
+
function buildSpotMarginHedgePlan(options) {
|
|
72578
|
+
const max_spot_buy_orders = options.max_spot_buy_orders ?? 5;
|
|
72579
|
+
const long_orders = sortOrderInputs(options.long_orders);
|
|
72580
|
+
const short_orders = sortOrderInputs(options.short_orders);
|
|
72581
|
+
const spot_long_orders = long_orders.slice(0, max_spot_buy_orders);
|
|
72582
|
+
const margin_long_orders = long_orders.slice(max_spot_buy_orders);
|
|
72583
|
+
const default_spot_orders_to_create = [
|
|
72584
|
+
...spot_long_orders.map((order) => buildLongOrderPlacement({
|
|
72585
|
+
order,
|
|
72586
|
+
current_price: options.current_price
|
|
72587
|
+
})),
|
|
72588
|
+
...short_orders.map((order) => ({
|
|
72589
|
+
side: "sell",
|
|
72590
|
+
type: "limit",
|
|
72591
|
+
price: order.price,
|
|
72592
|
+
quantity: order.quantity
|
|
72593
|
+
}))
|
|
72594
|
+
];
|
|
72595
|
+
const default_margin_orders_to_create = margin_long_orders.map((order) => buildLongOrderPlacement({
|
|
72596
|
+
order,
|
|
72597
|
+
current_price: options.current_price
|
|
72598
|
+
}));
|
|
72599
|
+
const spot_orders_to_create = options.placement_overrides?.spot || default_spot_orders_to_create;
|
|
72600
|
+
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
72601
|
+
const borrow_delta = roundNumber(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
72602
|
+
const spot_long_notional = sumNotional(spot_long_orders);
|
|
72603
|
+
const transfer_to_spot_amount = roundNumber(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
72604
|
+
const short_quantity = sumQuantity(short_orders);
|
|
72605
|
+
const transfer_base_to_spot_amount = roundNumber(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
72606
|
+
return {
|
|
72607
|
+
borrow_amount: options.borrow_amount,
|
|
72608
|
+
borrow_delta,
|
|
72609
|
+
transfer_to_spot_amount,
|
|
72610
|
+
transfer_to_spot_asset: getQuoteAsset(options.symbol),
|
|
72611
|
+
transfer_base_to_spot_amount,
|
|
72612
|
+
orders_to_cancel: {
|
|
72613
|
+
spot: options.snapshot.spot_orders,
|
|
72614
|
+
margin: options.snapshot.margin_orders
|
|
72615
|
+
},
|
|
72616
|
+
orders_to_create: {
|
|
72617
|
+
spot: spot_orders_to_create,
|
|
72618
|
+
margin: margin_orders_to_create
|
|
72619
|
+
},
|
|
72620
|
+
normalized: {
|
|
72621
|
+
spot: normalizeOrders(spot_orders_to_create),
|
|
72622
|
+
margin: normalizeOrders(margin_orders_to_create)
|
|
72623
|
+
}
|
|
72624
|
+
};
|
|
72625
|
+
}
|
|
72626
|
+
function compareSpotMarginHedgeOrders(options) {
|
|
72627
|
+
const actualSpot = normalizeOrders(options.spot_orders);
|
|
72628
|
+
const actualMargin = normalizeOrders(options.margin_orders);
|
|
72629
|
+
const spotDiff = buildDiff({
|
|
72630
|
+
expected: options.plan.normalized.spot,
|
|
72631
|
+
actual: actualSpot
|
|
72632
|
+
});
|
|
72633
|
+
const marginDiff = buildDiff({
|
|
72634
|
+
expected: options.plan.normalized.margin,
|
|
72635
|
+
actual: actualMargin
|
|
72636
|
+
});
|
|
72637
|
+
return {
|
|
72638
|
+
matches: spotDiff.missing.length === 0 && spotDiff.extra.length === 0 && marginDiff.missing.length === 0 && marginDiff.extra.length === 0,
|
|
72639
|
+
spot: {
|
|
72640
|
+
expected: options.plan.normalized.spot,
|
|
72641
|
+
actual: actualSpot,
|
|
72642
|
+
missing: spotDiff.missing,
|
|
72643
|
+
extra: spotDiff.extra
|
|
72644
|
+
},
|
|
72645
|
+
margin: {
|
|
72646
|
+
expected: options.plan.normalized.margin,
|
|
72647
|
+
actual: actualMargin,
|
|
72648
|
+
missing: marginDiff.missing,
|
|
72649
|
+
extra: marginDiff.extra
|
|
72650
|
+
}
|
|
72651
|
+
};
|
|
72652
|
+
}
|
|
72653
|
+
|
|
72654
|
+
// src/exchanges/binance/index.ts
|
|
72445
72655
|
var CONSTANTS = {
|
|
72446
72656
|
SPOT_TO_FIAT: "MAIN_C2C",
|
|
72447
72657
|
SPOT_TO_USDT_FUTURE: "MAIN_UMFUTURE",
|
|
@@ -72930,12 +73140,20 @@ async function getCrossMarginAccountDetails(client) {
|
|
|
72930
73140
|
async function cancelMarginOrders(client, symbol, isIsolated = false, orders) {
|
|
72931
73141
|
try {
|
|
72932
73142
|
if (orders && orders.length > 0) {
|
|
72933
|
-
const cancelPromises = orders.map((order) =>
|
|
72934
|
-
|
|
72935
|
-
|
|
72936
|
-
|
|
72937
|
-
|
|
72938
|
-
|
|
73143
|
+
const cancelPromises = orders.map((order) => {
|
|
73144
|
+
const payload = {
|
|
73145
|
+
symbol,
|
|
73146
|
+
isIsolated: isIsolated ? "TRUE" : "FALSE"
|
|
73147
|
+
};
|
|
73148
|
+
if (order.orderId !== undefined) {
|
|
73149
|
+
payload.orderId = order.orderId;
|
|
73150
|
+
}
|
|
73151
|
+
const origClientOrderId = order.origClientOrderId ?? order.clientOrderId;
|
|
73152
|
+
if (origClientOrderId !== undefined) {
|
|
73153
|
+
payload.origClientOrderId = origClientOrderId;
|
|
73154
|
+
}
|
|
73155
|
+
return client.marginAccountCancelOrder(payload);
|
|
73156
|
+
});
|
|
72939
73157
|
return await Promise.all(cancelPromises);
|
|
72940
73158
|
} else {
|
|
72941
73159
|
return await client.marginAccountCancelOpenOrders({
|
|
@@ -73190,7 +73408,7 @@ async function placeStopOrder(client, payload) {
|
|
|
73190
73408
|
});
|
|
73191
73409
|
}
|
|
73192
73410
|
}
|
|
73193
|
-
const spread = 1.00005;
|
|
73411
|
+
const spread = payload.stop > 30000 ? 1.00005 : 1.0002;
|
|
73194
73412
|
const order = {
|
|
73195
73413
|
kind: payload.kind,
|
|
73196
73414
|
side: payload.kind === "long" ? "sell" : "buy",
|
|
@@ -73597,6 +73815,16 @@ async function getAllOpenOrders(payload) {
|
|
|
73597
73815
|
const response = await client.getAllOpenOrders();
|
|
73598
73816
|
return response;
|
|
73599
73817
|
}
|
|
73818
|
+
function getPrintfFormatFromStepSize(stepSize, fallback) {
|
|
73819
|
+
if (stepSize === undefined || stepSize === null) {
|
|
73820
|
+
return fallback;
|
|
73821
|
+
}
|
|
73822
|
+
const normalized = String(stepSize);
|
|
73823
|
+
const [, decimalPart = ""] = normalized.split(".");
|
|
73824
|
+
const trimmed = decimalPart.replace(/0+$/, "");
|
|
73825
|
+
const digits = trimmed.length;
|
|
73826
|
+
return `%.${digits}f`;
|
|
73827
|
+
}
|
|
73600
73828
|
|
|
73601
73829
|
class BinanceExchange extends BaseExchange {
|
|
73602
73830
|
main_client;
|
|
@@ -74033,6 +74261,30 @@ class BinanceExchange extends BaseExchange {
|
|
|
74033
74261
|
throw error;
|
|
74034
74262
|
}
|
|
74035
74263
|
}
|
|
74264
|
+
async getSpotSymbolFormats(symbol) {
|
|
74265
|
+
if (!this.main_client || typeof this.main_client.getExchangeInfo !== "function") {
|
|
74266
|
+
return {
|
|
74267
|
+
price_places: "%.8f",
|
|
74268
|
+
decimal_places: "%.8f"
|
|
74269
|
+
};
|
|
74270
|
+
}
|
|
74271
|
+
try {
|
|
74272
|
+
const exchange_info = await this.main_client.getExchangeInfo();
|
|
74273
|
+
const symbol_info = exchange_info?.symbols?.find((item) => item.symbol === symbol.toUpperCase());
|
|
74274
|
+
const price_filter = symbol_info?.filters?.find((filter) => filter.filterType === "PRICE_FILTER");
|
|
74275
|
+
const lot_size_filter = symbol_info?.filters?.find((filter) => filter.filterType === "LOT_SIZE");
|
|
74276
|
+
return {
|
|
74277
|
+
price_places: getPrintfFormatFromStepSize(price_filter?.tickSize, "%.8f"),
|
|
74278
|
+
decimal_places: getPrintfFormatFromStepSize(lot_size_filter?.stepSize, "%.8f")
|
|
74279
|
+
};
|
|
74280
|
+
} catch (error) {
|
|
74281
|
+
console.error("Error fetching spot symbol formats:", error);
|
|
74282
|
+
return {
|
|
74283
|
+
price_places: "%.8f",
|
|
74284
|
+
decimal_places: "%.8f"
|
|
74285
|
+
};
|
|
74286
|
+
}
|
|
74287
|
+
}
|
|
74036
74288
|
async createMarginLimitOrders(payload) {
|
|
74037
74289
|
if (!this.main_client) {
|
|
74038
74290
|
throw new Error("Main client not available for margin trading");
|
|
@@ -74063,6 +74315,200 @@ class BinanceExchange extends BaseExchange {
|
|
|
74063
74315
|
}
|
|
74064
74316
|
return await getMarginOpenOrders(this.main_client, symbol, isIsolated);
|
|
74065
74317
|
}
|
|
74318
|
+
async getSpotMarginHedgeSnapshot(payload) {
|
|
74319
|
+
if (!this.main_client) {
|
|
74320
|
+
throw new Error("Main client not available for spot and margin trading");
|
|
74321
|
+
}
|
|
74322
|
+
if (payload.margin_type !== "isolated") {
|
|
74323
|
+
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
74324
|
+
}
|
|
74325
|
+
const symbol = payload.symbol.toUpperCase();
|
|
74326
|
+
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74327
|
+
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74328
|
+
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
74329
|
+
const [spot_orders, margin_orders, spot_balances, isolated_margin_position] = await Promise.all([
|
|
74330
|
+
this.getSpotOpenOrders(symbol),
|
|
74331
|
+
this.getMarginOpenOrders(symbol, true),
|
|
74332
|
+
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
74333
|
+
this.getIsolatedMarginPosition(symbol)
|
|
74334
|
+
]);
|
|
74335
|
+
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
74336
|
+
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
74337
|
+
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
74338
|
+
const isolated_asset = isolated_margin_position?.assets?.[0];
|
|
74339
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || "0");
|
|
74340
|
+
return {
|
|
74341
|
+
symbol,
|
|
74342
|
+
margin_type: payload.margin_type,
|
|
74343
|
+
quote_asset: quoteAsset,
|
|
74344
|
+
base_asset: baseAsset,
|
|
74345
|
+
borrowed_quote_amount,
|
|
74346
|
+
spot_quote_free: parseFloat(spot_quote_balance?.free?.toString?.() || "0"),
|
|
74347
|
+
spot_base_free: parseFloat(spot_base_balance?.free?.toString?.() || "0"),
|
|
74348
|
+
spot_orders,
|
|
74349
|
+
margin_orders,
|
|
74350
|
+
current_price,
|
|
74351
|
+
spot_balances,
|
|
74352
|
+
isolated_margin_position
|
|
74353
|
+
};
|
|
74354
|
+
}
|
|
74355
|
+
async previewSpotMarginHedge(payload) {
|
|
74356
|
+
const snapshot = payload.snapshot ? {
|
|
74357
|
+
...payload.snapshot,
|
|
74358
|
+
current_price: payload.snapshot.current_price ?? await this.getSpotCurrentPrice(payload.symbol)
|
|
74359
|
+
} : await this.getSpotMarginHedgeSnapshot({
|
|
74360
|
+
symbol: payload.symbol,
|
|
74361
|
+
margin_type: payload.margin_type
|
|
74362
|
+
});
|
|
74363
|
+
const shouldInferLongOrders = !payload.long_orders || payload.long_orders.length === 0;
|
|
74364
|
+
const shouldInferShortOrders = !payload.short_orders || payload.short_orders.length === 0;
|
|
74365
|
+
const inferred_input = shouldInferLongOrders || shouldInferShortOrders ? inferSpotMarginHedgeInputFromLiveOrders({
|
|
74366
|
+
symbol: payload.symbol,
|
|
74367
|
+
margin_type: payload.margin_type,
|
|
74368
|
+
spot_orders: snapshot.spot_orders,
|
|
74369
|
+
margin_orders: snapshot.margin_orders
|
|
74370
|
+
}) : undefined;
|
|
74371
|
+
const long_orders = shouldInferLongOrders ? inferred_input?.long_orders || [] : payload.long_orders || [];
|
|
74372
|
+
const short_orders = shouldInferShortOrders ? inferred_input?.short_orders || [] : payload.short_orders || [];
|
|
74373
|
+
const borrow_amount = payload.borrow_amount ?? (long_orders.length > 0 ? sumNotional(long_orders) : inferred_input?.borrow_amount ?? 0);
|
|
74374
|
+
const requested_input = {
|
|
74375
|
+
borrow_amount,
|
|
74376
|
+
symbol: payload.symbol,
|
|
74377
|
+
margin_type: payload.margin_type,
|
|
74378
|
+
long_orders,
|
|
74379
|
+
short_orders,
|
|
74380
|
+
current_price: snapshot.current_price,
|
|
74381
|
+
max_spot_buy_orders: payload.max_spot_buy_orders,
|
|
74382
|
+
placement_overrides: inferred_input?.placement_overrides
|
|
74383
|
+
};
|
|
74384
|
+
const plan = buildSpotMarginHedgePlan({
|
|
74385
|
+
...requested_input,
|
|
74386
|
+
snapshot
|
|
74387
|
+
});
|
|
74388
|
+
const comparison = compareSpotMarginHedgeOrders({
|
|
74389
|
+
plan,
|
|
74390
|
+
spot_orders: snapshot.spot_orders,
|
|
74391
|
+
margin_orders: snapshot.margin_orders
|
|
74392
|
+
});
|
|
74393
|
+
return {
|
|
74394
|
+
snapshot,
|
|
74395
|
+
inferred_input,
|
|
74396
|
+
requested_input,
|
|
74397
|
+
plan,
|
|
74398
|
+
comparison
|
|
74399
|
+
};
|
|
74400
|
+
}
|
|
74401
|
+
async placeSpotMarginHedge(payload) {
|
|
74402
|
+
const preview = await this.previewSpotMarginHedge(payload);
|
|
74403
|
+
if (!payload.place) {
|
|
74404
|
+
return preview;
|
|
74405
|
+
}
|
|
74406
|
+
if (!this.main_client) {
|
|
74407
|
+
throw new Error("Main client not available for spot and margin trading");
|
|
74408
|
+
}
|
|
74409
|
+
if (payload.margin_type !== "isolated") {
|
|
74410
|
+
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
74411
|
+
}
|
|
74412
|
+
const symbol = payload.symbol.toUpperCase();
|
|
74413
|
+
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74414
|
+
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74415
|
+
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
74416
|
+
const requested_input = preview.requested_input;
|
|
74417
|
+
if (preview.snapshot.spot_orders.length > 0) {
|
|
74418
|
+
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
74419
|
+
orderId: order.orderId || order.order_id || order.id,
|
|
74420
|
+
origClientOrderId: order.origClientOrderId
|
|
74421
|
+
})));
|
|
74422
|
+
}
|
|
74423
|
+
if (preview.snapshot.margin_orders.length > 0) {
|
|
74424
|
+
await this.cancelMarginOrders(symbol, true, preview.snapshot.margin_orders.map((order) => ({
|
|
74425
|
+
orderId: order.orderId || order.order_id || order.id,
|
|
74426
|
+
origClientOrderId: order.origClientOrderId
|
|
74427
|
+
})));
|
|
74428
|
+
}
|
|
74429
|
+
const refreshed_snapshot = await this.getSpotMarginHedgeSnapshot({
|
|
74430
|
+
symbol,
|
|
74431
|
+
margin_type: payload.margin_type
|
|
74432
|
+
});
|
|
74433
|
+
const symbol_formats = await this.getSpotSymbolFormats(symbol);
|
|
74434
|
+
const execution_plan = buildSpotMarginHedgePlan({
|
|
74435
|
+
borrow_amount: requested_input.borrow_amount,
|
|
74436
|
+
symbol: requested_input.symbol,
|
|
74437
|
+
margin_type: requested_input.margin_type,
|
|
74438
|
+
long_orders: requested_input.long_orders,
|
|
74439
|
+
short_orders: requested_input.short_orders,
|
|
74440
|
+
current_price: refreshed_snapshot.current_price,
|
|
74441
|
+
max_spot_buy_orders: requested_input.max_spot_buy_orders,
|
|
74442
|
+
snapshot: refreshed_snapshot
|
|
74443
|
+
});
|
|
74444
|
+
let borrow_result = null;
|
|
74445
|
+
if (execution_plan.borrow_delta > 0) {
|
|
74446
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay({
|
|
74447
|
+
asset: quote_asset,
|
|
74448
|
+
amount: execution_plan.borrow_delta,
|
|
74449
|
+
isIsolated: "TRUE",
|
|
74450
|
+
symbol,
|
|
74451
|
+
type: "BORROW"
|
|
74452
|
+
});
|
|
74453
|
+
}
|
|
74454
|
+
let quote_transfer_result = null;
|
|
74455
|
+
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
74456
|
+
quote_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
|
|
74457
|
+
asset: quote_asset,
|
|
74458
|
+
amount: execution_plan.transfer_to_spot_amount,
|
|
74459
|
+
symbol,
|
|
74460
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74461
|
+
transTo: "SPOT"
|
|
74462
|
+
});
|
|
74463
|
+
}
|
|
74464
|
+
let base_transfer_result = null;
|
|
74465
|
+
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
74466
|
+
base_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
|
|
74467
|
+
asset: base_asset,
|
|
74468
|
+
amount: execution_plan.transfer_base_to_spot_amount,
|
|
74469
|
+
symbol,
|
|
74470
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74471
|
+
transTo: "SPOT"
|
|
74472
|
+
});
|
|
74473
|
+
}
|
|
74474
|
+
const spot_result = execution_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
74475
|
+
symbol,
|
|
74476
|
+
orders: execution_plan.orders_to_create.spot,
|
|
74477
|
+
price_places: symbol_formats.price_places,
|
|
74478
|
+
decimal_places: symbol_formats.decimal_places
|
|
74479
|
+
}) : [];
|
|
74480
|
+
const margin_result = execution_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
74481
|
+
symbol,
|
|
74482
|
+
orders: execution_plan.orders_to_create.margin,
|
|
74483
|
+
isIsolated: true,
|
|
74484
|
+
price_places: symbol_formats.price_places,
|
|
74485
|
+
decimal_places: symbol_formats.decimal_places
|
|
74486
|
+
}) : [];
|
|
74487
|
+
return {
|
|
74488
|
+
...preview,
|
|
74489
|
+
execution_plan,
|
|
74490
|
+
refreshed_snapshot,
|
|
74491
|
+
execution: {
|
|
74492
|
+
borrow: borrow_result ? {
|
|
74493
|
+
asset: quote_asset,
|
|
74494
|
+
amount: execution_plan.borrow_delta,
|
|
74495
|
+
response: borrow_result
|
|
74496
|
+
} : null,
|
|
74497
|
+
quote_transfer: quote_transfer_result ? {
|
|
74498
|
+
asset: quote_asset,
|
|
74499
|
+
amount: execution_plan.transfer_to_spot_amount,
|
|
74500
|
+
response: quote_transfer_result
|
|
74501
|
+
} : null,
|
|
74502
|
+
base_transfer: base_transfer_result ? {
|
|
74503
|
+
asset: base_asset,
|
|
74504
|
+
amount: execution_plan.transfer_base_to_spot_amount,
|
|
74505
|
+
response: base_transfer_result
|
|
74506
|
+
} : null,
|
|
74507
|
+
spot_orders: spot_result,
|
|
74508
|
+
margin_orders: margin_result
|
|
74509
|
+
}
|
|
74510
|
+
};
|
|
74511
|
+
}
|
|
74066
74512
|
async getLastUserTrade(payload) {
|
|
74067
74513
|
return await getLastUserTrade(this.client, payload.symbol);
|
|
74068
74514
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -372,6 +372,37 @@ declare abstract class BaseExchange {
|
|
|
372
372
|
place?: boolean;
|
|
373
373
|
cancel?: boolean;
|
|
374
374
|
}): Promise<any>;
|
|
375
|
+
previewSpotMarginHedge(_payload: {
|
|
376
|
+
borrow_amount?: number;
|
|
377
|
+
symbol: string;
|
|
378
|
+
margin_type: "isolated" | "cross";
|
|
379
|
+
long_orders?: Array<{
|
|
380
|
+
price: number;
|
|
381
|
+
quantity: number;
|
|
382
|
+
}>;
|
|
383
|
+
short_orders?: Array<{
|
|
384
|
+
price: number;
|
|
385
|
+
quantity: number;
|
|
386
|
+
}>;
|
|
387
|
+
max_spot_buy_orders?: number;
|
|
388
|
+
snapshot?: any;
|
|
389
|
+
}): Promise<any>;
|
|
390
|
+
placeSpotMarginHedge(_payload: {
|
|
391
|
+
borrow_amount?: number;
|
|
392
|
+
symbol: string;
|
|
393
|
+
margin_type: "isolated" | "cross";
|
|
394
|
+
long_orders?: Array<{
|
|
395
|
+
price: number;
|
|
396
|
+
quantity: number;
|
|
397
|
+
}>;
|
|
398
|
+
short_orders?: Array<{
|
|
399
|
+
price: number;
|
|
400
|
+
quantity: number;
|
|
401
|
+
}>;
|
|
402
|
+
max_spot_buy_orders?: number;
|
|
403
|
+
snapshot?: any;
|
|
404
|
+
place?: boolean;
|
|
405
|
+
}): Promise<any>;
|
|
375
406
|
placeSpotLimitOrders(_payload: any): Promise<void>;
|
|
376
407
|
buildCumulative(payload: {
|
|
377
408
|
orders: Order$1[];
|