@gbozee/ultimate 0.0.2-next.72 → 0.0.2-next.74
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 +782 -7
- package/dist/index.d.ts +102 -0
- package/dist/index.js +782 -7
- package/dist/mcp-server.cjs +782 -7
- package/dist/mcp-server.js +782 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71933,6 +71933,21 @@ class BaseExchange {
|
|
|
71933
71933
|
});
|
|
71934
71934
|
}
|
|
71935
71935
|
}
|
|
71936
|
+
async previewSpotMarginHedge(_payload) {
|
|
71937
|
+
throw new Error("Spot margin hedge preview is not supported on this exchange");
|
|
71938
|
+
}
|
|
71939
|
+
async previewFuturesReplay(_payload) {
|
|
71940
|
+
throw new Error("Futures replay preview is not supported on this exchange");
|
|
71941
|
+
}
|
|
71942
|
+
async placeSpotMarginHedge(_payload) {
|
|
71943
|
+
throw new Error("Spot margin hedge placement is not supported on this exchange");
|
|
71944
|
+
}
|
|
71945
|
+
async placeFuturesReplay(_payload) {
|
|
71946
|
+
throw new Error("Futures replay placement is not supported on this exchange");
|
|
71947
|
+
}
|
|
71948
|
+
async placeFuturesExactTrade(_payload) {
|
|
71949
|
+
throw new Error("Futures exact trade placement is not supported on this exchange");
|
|
71950
|
+
}
|
|
71936
71951
|
async placeSpotLimitOrders(_payload) {}
|
|
71937
71952
|
async buildCumulative(payload) {
|
|
71938
71953
|
const {
|
|
@@ -72340,6 +72355,389 @@ class BaseExchange {
|
|
|
72340
72355
|
|
|
72341
72356
|
// src/exchanges/binance/index.ts
|
|
72342
72357
|
var import_p_limit = __toESM(require_p_limit(), 1);
|
|
72358
|
+
|
|
72359
|
+
// src/exchanges/binance/futures-replay.ts
|
|
72360
|
+
function roundNumber(value2, digits = 8) {
|
|
72361
|
+
return Number(value2.toFixed(digits));
|
|
72362
|
+
}
|
|
72363
|
+
function parseNumber2(value2) {
|
|
72364
|
+
if (value2 === undefined || value2 === null || value2 === "")
|
|
72365
|
+
return;
|
|
72366
|
+
const parsed = Number(value2);
|
|
72367
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
72368
|
+
return;
|
|
72369
|
+
return roundNumber(parsed);
|
|
72370
|
+
}
|
|
72371
|
+
function normalizeOrderInput(order) {
|
|
72372
|
+
const stop = parseNumber2(order.stop) ?? parseNumber2(order.stopPrice) ?? parseNumber2(order.triggerPrice);
|
|
72373
|
+
const type = order.type ? order.type.toUpperCase() : undefined;
|
|
72374
|
+
const quantity = order.quantity ?? order.qty ?? 0;
|
|
72375
|
+
return {
|
|
72376
|
+
price: roundNumber(Number(order.price)),
|
|
72377
|
+
quantity: roundNumber(Number(quantity)),
|
|
72378
|
+
...stop ? { stop } : {},
|
|
72379
|
+
...type && type !== "LIMIT" ? { type } : {}
|
|
72380
|
+
};
|
|
72381
|
+
}
|
|
72382
|
+
function sortReplayOrders(orders) {
|
|
72383
|
+
return [...orders].sort((left, right) => {
|
|
72384
|
+
const leftTrigger = left.stop ?? left.price;
|
|
72385
|
+
const rightTrigger = right.stop ?? right.price;
|
|
72386
|
+
if (leftTrigger !== rightTrigger)
|
|
72387
|
+
return leftTrigger - rightTrigger;
|
|
72388
|
+
if (left.price !== right.price)
|
|
72389
|
+
return left.price - right.price;
|
|
72390
|
+
return left.quantity - right.quantity;
|
|
72391
|
+
});
|
|
72392
|
+
}
|
|
72393
|
+
function buildReplayStopLimitPrice(payload) {
|
|
72394
|
+
const spread = payload.stop > 30000 ? 1.00005 : 1.0002;
|
|
72395
|
+
return roundNumber(payload.kind === "long" ? payload.stop * spread ** -1 : payload.stop * spread);
|
|
72396
|
+
}
|
|
72397
|
+
function normalizeRequestedEntryOrders(orders) {
|
|
72398
|
+
return sortReplayOrders(orders.map((order) => ({
|
|
72399
|
+
price: roundNumber(Number(order.price)),
|
|
72400
|
+
quantity: roundNumber(Number(order.quantity))
|
|
72401
|
+
})));
|
|
72402
|
+
}
|
|
72403
|
+
function normalizeRequestedStopOrders(payload) {
|
|
72404
|
+
return sortReplayOrders(payload.orders.map((order) => {
|
|
72405
|
+
const stop = parseNumber2(order.stop) ?? roundNumber(Number(order.price));
|
|
72406
|
+
return {
|
|
72407
|
+
price: order.price !== undefined ? roundNumber(Number(order.price)) : buildReplayStopLimitPrice({
|
|
72408
|
+
kind: payload.kind,
|
|
72409
|
+
stop
|
|
72410
|
+
}),
|
|
72411
|
+
quantity: roundNumber(Number(order.quantity)),
|
|
72412
|
+
stop,
|
|
72413
|
+
type: (order.type || "STOP").toUpperCase()
|
|
72414
|
+
};
|
|
72415
|
+
}));
|
|
72416
|
+
}
|
|
72417
|
+
function normalizeRequestedTakeProfitOrders(orders) {
|
|
72418
|
+
return sortReplayOrders(orders.map((order) => {
|
|
72419
|
+
const stop = parseNumber2(order.stop);
|
|
72420
|
+
const type = order.type?.toUpperCase();
|
|
72421
|
+
return {
|
|
72422
|
+
price: roundNumber(Number(order.price)),
|
|
72423
|
+
quantity: roundNumber(Number(order.quantity)),
|
|
72424
|
+
...stop ? { stop } : {},
|
|
72425
|
+
...type ? { type } : stop ? { type: "TAKE_PROFIT" } : {}
|
|
72426
|
+
};
|
|
72427
|
+
}));
|
|
72428
|
+
}
|
|
72429
|
+
function buildOrderKey(order) {
|
|
72430
|
+
return [
|
|
72431
|
+
order.type || "LIMIT",
|
|
72432
|
+
order.price,
|
|
72433
|
+
order.quantity,
|
|
72434
|
+
order.stop ?? ""
|
|
72435
|
+
].join(":");
|
|
72436
|
+
}
|
|
72437
|
+
function buildComparisonBucket(options) {
|
|
72438
|
+
const expected = sortReplayOrders(options.expected);
|
|
72439
|
+
const actual = sortReplayOrders(options.actual);
|
|
72440
|
+
const expected_keys = expected.map(buildOrderKey);
|
|
72441
|
+
const actual_keys = actual.map(buildOrderKey);
|
|
72442
|
+
return {
|
|
72443
|
+
expected,
|
|
72444
|
+
actual,
|
|
72445
|
+
missing: expected_keys.filter((key) => !actual_keys.includes(key)),
|
|
72446
|
+
extra: actual_keys.filter((key) => !expected_keys.includes(key))
|
|
72447
|
+
};
|
|
72448
|
+
}
|
|
72449
|
+
function buildFuturesReplaySnapshot(payload) {
|
|
72450
|
+
const entry_side = payload.kind === "long" ? "buy" : "sell";
|
|
72451
|
+
const close_side = payload.kind === "long" ? "sell" : "buy";
|
|
72452
|
+
const entry_orders = [];
|
|
72453
|
+
const stop_orders = [];
|
|
72454
|
+
const take_profit_orders = [];
|
|
72455
|
+
for (const order of payload.open_orders) {
|
|
72456
|
+
if (order.kind !== payload.kind)
|
|
72457
|
+
continue;
|
|
72458
|
+
const normalized_order = normalizeOrderInput(order);
|
|
72459
|
+
const type = (normalized_order.type || "LIMIT").toUpperCase();
|
|
72460
|
+
if (order.side === entry_side) {
|
|
72461
|
+
entry_orders.push(normalized_order);
|
|
72462
|
+
continue;
|
|
72463
|
+
}
|
|
72464
|
+
if (order.side !== close_side)
|
|
72465
|
+
continue;
|
|
72466
|
+
if (type.includes("TAKE_PROFIT") || normalized_order.stop === undefined) {
|
|
72467
|
+
take_profit_orders.push(normalized_order);
|
|
72468
|
+
continue;
|
|
72469
|
+
}
|
|
72470
|
+
stop_orders.push(normalized_order);
|
|
72471
|
+
}
|
|
72472
|
+
return {
|
|
72473
|
+
symbol: payload.symbol,
|
|
72474
|
+
kind: payload.kind,
|
|
72475
|
+
current_price: payload.current_price,
|
|
72476
|
+
position: payload.position,
|
|
72477
|
+
open_orders: payload.open_orders.filter((order) => order.kind === payload.kind),
|
|
72478
|
+
entry_orders: sortReplayOrders(entry_orders),
|
|
72479
|
+
stop_orders: sortReplayOrders(stop_orders),
|
|
72480
|
+
take_profit_orders: sortReplayOrders(take_profit_orders)
|
|
72481
|
+
};
|
|
72482
|
+
}
|
|
72483
|
+
function inferFuturesReplayInputFromLiveOrders(payload) {
|
|
72484
|
+
const snapshot = buildFuturesReplaySnapshot(payload);
|
|
72485
|
+
return {
|
|
72486
|
+
symbol: payload.symbol,
|
|
72487
|
+
kind: payload.kind,
|
|
72488
|
+
current_price: payload.current_price,
|
|
72489
|
+
position: payload.position,
|
|
72490
|
+
entry_orders: snapshot.entry_orders,
|
|
72491
|
+
stop_orders: snapshot.stop_orders,
|
|
72492
|
+
take_profit_orders: snapshot.take_profit_orders
|
|
72493
|
+
};
|
|
72494
|
+
}
|
|
72495
|
+
function compareFuturesReplayOrders(payload) {
|
|
72496
|
+
const entry = buildComparisonBucket({
|
|
72497
|
+
expected: payload.entry_orders,
|
|
72498
|
+
actual: payload.snapshot.entry_orders
|
|
72499
|
+
});
|
|
72500
|
+
const stop = buildComparisonBucket({
|
|
72501
|
+
expected: payload.stop_orders,
|
|
72502
|
+
actual: payload.snapshot.stop_orders
|
|
72503
|
+
});
|
|
72504
|
+
const take_profit = buildComparisonBucket({
|
|
72505
|
+
expected: payload.take_profit_orders,
|
|
72506
|
+
actual: payload.snapshot.take_profit_orders
|
|
72507
|
+
});
|
|
72508
|
+
return {
|
|
72509
|
+
matches: entry.missing.length === 0 && entry.extra.length === 0 && stop.missing.length === 0 && stop.extra.length === 0 && take_profit.missing.length === 0 && take_profit.extra.length === 0,
|
|
72510
|
+
entry,
|
|
72511
|
+
stop,
|
|
72512
|
+
take_profit
|
|
72513
|
+
};
|
|
72514
|
+
}
|
|
72515
|
+
function buildFuturesReplayPlan(payload) {
|
|
72516
|
+
return {
|
|
72517
|
+
symbol: payload.symbol,
|
|
72518
|
+
kind: payload.kind,
|
|
72519
|
+
orders_to_cancel: payload.snapshot.open_orders,
|
|
72520
|
+
orders_to_create: {
|
|
72521
|
+
entry: sortReplayOrders(payload.entry_orders),
|
|
72522
|
+
stop: sortReplayOrders(payload.stop_orders),
|
|
72523
|
+
take_profit: sortReplayOrders(payload.take_profit_orders)
|
|
72524
|
+
}
|
|
72525
|
+
};
|
|
72526
|
+
}
|
|
72527
|
+
function normalizeFuturesReplayInput(payload) {
|
|
72528
|
+
return {
|
|
72529
|
+
entry_orders: normalizeRequestedEntryOrders(payload.entry_orders),
|
|
72530
|
+
stop_orders: normalizeRequestedStopOrders({
|
|
72531
|
+
kind: payload.kind,
|
|
72532
|
+
orders: payload.stop_orders
|
|
72533
|
+
}),
|
|
72534
|
+
take_profit_orders: normalizeRequestedTakeProfitOrders(payload.take_profit_orders)
|
|
72535
|
+
};
|
|
72536
|
+
}
|
|
72537
|
+
|
|
72538
|
+
// src/exchanges/binance/spot-margin-hedge.ts
|
|
72539
|
+
function roundNumber2(value2, digits = 8) {
|
|
72540
|
+
return Number(value2.toFixed(digits));
|
|
72541
|
+
}
|
|
72542
|
+
var STOP_LIMIT_SPREAD_PERCENT = 0.0005;
|
|
72543
|
+
function normalizeOrderInput2(order) {
|
|
72544
|
+
return {
|
|
72545
|
+
price: roundNumber2(order.price),
|
|
72546
|
+
quantity: roundNumber2(order.quantity)
|
|
72547
|
+
};
|
|
72548
|
+
}
|
|
72549
|
+
function sortOrderInputs(orders) {
|
|
72550
|
+
return [...orders].map(normalizeOrderInput2).sort((left, right) => left.price - right.price);
|
|
72551
|
+
}
|
|
72552
|
+
function normalizeOrderForComparison(order) {
|
|
72553
|
+
const type = (order.type || "limit").toLowerCase();
|
|
72554
|
+
const raw_stop = "stop" in order && order.stop !== undefined ? order.stop : ("stopPrice" in order) ? order.stopPrice : undefined;
|
|
72555
|
+
const parsed_stop = raw_stop === undefined || raw_stop === null || raw_stop === "" ? undefined : Number(raw_stop);
|
|
72556
|
+
return {
|
|
72557
|
+
side: order.side,
|
|
72558
|
+
type,
|
|
72559
|
+
price: roundNumber2(order.price),
|
|
72560
|
+
quantity: roundNumber2(order.quantity),
|
|
72561
|
+
stop: parsed_stop !== undefined && Number.isFinite(parsed_stop) && parsed_stop > 0 ? roundNumber2(parsed_stop) : undefined
|
|
72562
|
+
};
|
|
72563
|
+
}
|
|
72564
|
+
function normalizeOrders(orders) {
|
|
72565
|
+
return [...orders].map(normalizeOrderForComparison).sort((left, right) => {
|
|
72566
|
+
if (left.side !== right.side)
|
|
72567
|
+
return left.side.localeCompare(right.side);
|
|
72568
|
+
if (left.type !== right.type)
|
|
72569
|
+
return left.type.localeCompare(right.type);
|
|
72570
|
+
if (left.price !== right.price)
|
|
72571
|
+
return left.price - right.price;
|
|
72572
|
+
return left.quantity - right.quantity;
|
|
72573
|
+
});
|
|
72574
|
+
}
|
|
72575
|
+
function toPlannedOrders(orders) {
|
|
72576
|
+
return normalizeOrders(orders).map((order) => ({
|
|
72577
|
+
side: order.side,
|
|
72578
|
+
type: order.type,
|
|
72579
|
+
price: order.price,
|
|
72580
|
+
quantity: order.quantity,
|
|
72581
|
+
stop: order.stop
|
|
72582
|
+
}));
|
|
72583
|
+
}
|
|
72584
|
+
function buildOrderKey2(order) {
|
|
72585
|
+
return [
|
|
72586
|
+
order.side,
|
|
72587
|
+
order.type,
|
|
72588
|
+
order.price,
|
|
72589
|
+
order.quantity,
|
|
72590
|
+
order.stop ?? ""
|
|
72591
|
+
].join(":");
|
|
72592
|
+
}
|
|
72593
|
+
function buildDiff(options) {
|
|
72594
|
+
const expectedKeys = options.expected.map(buildOrderKey2);
|
|
72595
|
+
const actualKeys = options.actual.map(buildOrderKey2);
|
|
72596
|
+
return {
|
|
72597
|
+
missing: expectedKeys.filter((key) => !actualKeys.includes(key)),
|
|
72598
|
+
extra: actualKeys.filter((key) => !expectedKeys.includes(key))
|
|
72599
|
+
};
|
|
72600
|
+
}
|
|
72601
|
+
function getQuoteAsset(symbol) {
|
|
72602
|
+
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
72603
|
+
const target = quoteAssets.find((asset) => symbol.endsWith(asset));
|
|
72604
|
+
return target || symbol.slice(-4);
|
|
72605
|
+
}
|
|
72606
|
+
function sumNotional(orders) {
|
|
72607
|
+
return roundNumber2(orders.reduce((sum, order) => sum + order.price * order.quantity, 0), 8);
|
|
72608
|
+
}
|
|
72609
|
+
function sumQuantity(orders) {
|
|
72610
|
+
return roundNumber2(orders.reduce((sum, order) => sum + order.quantity, 0), 8);
|
|
72611
|
+
}
|
|
72612
|
+
function buildLongOrderPlacement(options) {
|
|
72613
|
+
const { order, current_price } = options;
|
|
72614
|
+
if (order.price > current_price) {
|
|
72615
|
+
return {
|
|
72616
|
+
side: "buy",
|
|
72617
|
+
type: "stop_loss_limit",
|
|
72618
|
+
price: order.price,
|
|
72619
|
+
stop: roundNumber2(order.price * (1 - STOP_LIMIT_SPREAD_PERCENT)),
|
|
72620
|
+
quantity: order.quantity
|
|
72621
|
+
};
|
|
72622
|
+
}
|
|
72623
|
+
return {
|
|
72624
|
+
side: "buy",
|
|
72625
|
+
type: "limit",
|
|
72626
|
+
price: order.price,
|
|
72627
|
+
quantity: order.quantity
|
|
72628
|
+
};
|
|
72629
|
+
}
|
|
72630
|
+
function inferSpotMarginHedgeInputFromLiveOrders(options) {
|
|
72631
|
+
const long_orders = sortOrderInputs([
|
|
72632
|
+
...options.spot_orders.filter((order) => order.side === "buy").map((order) => ({
|
|
72633
|
+
price: order.price,
|
|
72634
|
+
quantity: order.quantity
|
|
72635
|
+
})),
|
|
72636
|
+
...options.margin_orders.filter((order) => order.side === "buy").map((order) => ({
|
|
72637
|
+
price: order.price,
|
|
72638
|
+
quantity: order.quantity
|
|
72639
|
+
}))
|
|
72640
|
+
]);
|
|
72641
|
+
const short_orders = sortOrderInputs([
|
|
72642
|
+
...options.spot_orders.filter((order) => order.side === "sell").map((order) => ({
|
|
72643
|
+
price: order.price,
|
|
72644
|
+
quantity: order.quantity
|
|
72645
|
+
})),
|
|
72646
|
+
...options.margin_orders.filter((order) => order.side === "sell").map((order) => ({
|
|
72647
|
+
price: order.price,
|
|
72648
|
+
quantity: order.quantity
|
|
72649
|
+
}))
|
|
72650
|
+
]);
|
|
72651
|
+
return {
|
|
72652
|
+
symbol: options.symbol,
|
|
72653
|
+
margin_type: options.margin_type,
|
|
72654
|
+
borrow_amount: sumNotional(long_orders),
|
|
72655
|
+
long_orders,
|
|
72656
|
+
short_orders,
|
|
72657
|
+
placement_overrides: {
|
|
72658
|
+
spot: toPlannedOrders(options.spot_orders),
|
|
72659
|
+
margin: toPlannedOrders(options.margin_orders)
|
|
72660
|
+
}
|
|
72661
|
+
};
|
|
72662
|
+
}
|
|
72663
|
+
function buildSpotMarginHedgePlan(options) {
|
|
72664
|
+
const max_spot_buy_orders = options.max_spot_buy_orders ?? 5;
|
|
72665
|
+
const long_orders = sortOrderInputs(options.long_orders);
|
|
72666
|
+
const short_orders = sortOrderInputs(options.short_orders);
|
|
72667
|
+
const spot_long_orders = long_orders.slice(0, max_spot_buy_orders);
|
|
72668
|
+
const margin_long_orders = long_orders.slice(max_spot_buy_orders);
|
|
72669
|
+
const default_spot_orders_to_create = [
|
|
72670
|
+
...spot_long_orders.map((order) => buildLongOrderPlacement({
|
|
72671
|
+
order,
|
|
72672
|
+
current_price: options.current_price
|
|
72673
|
+
})),
|
|
72674
|
+
...short_orders.map((order) => ({
|
|
72675
|
+
side: "sell",
|
|
72676
|
+
type: "limit",
|
|
72677
|
+
price: order.price,
|
|
72678
|
+
quantity: order.quantity
|
|
72679
|
+
}))
|
|
72680
|
+
];
|
|
72681
|
+
const default_margin_orders_to_create = margin_long_orders.map((order) => buildLongOrderPlacement({
|
|
72682
|
+
order,
|
|
72683
|
+
current_price: options.current_price
|
|
72684
|
+
}));
|
|
72685
|
+
const spot_orders_to_create = options.placement_overrides?.spot || default_spot_orders_to_create;
|
|
72686
|
+
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
72687
|
+
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
72688
|
+
const spot_long_notional = sumNotional(spot_long_orders);
|
|
72689
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
72690
|
+
const short_quantity = sumQuantity(short_orders);
|
|
72691
|
+
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
72692
|
+
return {
|
|
72693
|
+
borrow_amount: options.borrow_amount,
|
|
72694
|
+
borrow_delta,
|
|
72695
|
+
transfer_to_spot_amount,
|
|
72696
|
+
transfer_to_spot_asset: getQuoteAsset(options.symbol),
|
|
72697
|
+
transfer_base_to_spot_amount,
|
|
72698
|
+
orders_to_cancel: {
|
|
72699
|
+
spot: options.snapshot.spot_orders,
|
|
72700
|
+
margin: options.snapshot.margin_orders
|
|
72701
|
+
},
|
|
72702
|
+
orders_to_create: {
|
|
72703
|
+
spot: spot_orders_to_create,
|
|
72704
|
+
margin: margin_orders_to_create
|
|
72705
|
+
},
|
|
72706
|
+
normalized: {
|
|
72707
|
+
spot: normalizeOrders(spot_orders_to_create),
|
|
72708
|
+
margin: normalizeOrders(margin_orders_to_create)
|
|
72709
|
+
}
|
|
72710
|
+
};
|
|
72711
|
+
}
|
|
72712
|
+
function compareSpotMarginHedgeOrders(options) {
|
|
72713
|
+
const actualSpot = normalizeOrders(options.spot_orders);
|
|
72714
|
+
const actualMargin = normalizeOrders(options.margin_orders);
|
|
72715
|
+
const spotDiff = buildDiff({
|
|
72716
|
+
expected: options.plan.normalized.spot,
|
|
72717
|
+
actual: actualSpot
|
|
72718
|
+
});
|
|
72719
|
+
const marginDiff = buildDiff({
|
|
72720
|
+
expected: options.plan.normalized.margin,
|
|
72721
|
+
actual: actualMargin
|
|
72722
|
+
});
|
|
72723
|
+
return {
|
|
72724
|
+
matches: spotDiff.missing.length === 0 && spotDiff.extra.length === 0 && marginDiff.missing.length === 0 && marginDiff.extra.length === 0,
|
|
72725
|
+
spot: {
|
|
72726
|
+
expected: options.plan.normalized.spot,
|
|
72727
|
+
actual: actualSpot,
|
|
72728
|
+
missing: spotDiff.missing,
|
|
72729
|
+
extra: spotDiff.extra
|
|
72730
|
+
},
|
|
72731
|
+
margin: {
|
|
72732
|
+
expected: options.plan.normalized.margin,
|
|
72733
|
+
actual: actualMargin,
|
|
72734
|
+
missing: marginDiff.missing,
|
|
72735
|
+
extra: marginDiff.extra
|
|
72736
|
+
}
|
|
72737
|
+
};
|
|
72738
|
+
}
|
|
72739
|
+
|
|
72740
|
+
// src/exchanges/binance/index.ts
|
|
72343
72741
|
var CONSTANTS = {
|
|
72344
72742
|
SPOT_TO_FIAT: "MAIN_C2C",
|
|
72345
72743
|
SPOT_TO_USDT_FUTURE: "MAIN_UMFUTURE",
|
|
@@ -72828,12 +73226,20 @@ async function getCrossMarginAccountDetails(client) {
|
|
|
72828
73226
|
async function cancelMarginOrders(client, symbol, isIsolated = false, orders) {
|
|
72829
73227
|
try {
|
|
72830
73228
|
if (orders && orders.length > 0) {
|
|
72831
|
-
const cancelPromises = orders.map((order) =>
|
|
72832
|
-
|
|
72833
|
-
|
|
72834
|
-
|
|
72835
|
-
|
|
72836
|
-
|
|
73229
|
+
const cancelPromises = orders.map((order) => {
|
|
73230
|
+
const payload = {
|
|
73231
|
+
symbol,
|
|
73232
|
+
isIsolated: isIsolated ? "TRUE" : "FALSE"
|
|
73233
|
+
};
|
|
73234
|
+
if (order.orderId !== undefined) {
|
|
73235
|
+
payload.orderId = order.orderId;
|
|
73236
|
+
}
|
|
73237
|
+
const origClientOrderId = order.origClientOrderId ?? order.clientOrderId;
|
|
73238
|
+
if (origClientOrderId !== undefined) {
|
|
73239
|
+
payload.origClientOrderId = origClientOrderId;
|
|
73240
|
+
}
|
|
73241
|
+
return client.marginAccountCancelOrder(payload);
|
|
73242
|
+
});
|
|
72837
73243
|
return await Promise.all(cancelPromises);
|
|
72838
73244
|
} else {
|
|
72839
73245
|
return await client.marginAccountCancelOpenOrders({
|
|
@@ -73088,7 +73494,7 @@ async function placeStopOrder(client, payload) {
|
|
|
73088
73494
|
});
|
|
73089
73495
|
}
|
|
73090
73496
|
}
|
|
73091
|
-
const spread = 1.00005;
|
|
73497
|
+
const spread = payload.stop > 30000 ? 1.00005 : 1.0002;
|
|
73092
73498
|
const order = {
|
|
73093
73499
|
kind: payload.kind,
|
|
73094
73500
|
side: payload.kind === "long" ? "sell" : "buy",
|
|
@@ -73495,6 +73901,16 @@ async function getAllOpenOrders(payload) {
|
|
|
73495
73901
|
const response = await client.getAllOpenOrders();
|
|
73496
73902
|
return response;
|
|
73497
73903
|
}
|
|
73904
|
+
function getPrintfFormatFromStepSize(stepSize, fallback) {
|
|
73905
|
+
if (stepSize === undefined || stepSize === null) {
|
|
73906
|
+
return fallback;
|
|
73907
|
+
}
|
|
73908
|
+
const normalized = String(stepSize);
|
|
73909
|
+
const [, decimalPart = ""] = normalized.split(".");
|
|
73910
|
+
const trimmed = decimalPart.replace(/0+$/, "");
|
|
73911
|
+
const digits = trimmed.length;
|
|
73912
|
+
return `%.${digits}f`;
|
|
73913
|
+
}
|
|
73498
73914
|
|
|
73499
73915
|
class BinanceExchange extends BaseExchange {
|
|
73500
73916
|
main_client;
|
|
@@ -73931,6 +74347,54 @@ class BinanceExchange extends BaseExchange {
|
|
|
73931
74347
|
throw error;
|
|
73932
74348
|
}
|
|
73933
74349
|
}
|
|
74350
|
+
async getFuturesSymbolFormats(symbol) {
|
|
74351
|
+
if (typeof this.client.getExchangeInfo !== "function") {
|
|
74352
|
+
return {
|
|
74353
|
+
price_places: "%.8f",
|
|
74354
|
+
decimal_places: "%.8f"
|
|
74355
|
+
};
|
|
74356
|
+
}
|
|
74357
|
+
try {
|
|
74358
|
+
const exchange_info = await this.client.getExchangeInfo();
|
|
74359
|
+
const symbol_info = exchange_info?.symbols?.find((item) => item.symbol === symbol.toUpperCase());
|
|
74360
|
+
const price_filter = symbol_info?.filters?.find((filter) => filter.filterType === "PRICE_FILTER");
|
|
74361
|
+
const lot_size_filter = symbol_info?.filters?.find((filter) => filter.filterType === "LOT_SIZE");
|
|
74362
|
+
return {
|
|
74363
|
+
price_places: getPrintfFormatFromStepSize(price_filter?.tickSize, "%.8f"),
|
|
74364
|
+
decimal_places: getPrintfFormatFromStepSize(lot_size_filter?.stepSize, "%.8f")
|
|
74365
|
+
};
|
|
74366
|
+
} catch (error) {
|
|
74367
|
+
console.error("Error fetching futures symbol formats:", error);
|
|
74368
|
+
return {
|
|
74369
|
+
price_places: "%.8f",
|
|
74370
|
+
decimal_places: "%.8f"
|
|
74371
|
+
};
|
|
74372
|
+
}
|
|
74373
|
+
}
|
|
74374
|
+
async getSpotSymbolFormats(symbol) {
|
|
74375
|
+
if (!this.main_client || typeof this.main_client.getExchangeInfo !== "function") {
|
|
74376
|
+
return {
|
|
74377
|
+
price_places: "%.8f",
|
|
74378
|
+
decimal_places: "%.8f"
|
|
74379
|
+
};
|
|
74380
|
+
}
|
|
74381
|
+
try {
|
|
74382
|
+
const exchange_info = await this.main_client.getExchangeInfo();
|
|
74383
|
+
const symbol_info = exchange_info?.symbols?.find((item) => item.symbol === symbol.toUpperCase());
|
|
74384
|
+
const price_filter = symbol_info?.filters?.find((filter) => filter.filterType === "PRICE_FILTER");
|
|
74385
|
+
const lot_size_filter = symbol_info?.filters?.find((filter) => filter.filterType === "LOT_SIZE");
|
|
74386
|
+
return {
|
|
74387
|
+
price_places: getPrintfFormatFromStepSize(price_filter?.tickSize, "%.8f"),
|
|
74388
|
+
decimal_places: getPrintfFormatFromStepSize(lot_size_filter?.stepSize, "%.8f")
|
|
74389
|
+
};
|
|
74390
|
+
} catch (error) {
|
|
74391
|
+
console.error("Error fetching spot symbol formats:", error);
|
|
74392
|
+
return {
|
|
74393
|
+
price_places: "%.8f",
|
|
74394
|
+
decimal_places: "%.8f"
|
|
74395
|
+
};
|
|
74396
|
+
}
|
|
74397
|
+
}
|
|
73934
74398
|
async createMarginLimitOrders(payload) {
|
|
73935
74399
|
if (!this.main_client) {
|
|
73936
74400
|
throw new Error("Main client not available for margin trading");
|
|
@@ -73961,6 +74425,317 @@ class BinanceExchange extends BaseExchange {
|
|
|
73961
74425
|
}
|
|
73962
74426
|
return await getMarginOpenOrders(this.main_client, symbol, isIsolated);
|
|
73963
74427
|
}
|
|
74428
|
+
async getFuturesReplaySnapshot(payload) {
|
|
74429
|
+
const symbol = payload.symbol.toUpperCase();
|
|
74430
|
+
const [open_orders, position_info, current_price] = await Promise.all([
|
|
74431
|
+
this.getOpenOrders({ symbol }),
|
|
74432
|
+
this.getPositionInfo(symbol),
|
|
74433
|
+
this.getCurrentPrice(symbol)
|
|
74434
|
+
]);
|
|
74435
|
+
return buildFuturesReplaySnapshot({
|
|
74436
|
+
symbol,
|
|
74437
|
+
kind: payload.kind,
|
|
74438
|
+
current_price,
|
|
74439
|
+
position: {
|
|
74440
|
+
size: position_info[payload.kind]?.size || 0
|
|
74441
|
+
},
|
|
74442
|
+
open_orders
|
|
74443
|
+
});
|
|
74444
|
+
}
|
|
74445
|
+
async previewFuturesReplay(payload) {
|
|
74446
|
+
const snapshot = payload.snapshot ? {
|
|
74447
|
+
...payload.snapshot,
|
|
74448
|
+
current_price: payload.snapshot.current_price ?? await this.getCurrentPrice(payload.symbol)
|
|
74449
|
+
} : await this.getFuturesReplaySnapshot({
|
|
74450
|
+
symbol: payload.symbol,
|
|
74451
|
+
kind: payload.kind
|
|
74452
|
+
});
|
|
74453
|
+
const shouldInferEntryOrders = !payload.entry_orders || payload.entry_orders.length === 0;
|
|
74454
|
+
const shouldInferStopOrders = !payload.stop_orders || payload.stop_orders.length === 0;
|
|
74455
|
+
const shouldInferTakeProfitOrders = !payload.take_profit_orders || payload.take_profit_orders.length === 0;
|
|
74456
|
+
const inferred_input = shouldInferEntryOrders || shouldInferStopOrders || shouldInferTakeProfitOrders ? inferFuturesReplayInputFromLiveOrders({
|
|
74457
|
+
symbol: payload.symbol,
|
|
74458
|
+
kind: payload.kind,
|
|
74459
|
+
current_price: snapshot.current_price,
|
|
74460
|
+
position: snapshot.position,
|
|
74461
|
+
open_orders: snapshot.open_orders
|
|
74462
|
+
}) : undefined;
|
|
74463
|
+
const explicit_input = normalizeFuturesReplayInput({
|
|
74464
|
+
kind: payload.kind,
|
|
74465
|
+
entry_orders: payload.entry_orders || [],
|
|
74466
|
+
stop_orders: payload.stop_orders || [],
|
|
74467
|
+
take_profit_orders: payload.take_profit_orders || []
|
|
74468
|
+
});
|
|
74469
|
+
const entry_orders = shouldInferEntryOrders ? inferred_input?.entry_orders || [] : explicit_input.entry_orders;
|
|
74470
|
+
const stop_orders = shouldInferStopOrders ? inferred_input?.stop_orders || [] : explicit_input.stop_orders;
|
|
74471
|
+
const take_profit_orders = shouldInferTakeProfitOrders ? inferred_input?.take_profit_orders || [] : explicit_input.take_profit_orders;
|
|
74472
|
+
const requested_input = {
|
|
74473
|
+
symbol: payload.symbol,
|
|
74474
|
+
kind: payload.kind,
|
|
74475
|
+
entry_orders,
|
|
74476
|
+
stop_orders,
|
|
74477
|
+
take_profit_orders
|
|
74478
|
+
};
|
|
74479
|
+
const plan = buildFuturesReplayPlan({
|
|
74480
|
+
...requested_input,
|
|
74481
|
+
snapshot
|
|
74482
|
+
});
|
|
74483
|
+
const comparison = compareFuturesReplayOrders({
|
|
74484
|
+
...requested_input,
|
|
74485
|
+
snapshot
|
|
74486
|
+
});
|
|
74487
|
+
return {
|
|
74488
|
+
snapshot,
|
|
74489
|
+
inferred_input,
|
|
74490
|
+
requested_input,
|
|
74491
|
+
plan,
|
|
74492
|
+
comparison
|
|
74493
|
+
};
|
|
74494
|
+
}
|
|
74495
|
+
async placeFuturesReplay(payload) {
|
|
74496
|
+
const preview = await this.previewFuturesReplay(payload);
|
|
74497
|
+
if (!payload.place) {
|
|
74498
|
+
return preview;
|
|
74499
|
+
}
|
|
74500
|
+
const symbol = payload.symbol.toUpperCase();
|
|
74501
|
+
await cancelAllOrders(this.client, symbol, {
|
|
74502
|
+
kind: payload.kind
|
|
74503
|
+
});
|
|
74504
|
+
const refreshed_snapshot = await this.getFuturesReplaySnapshot({
|
|
74505
|
+
symbol,
|
|
74506
|
+
kind: payload.kind
|
|
74507
|
+
});
|
|
74508
|
+
const execution_plan = buildFuturesReplayPlan({
|
|
74509
|
+
symbol,
|
|
74510
|
+
kind: payload.kind,
|
|
74511
|
+
entry_orders: preview.requested_input.entry_orders,
|
|
74512
|
+
stop_orders: preview.requested_input.stop_orders,
|
|
74513
|
+
take_profit_orders: preview.requested_input.take_profit_orders,
|
|
74514
|
+
snapshot: refreshed_snapshot
|
|
74515
|
+
});
|
|
74516
|
+
const { price_places, decimal_places } = await this.getFuturesSymbolFormats(symbol);
|
|
74517
|
+
const entry_orders = execution_plan.orders_to_create.entry.map((order) => ({
|
|
74518
|
+
...order,
|
|
74519
|
+
side: payload.kind === "long" ? "buy" : "sell",
|
|
74520
|
+
kind: payload.kind
|
|
74521
|
+
}));
|
|
74522
|
+
const stop_orders = execution_plan.orders_to_create.stop.map((order) => ({
|
|
74523
|
+
...order,
|
|
74524
|
+
side: payload.kind === "long" ? "sell" : "buy",
|
|
74525
|
+
kind: payload.kind,
|
|
74526
|
+
type: order.type || "STOP"
|
|
74527
|
+
}));
|
|
74528
|
+
const take_profit_orders = execution_plan.orders_to_create.take_profit.map((order) => ({
|
|
74529
|
+
...order,
|
|
74530
|
+
side: payload.kind === "long" ? "sell" : "buy",
|
|
74531
|
+
kind: payload.kind,
|
|
74532
|
+
...order.stop ? { type: order.type || "TAKE_PROFIT" } : {}
|
|
74533
|
+
}));
|
|
74534
|
+
const execution = entry_orders.length > 0 || stop_orders.length > 0 || take_profit_orders.length > 0 ? await createLimitPurchaseOrdersParallel(this.client, symbol, price_places, decimal_places, [...entry_orders, ...stop_orders, ...take_profit_orders]) : [];
|
|
74535
|
+
return {
|
|
74536
|
+
...preview,
|
|
74537
|
+
execution_plan,
|
|
74538
|
+
refreshed_snapshot,
|
|
74539
|
+
execution
|
|
74540
|
+
};
|
|
74541
|
+
}
|
|
74542
|
+
async placeFuturesExactTrade(payload) {
|
|
74543
|
+
return await this.placeFuturesReplay(payload);
|
|
74544
|
+
}
|
|
74545
|
+
async getSpotMarginHedgeSnapshot(payload) {
|
|
74546
|
+
if (!this.main_client) {
|
|
74547
|
+
throw new Error("Main client not available for spot and margin trading");
|
|
74548
|
+
}
|
|
74549
|
+
if (payload.margin_type !== "isolated") {
|
|
74550
|
+
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
74551
|
+
}
|
|
74552
|
+
const symbol = payload.symbol.toUpperCase();
|
|
74553
|
+
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74554
|
+
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74555
|
+
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
74556
|
+
const [spot_orders, margin_orders, spot_balances, isolated_margin_position] = await Promise.all([
|
|
74557
|
+
this.getSpotOpenOrders(symbol),
|
|
74558
|
+
this.getMarginOpenOrders(symbol, true),
|
|
74559
|
+
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
74560
|
+
this.getIsolatedMarginPosition(symbol)
|
|
74561
|
+
]);
|
|
74562
|
+
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
74563
|
+
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
74564
|
+
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
74565
|
+
const isolated_asset = isolated_margin_position?.assets?.[0];
|
|
74566
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || "0");
|
|
74567
|
+
return {
|
|
74568
|
+
symbol,
|
|
74569
|
+
margin_type: payload.margin_type,
|
|
74570
|
+
quote_asset: quoteAsset,
|
|
74571
|
+
base_asset: baseAsset,
|
|
74572
|
+
borrowed_quote_amount,
|
|
74573
|
+
spot_quote_free: parseFloat(spot_quote_balance?.free?.toString?.() || "0"),
|
|
74574
|
+
spot_base_free: parseFloat(spot_base_balance?.free?.toString?.() || "0"),
|
|
74575
|
+
spot_orders,
|
|
74576
|
+
margin_orders,
|
|
74577
|
+
current_price,
|
|
74578
|
+
spot_balances,
|
|
74579
|
+
isolated_margin_position
|
|
74580
|
+
};
|
|
74581
|
+
}
|
|
74582
|
+
async previewSpotMarginHedge(payload) {
|
|
74583
|
+
const snapshot = payload.snapshot ? {
|
|
74584
|
+
...payload.snapshot,
|
|
74585
|
+
current_price: payload.snapshot.current_price ?? await this.getSpotCurrentPrice(payload.symbol)
|
|
74586
|
+
} : await this.getSpotMarginHedgeSnapshot({
|
|
74587
|
+
symbol: payload.symbol,
|
|
74588
|
+
margin_type: payload.margin_type
|
|
74589
|
+
});
|
|
74590
|
+
const shouldInferLongOrders = !payload.long_orders || payload.long_orders.length === 0;
|
|
74591
|
+
const shouldInferShortOrders = !payload.short_orders || payload.short_orders.length === 0;
|
|
74592
|
+
const inferred_input = shouldInferLongOrders || shouldInferShortOrders ? inferSpotMarginHedgeInputFromLiveOrders({
|
|
74593
|
+
symbol: payload.symbol,
|
|
74594
|
+
margin_type: payload.margin_type,
|
|
74595
|
+
spot_orders: snapshot.spot_orders,
|
|
74596
|
+
margin_orders: snapshot.margin_orders
|
|
74597
|
+
}) : undefined;
|
|
74598
|
+
const long_orders = shouldInferLongOrders ? inferred_input?.long_orders || [] : payload.long_orders || [];
|
|
74599
|
+
const short_orders = shouldInferShortOrders ? inferred_input?.short_orders || [] : payload.short_orders || [];
|
|
74600
|
+
const borrow_amount = payload.borrow_amount ?? (long_orders.length > 0 ? sumNotional(long_orders) : inferred_input?.borrow_amount ?? 0);
|
|
74601
|
+
const requested_input = {
|
|
74602
|
+
borrow_amount,
|
|
74603
|
+
symbol: payload.symbol,
|
|
74604
|
+
margin_type: payload.margin_type,
|
|
74605
|
+
long_orders,
|
|
74606
|
+
short_orders,
|
|
74607
|
+
current_price: snapshot.current_price,
|
|
74608
|
+
max_spot_buy_orders: payload.max_spot_buy_orders,
|
|
74609
|
+
placement_overrides: inferred_input?.placement_overrides
|
|
74610
|
+
};
|
|
74611
|
+
const plan = buildSpotMarginHedgePlan({
|
|
74612
|
+
...requested_input,
|
|
74613
|
+
snapshot
|
|
74614
|
+
});
|
|
74615
|
+
const comparison = compareSpotMarginHedgeOrders({
|
|
74616
|
+
plan,
|
|
74617
|
+
spot_orders: snapshot.spot_orders,
|
|
74618
|
+
margin_orders: snapshot.margin_orders
|
|
74619
|
+
});
|
|
74620
|
+
return {
|
|
74621
|
+
snapshot,
|
|
74622
|
+
inferred_input,
|
|
74623
|
+
requested_input,
|
|
74624
|
+
plan,
|
|
74625
|
+
comparison
|
|
74626
|
+
};
|
|
74627
|
+
}
|
|
74628
|
+
async placeSpotMarginHedge(payload) {
|
|
74629
|
+
const preview = await this.previewSpotMarginHedge(payload);
|
|
74630
|
+
if (!payload.place) {
|
|
74631
|
+
return preview;
|
|
74632
|
+
}
|
|
74633
|
+
if (!this.main_client) {
|
|
74634
|
+
throw new Error("Main client not available for spot and margin trading");
|
|
74635
|
+
}
|
|
74636
|
+
if (payload.margin_type !== "isolated") {
|
|
74637
|
+
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
74638
|
+
}
|
|
74639
|
+
const symbol = payload.symbol.toUpperCase();
|
|
74640
|
+
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74641
|
+
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74642
|
+
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
74643
|
+
const requested_input = preview.requested_input;
|
|
74644
|
+
if (preview.snapshot.spot_orders.length > 0) {
|
|
74645
|
+
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
74646
|
+
orderId: order.orderId || order.order_id || order.id,
|
|
74647
|
+
origClientOrderId: order.origClientOrderId
|
|
74648
|
+
})));
|
|
74649
|
+
}
|
|
74650
|
+
if (preview.snapshot.margin_orders.length > 0) {
|
|
74651
|
+
await this.cancelMarginOrders(symbol, true, preview.snapshot.margin_orders.map((order) => ({
|
|
74652
|
+
orderId: order.orderId || order.order_id || order.id,
|
|
74653
|
+
origClientOrderId: order.origClientOrderId
|
|
74654
|
+
})));
|
|
74655
|
+
}
|
|
74656
|
+
const refreshed_snapshot = await this.getSpotMarginHedgeSnapshot({
|
|
74657
|
+
symbol,
|
|
74658
|
+
margin_type: payload.margin_type
|
|
74659
|
+
});
|
|
74660
|
+
const symbol_formats = await this.getSpotSymbolFormats(symbol);
|
|
74661
|
+
const execution_plan = buildSpotMarginHedgePlan({
|
|
74662
|
+
borrow_amount: requested_input.borrow_amount,
|
|
74663
|
+
symbol: requested_input.symbol,
|
|
74664
|
+
margin_type: requested_input.margin_type,
|
|
74665
|
+
long_orders: requested_input.long_orders,
|
|
74666
|
+
short_orders: requested_input.short_orders,
|
|
74667
|
+
current_price: refreshed_snapshot.current_price,
|
|
74668
|
+
max_spot_buy_orders: requested_input.max_spot_buy_orders,
|
|
74669
|
+
snapshot: refreshed_snapshot
|
|
74670
|
+
});
|
|
74671
|
+
let borrow_result = null;
|
|
74672
|
+
if (execution_plan.borrow_delta > 0) {
|
|
74673
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay({
|
|
74674
|
+
asset: quote_asset,
|
|
74675
|
+
amount: execution_plan.borrow_delta,
|
|
74676
|
+
isIsolated: "TRUE",
|
|
74677
|
+
symbol,
|
|
74678
|
+
type: "BORROW"
|
|
74679
|
+
});
|
|
74680
|
+
}
|
|
74681
|
+
let quote_transfer_result = null;
|
|
74682
|
+
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
74683
|
+
quote_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
|
|
74684
|
+
asset: quote_asset,
|
|
74685
|
+
amount: execution_plan.transfer_to_spot_amount,
|
|
74686
|
+
symbol,
|
|
74687
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74688
|
+
transTo: "SPOT"
|
|
74689
|
+
});
|
|
74690
|
+
}
|
|
74691
|
+
let base_transfer_result = null;
|
|
74692
|
+
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
74693
|
+
base_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
|
|
74694
|
+
asset: base_asset,
|
|
74695
|
+
amount: execution_plan.transfer_base_to_spot_amount,
|
|
74696
|
+
symbol,
|
|
74697
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74698
|
+
transTo: "SPOT"
|
|
74699
|
+
});
|
|
74700
|
+
}
|
|
74701
|
+
const spot_result = execution_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
74702
|
+
symbol,
|
|
74703
|
+
orders: execution_plan.orders_to_create.spot,
|
|
74704
|
+
price_places: symbol_formats.price_places,
|
|
74705
|
+
decimal_places: symbol_formats.decimal_places
|
|
74706
|
+
}) : [];
|
|
74707
|
+
const margin_result = execution_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
74708
|
+
symbol,
|
|
74709
|
+
orders: execution_plan.orders_to_create.margin,
|
|
74710
|
+
isIsolated: true,
|
|
74711
|
+
price_places: symbol_formats.price_places,
|
|
74712
|
+
decimal_places: symbol_formats.decimal_places
|
|
74713
|
+
}) : [];
|
|
74714
|
+
return {
|
|
74715
|
+
...preview,
|
|
74716
|
+
execution_plan,
|
|
74717
|
+
refreshed_snapshot,
|
|
74718
|
+
execution: {
|
|
74719
|
+
borrow: borrow_result ? {
|
|
74720
|
+
asset: quote_asset,
|
|
74721
|
+
amount: execution_plan.borrow_delta,
|
|
74722
|
+
response: borrow_result
|
|
74723
|
+
} : null,
|
|
74724
|
+
quote_transfer: quote_transfer_result ? {
|
|
74725
|
+
asset: quote_asset,
|
|
74726
|
+
amount: execution_plan.transfer_to_spot_amount,
|
|
74727
|
+
response: quote_transfer_result
|
|
74728
|
+
} : null,
|
|
74729
|
+
base_transfer: base_transfer_result ? {
|
|
74730
|
+
asset: base_asset,
|
|
74731
|
+
amount: execution_plan.transfer_base_to_spot_amount,
|
|
74732
|
+
response: base_transfer_result
|
|
74733
|
+
} : null,
|
|
74734
|
+
spot_orders: spot_result,
|
|
74735
|
+
margin_orders: margin_result
|
|
74736
|
+
}
|
|
74737
|
+
};
|
|
74738
|
+
}
|
|
73964
74739
|
async getLastUserTrade(payload) {
|
|
73965
74740
|
return await getLastUserTrade(this.client, payload.symbol);
|
|
73966
74741
|
}
|