@gbozee/ultimate 0.0.2-next.74 → 0.0.2-next.75
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 +113 -43
- package/dist/index.js +113 -43
- package/dist/mcp-server.cjs +113 -43
- package/dist/mcp-server.js +113 -43
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -72788,7 +72788,7 @@ function buildSpotMarginHedgePlan(options) {
|
|
|
72788
72788
|
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
72789
72789
|
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
72790
72790
|
const spot_long_notional = sumNotional(spot_long_orders);
|
|
72791
|
-
const transfer_to_spot_amount = roundNumber2(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
72791
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, options.margin_type === "cross" ? options.borrow_amount : spot_long_notional - options.snapshot.spot_quote_free));
|
|
72792
72792
|
const short_quantity = sumQuantity(short_orders);
|
|
72793
72793
|
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
72794
72794
|
return {
|
|
@@ -73292,17 +73292,17 @@ async function createMarginLimitOrdersParallel(client, symbol, priceFormat, quan
|
|
|
73292
73292
|
return Object.fromEntries(Object.entries(v).filter(([, value2]) => value2 !== undefined));
|
|
73293
73293
|
};
|
|
73294
73294
|
const newOrders = orders.map(createMarginOrder);
|
|
73295
|
-
const
|
|
73296
|
-
const
|
|
73295
|
+
const results = [];
|
|
73296
|
+
for (const orderPayload of newOrders) {
|
|
73297
73297
|
try {
|
|
73298
73298
|
const result = await client.marginAccountNewOrder(orderPayload);
|
|
73299
73299
|
console.log("Margin order result:", result);
|
|
73300
|
-
|
|
73300
|
+
results.push(result);
|
|
73301
73301
|
} catch (error) {
|
|
73302
73302
|
console.error("Error processing margin order:", error);
|
|
73303
73303
|
throw error;
|
|
73304
73304
|
}
|
|
73305
|
-
}
|
|
73305
|
+
}
|
|
73306
73306
|
return results;
|
|
73307
73307
|
}
|
|
73308
73308
|
async function getIsolatedMarginAccountInfo(client, symbol) {
|
|
@@ -74648,24 +74648,25 @@ class BinanceExchange extends BaseExchange {
|
|
|
74648
74648
|
if (!this.main_client) {
|
|
74649
74649
|
throw new Error("Main client not available for spot and margin trading");
|
|
74650
74650
|
}
|
|
74651
|
-
if (payload.margin_type !== "isolated") {
|
|
74652
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
74653
|
-
}
|
|
74654
74651
|
const symbol = payload.symbol.toUpperCase();
|
|
74655
74652
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74656
74653
|
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74657
74654
|
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
74658
|
-
const
|
|
74655
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74656
|
+
const [spot_orders, margin_orders, spot_balances, margin_account] = await Promise.all([
|
|
74659
74657
|
this.getSpotOpenOrders(symbol),
|
|
74660
|
-
this.getMarginOpenOrders(symbol,
|
|
74658
|
+
this.getMarginOpenOrders(symbol, is_isolated),
|
|
74661
74659
|
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
74662
|
-
this.getIsolatedMarginPosition(symbol)
|
|
74660
|
+
is_isolated ? this.getIsolatedMarginPosition(symbol) : this.getCrossMarginAccount()
|
|
74663
74661
|
]);
|
|
74664
74662
|
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
74665
74663
|
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
74666
74664
|
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
74667
|
-
const
|
|
74668
|
-
const
|
|
74665
|
+
const isolated_margin_account = is_isolated ? margin_account : undefined;
|
|
74666
|
+
const cross_margin_account = !is_isolated ? margin_account : undefined;
|
|
74667
|
+
const isolated_asset = isolated_margin_account?.assets?.[0];
|
|
74668
|
+
const cross_quote_asset = !is_isolated ? cross_margin_account?.userAssets?.find((asset) => asset.asset?.toUpperCase?.() === quoteAsset) : undefined;
|
|
74669
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || cross_quote_asset?.borrowed?.toString?.() || "0");
|
|
74669
74670
|
return {
|
|
74670
74671
|
symbol,
|
|
74671
74672
|
margin_type: payload.margin_type,
|
|
@@ -74678,7 +74679,8 @@ class BinanceExchange extends BaseExchange {
|
|
|
74678
74679
|
margin_orders,
|
|
74679
74680
|
current_price,
|
|
74680
74681
|
spot_balances,
|
|
74681
|
-
isolated_margin_position
|
|
74682
|
+
isolated_margin_position: isolated_margin_account,
|
|
74683
|
+
cross_margin_account
|
|
74682
74684
|
};
|
|
74683
74685
|
}
|
|
74684
74686
|
async previewSpotMarginHedge(payload) {
|
|
@@ -74735,14 +74737,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74735
74737
|
if (!this.main_client) {
|
|
74736
74738
|
throw new Error("Main client not available for spot and margin trading");
|
|
74737
74739
|
}
|
|
74738
|
-
if (payload.margin_type !== "isolated") {
|
|
74739
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
74740
|
-
}
|
|
74741
74740
|
const symbol = payload.symbol.toUpperCase();
|
|
74742
74741
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74743
74742
|
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74744
74743
|
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
74745
74744
|
const requested_input = preview.requested_input;
|
|
74745
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74746
74746
|
if (preview.snapshot.spot_orders.length > 0) {
|
|
74747
74747
|
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
74748
74748
|
orderId: order.orderId || order.order_id || order.id,
|
|
@@ -74750,7 +74750,7 @@ class BinanceExchange extends BaseExchange {
|
|
|
74750
74750
|
})));
|
|
74751
74751
|
}
|
|
74752
74752
|
if (preview.snapshot.margin_orders.length > 0) {
|
|
74753
|
-
await this.cancelMarginOrders(symbol,
|
|
74753
|
+
await this.cancelMarginOrders(symbol, is_isolated, preview.snapshot.margin_orders.map((order) => ({
|
|
74754
74754
|
orderId: order.orderId || order.order_id || order.id,
|
|
74755
74755
|
origClientOrderId: order.origClientOrderId
|
|
74756
74756
|
})));
|
|
@@ -74772,50 +74772,120 @@ class BinanceExchange extends BaseExchange {
|
|
|
74772
74772
|
});
|
|
74773
74773
|
let borrow_result = null;
|
|
74774
74774
|
if (execution_plan.borrow_delta > 0) {
|
|
74775
|
-
|
|
74775
|
+
const borrow_payload = {
|
|
74776
74776
|
asset: quote_asset,
|
|
74777
74777
|
amount: execution_plan.borrow_delta,
|
|
74778
|
-
isIsolated: "TRUE",
|
|
74779
|
-
symbol,
|
|
74780
74778
|
type: "BORROW"
|
|
74779
|
+
};
|
|
74780
|
+
if (is_isolated) {
|
|
74781
|
+
borrow_payload.isIsolated = "TRUE";
|
|
74782
|
+
borrow_payload.symbol = symbol;
|
|
74783
|
+
}
|
|
74784
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay(borrow_payload);
|
|
74785
|
+
}
|
|
74786
|
+
async function getCrossTransferAmount(asset, amount) {
|
|
74787
|
+
if (is_isolated) {
|
|
74788
|
+
return amount;
|
|
74789
|
+
}
|
|
74790
|
+
if (!this.main_client || typeof this.main_client.queryMaxTransferOutAmount !== "function") {
|
|
74791
|
+
return amount;
|
|
74792
|
+
}
|
|
74793
|
+
const response = await this.main_client.queryMaxTransferOutAmount({
|
|
74794
|
+
asset
|
|
74781
74795
|
});
|
|
74796
|
+
const allowed_amount = parseFloat(response?.amount?.toString?.() || "0");
|
|
74797
|
+
if (!Number.isFinite(allowed_amount)) {
|
|
74798
|
+
return amount;
|
|
74799
|
+
}
|
|
74800
|
+
return Math.max(0, Math.min(amount, allowed_amount));
|
|
74782
74801
|
}
|
|
74783
74802
|
let quote_transfer_result = null;
|
|
74803
|
+
let quote_transfer_amount = execution_plan.transfer_to_spot_amount;
|
|
74784
74804
|
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
74785
|
-
|
|
74786
|
-
|
|
74787
|
-
|
|
74788
|
-
|
|
74789
|
-
|
|
74790
|
-
|
|
74791
|
-
|
|
74805
|
+
quote_transfer_amount = await getCrossTransferAmount.call(this, quote_asset, execution_plan.transfer_to_spot_amount);
|
|
74806
|
+
if (quote_transfer_amount > 0) {
|
|
74807
|
+
quote_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74808
|
+
asset: quote_asset,
|
|
74809
|
+
amount: quote_transfer_amount,
|
|
74810
|
+
symbol,
|
|
74811
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74812
|
+
transTo: "SPOT"
|
|
74813
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74814
|
+
type: "MARGIN_MAIN",
|
|
74815
|
+
asset: quote_asset,
|
|
74816
|
+
amount: quote_transfer_amount
|
|
74817
|
+
});
|
|
74818
|
+
}
|
|
74792
74819
|
}
|
|
74793
74820
|
let base_transfer_result = null;
|
|
74821
|
+
let base_transfer_amount = execution_plan.transfer_base_to_spot_amount;
|
|
74794
74822
|
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
74795
|
-
|
|
74796
|
-
|
|
74797
|
-
|
|
74798
|
-
|
|
74799
|
-
|
|
74800
|
-
|
|
74801
|
-
|
|
74823
|
+
base_transfer_amount = await getCrossTransferAmount.call(this, base_asset, execution_plan.transfer_base_to_spot_amount);
|
|
74824
|
+
if (base_transfer_amount > 0) {
|
|
74825
|
+
base_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74826
|
+
asset: base_asset,
|
|
74827
|
+
amount: base_transfer_amount,
|
|
74828
|
+
symbol,
|
|
74829
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74830
|
+
transTo: "SPOT"
|
|
74831
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74832
|
+
type: "MARGIN_MAIN",
|
|
74833
|
+
asset: base_asset,
|
|
74834
|
+
amount: base_transfer_amount
|
|
74835
|
+
});
|
|
74836
|
+
}
|
|
74837
|
+
}
|
|
74838
|
+
const spot_quote_available = refreshed_snapshot.spot_quote_free + quote_transfer_amount;
|
|
74839
|
+
const requested_max_spot_buy_orders = requested_input.max_spot_buy_orders ?? 5;
|
|
74840
|
+
const sorted_long_orders = [...requested_input.long_orders].sort((a, b) => a.price - b.price);
|
|
74841
|
+
let affordable_spot_buy_orders = 0;
|
|
74842
|
+
let affordable_spot_buy_notional = 0;
|
|
74843
|
+
for (const order of sorted_long_orders.slice(0, requested_max_spot_buy_orders)) {
|
|
74844
|
+
const next_notional = affordable_spot_buy_notional + order.price * order.quantity;
|
|
74845
|
+
if (next_notional <= spot_quote_available + 0.00000001) {
|
|
74846
|
+
affordable_spot_buy_orders += 1;
|
|
74847
|
+
affordable_spot_buy_notional = next_notional;
|
|
74848
|
+
} else {
|
|
74849
|
+
break;
|
|
74850
|
+
}
|
|
74802
74851
|
}
|
|
74803
|
-
const
|
|
74852
|
+
const placement_plan = buildSpotMarginHedgePlan({
|
|
74853
|
+
borrow_amount: requested_input.borrow_amount,
|
|
74854
|
+
symbol: requested_input.symbol,
|
|
74855
|
+
margin_type: requested_input.margin_type,
|
|
74856
|
+
long_orders: requested_input.long_orders,
|
|
74857
|
+
short_orders: requested_input.short_orders,
|
|
74858
|
+
current_price: refreshed_snapshot.current_price,
|
|
74859
|
+
max_spot_buy_orders: affordable_spot_buy_orders,
|
|
74860
|
+
snapshot: {
|
|
74861
|
+
...refreshed_snapshot,
|
|
74862
|
+
borrowed_quote_amount: refreshed_snapshot.borrowed_quote_amount + execution_plan.borrow_delta,
|
|
74863
|
+
spot_quote_free: spot_quote_available,
|
|
74864
|
+
spot_base_free: refreshed_snapshot.spot_base_free + base_transfer_amount
|
|
74865
|
+
}
|
|
74866
|
+
});
|
|
74867
|
+
const spot_result = placement_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
74804
74868
|
symbol,
|
|
74805
|
-
orders:
|
|
74869
|
+
orders: placement_plan.orders_to_create.spot,
|
|
74806
74870
|
price_places: symbol_formats.price_places,
|
|
74807
74871
|
decimal_places: symbol_formats.decimal_places
|
|
74808
74872
|
}) : [];
|
|
74809
|
-
const margin_result =
|
|
74873
|
+
const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
74810
74874
|
symbol,
|
|
74811
|
-
orders:
|
|
74812
|
-
isIsolated:
|
|
74875
|
+
orders: placement_plan.orders_to_create.margin,
|
|
74876
|
+
isIsolated: is_isolated,
|
|
74813
74877
|
price_places: symbol_formats.price_places,
|
|
74814
74878
|
decimal_places: symbol_formats.decimal_places
|
|
74815
74879
|
}) : [];
|
|
74816
74880
|
return {
|
|
74817
74881
|
...preview,
|
|
74818
|
-
execution_plan
|
|
74882
|
+
execution_plan: {
|
|
74883
|
+
...placement_plan,
|
|
74884
|
+
borrow_amount: execution_plan.borrow_amount,
|
|
74885
|
+
borrow_delta: execution_plan.borrow_delta,
|
|
74886
|
+
transfer_to_spot_amount: quote_transfer_amount,
|
|
74887
|
+
transfer_base_to_spot_amount: base_transfer_amount
|
|
74888
|
+
},
|
|
74819
74889
|
refreshed_snapshot,
|
|
74820
74890
|
execution: {
|
|
74821
74891
|
borrow: borrow_result ? {
|
|
@@ -74825,12 +74895,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74825
74895
|
} : null,
|
|
74826
74896
|
quote_transfer: quote_transfer_result ? {
|
|
74827
74897
|
asset: quote_asset,
|
|
74828
|
-
amount:
|
|
74898
|
+
amount: quote_transfer_amount,
|
|
74829
74899
|
response: quote_transfer_result
|
|
74830
74900
|
} : null,
|
|
74831
74901
|
base_transfer: base_transfer_result ? {
|
|
74832
74902
|
asset: base_asset,
|
|
74833
|
-
amount:
|
|
74903
|
+
amount: base_transfer_amount,
|
|
74834
74904
|
response: base_transfer_result
|
|
74835
74905
|
} : null,
|
|
74836
74906
|
spot_orders: spot_result,
|
package/dist/index.js
CHANGED
|
@@ -72686,7 +72686,7 @@ function buildSpotMarginHedgePlan(options) {
|
|
|
72686
72686
|
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
72687
72687
|
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
72688
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));
|
|
72689
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, options.margin_type === "cross" ? options.borrow_amount : spot_long_notional - options.snapshot.spot_quote_free));
|
|
72690
72690
|
const short_quantity = sumQuantity(short_orders);
|
|
72691
72691
|
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
72692
72692
|
return {
|
|
@@ -73190,17 +73190,17 @@ async function createMarginLimitOrdersParallel(client, symbol, priceFormat, quan
|
|
|
73190
73190
|
return Object.fromEntries(Object.entries(v).filter(([, value2]) => value2 !== undefined));
|
|
73191
73191
|
};
|
|
73192
73192
|
const newOrders = orders.map(createMarginOrder);
|
|
73193
|
-
const
|
|
73194
|
-
const
|
|
73193
|
+
const results = [];
|
|
73194
|
+
for (const orderPayload of newOrders) {
|
|
73195
73195
|
try {
|
|
73196
73196
|
const result = await client.marginAccountNewOrder(orderPayload);
|
|
73197
73197
|
console.log("Margin order result:", result);
|
|
73198
|
-
|
|
73198
|
+
results.push(result);
|
|
73199
73199
|
} catch (error) {
|
|
73200
73200
|
console.error("Error processing margin order:", error);
|
|
73201
73201
|
throw error;
|
|
73202
73202
|
}
|
|
73203
|
-
}
|
|
73203
|
+
}
|
|
73204
73204
|
return results;
|
|
73205
73205
|
}
|
|
73206
73206
|
async function getIsolatedMarginAccountInfo(client, symbol) {
|
|
@@ -74546,24 +74546,25 @@ class BinanceExchange extends BaseExchange {
|
|
|
74546
74546
|
if (!this.main_client) {
|
|
74547
74547
|
throw new Error("Main client not available for spot and margin trading");
|
|
74548
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
74549
|
const symbol = payload.symbol.toUpperCase();
|
|
74553
74550
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74554
74551
|
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74555
74552
|
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
74556
|
-
const
|
|
74553
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74554
|
+
const [spot_orders, margin_orders, spot_balances, margin_account] = await Promise.all([
|
|
74557
74555
|
this.getSpotOpenOrders(symbol),
|
|
74558
|
-
this.getMarginOpenOrders(symbol,
|
|
74556
|
+
this.getMarginOpenOrders(symbol, is_isolated),
|
|
74559
74557
|
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
74560
|
-
this.getIsolatedMarginPosition(symbol)
|
|
74558
|
+
is_isolated ? this.getIsolatedMarginPosition(symbol) : this.getCrossMarginAccount()
|
|
74561
74559
|
]);
|
|
74562
74560
|
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
74563
74561
|
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
74564
74562
|
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
74565
|
-
const
|
|
74566
|
-
const
|
|
74563
|
+
const isolated_margin_account = is_isolated ? margin_account : undefined;
|
|
74564
|
+
const cross_margin_account = !is_isolated ? margin_account : undefined;
|
|
74565
|
+
const isolated_asset = isolated_margin_account?.assets?.[0];
|
|
74566
|
+
const cross_quote_asset = !is_isolated ? cross_margin_account?.userAssets?.find((asset) => asset.asset?.toUpperCase?.() === quoteAsset) : undefined;
|
|
74567
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || cross_quote_asset?.borrowed?.toString?.() || "0");
|
|
74567
74568
|
return {
|
|
74568
74569
|
symbol,
|
|
74569
74570
|
margin_type: payload.margin_type,
|
|
@@ -74576,7 +74577,8 @@ class BinanceExchange extends BaseExchange {
|
|
|
74576
74577
|
margin_orders,
|
|
74577
74578
|
current_price,
|
|
74578
74579
|
spot_balances,
|
|
74579
|
-
isolated_margin_position
|
|
74580
|
+
isolated_margin_position: isolated_margin_account,
|
|
74581
|
+
cross_margin_account
|
|
74580
74582
|
};
|
|
74581
74583
|
}
|
|
74582
74584
|
async previewSpotMarginHedge(payload) {
|
|
@@ -74633,14 +74635,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74633
74635
|
if (!this.main_client) {
|
|
74634
74636
|
throw new Error("Main client not available for spot and margin trading");
|
|
74635
74637
|
}
|
|
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
74638
|
const symbol = payload.symbol.toUpperCase();
|
|
74640
74639
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74641
74640
|
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74642
74641
|
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
74643
74642
|
const requested_input = preview.requested_input;
|
|
74643
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74644
74644
|
if (preview.snapshot.spot_orders.length > 0) {
|
|
74645
74645
|
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
74646
74646
|
orderId: order.orderId || order.order_id || order.id,
|
|
@@ -74648,7 +74648,7 @@ class BinanceExchange extends BaseExchange {
|
|
|
74648
74648
|
})));
|
|
74649
74649
|
}
|
|
74650
74650
|
if (preview.snapshot.margin_orders.length > 0) {
|
|
74651
|
-
await this.cancelMarginOrders(symbol,
|
|
74651
|
+
await this.cancelMarginOrders(symbol, is_isolated, preview.snapshot.margin_orders.map((order) => ({
|
|
74652
74652
|
orderId: order.orderId || order.order_id || order.id,
|
|
74653
74653
|
origClientOrderId: order.origClientOrderId
|
|
74654
74654
|
})));
|
|
@@ -74670,50 +74670,120 @@ class BinanceExchange extends BaseExchange {
|
|
|
74670
74670
|
});
|
|
74671
74671
|
let borrow_result = null;
|
|
74672
74672
|
if (execution_plan.borrow_delta > 0) {
|
|
74673
|
-
|
|
74673
|
+
const borrow_payload = {
|
|
74674
74674
|
asset: quote_asset,
|
|
74675
74675
|
amount: execution_plan.borrow_delta,
|
|
74676
|
-
isIsolated: "TRUE",
|
|
74677
|
-
symbol,
|
|
74678
74676
|
type: "BORROW"
|
|
74677
|
+
};
|
|
74678
|
+
if (is_isolated) {
|
|
74679
|
+
borrow_payload.isIsolated = "TRUE";
|
|
74680
|
+
borrow_payload.symbol = symbol;
|
|
74681
|
+
}
|
|
74682
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay(borrow_payload);
|
|
74683
|
+
}
|
|
74684
|
+
async function getCrossTransferAmount(asset, amount) {
|
|
74685
|
+
if (is_isolated) {
|
|
74686
|
+
return amount;
|
|
74687
|
+
}
|
|
74688
|
+
if (!this.main_client || typeof this.main_client.queryMaxTransferOutAmount !== "function") {
|
|
74689
|
+
return amount;
|
|
74690
|
+
}
|
|
74691
|
+
const response = await this.main_client.queryMaxTransferOutAmount({
|
|
74692
|
+
asset
|
|
74679
74693
|
});
|
|
74694
|
+
const allowed_amount = parseFloat(response?.amount?.toString?.() || "0");
|
|
74695
|
+
if (!Number.isFinite(allowed_amount)) {
|
|
74696
|
+
return amount;
|
|
74697
|
+
}
|
|
74698
|
+
return Math.max(0, Math.min(amount, allowed_amount));
|
|
74680
74699
|
}
|
|
74681
74700
|
let quote_transfer_result = null;
|
|
74701
|
+
let quote_transfer_amount = execution_plan.transfer_to_spot_amount;
|
|
74682
74702
|
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
74683
|
-
|
|
74684
|
-
|
|
74685
|
-
|
|
74686
|
-
|
|
74687
|
-
|
|
74688
|
-
|
|
74689
|
-
|
|
74703
|
+
quote_transfer_amount = await getCrossTransferAmount.call(this, quote_asset, execution_plan.transfer_to_spot_amount);
|
|
74704
|
+
if (quote_transfer_amount > 0) {
|
|
74705
|
+
quote_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74706
|
+
asset: quote_asset,
|
|
74707
|
+
amount: quote_transfer_amount,
|
|
74708
|
+
symbol,
|
|
74709
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74710
|
+
transTo: "SPOT"
|
|
74711
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74712
|
+
type: "MARGIN_MAIN",
|
|
74713
|
+
asset: quote_asset,
|
|
74714
|
+
amount: quote_transfer_amount
|
|
74715
|
+
});
|
|
74716
|
+
}
|
|
74690
74717
|
}
|
|
74691
74718
|
let base_transfer_result = null;
|
|
74719
|
+
let base_transfer_amount = execution_plan.transfer_base_to_spot_amount;
|
|
74692
74720
|
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
74693
|
-
|
|
74694
|
-
|
|
74695
|
-
|
|
74696
|
-
|
|
74697
|
-
|
|
74698
|
-
|
|
74699
|
-
|
|
74721
|
+
base_transfer_amount = await getCrossTransferAmount.call(this, base_asset, execution_plan.transfer_base_to_spot_amount);
|
|
74722
|
+
if (base_transfer_amount > 0) {
|
|
74723
|
+
base_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74724
|
+
asset: base_asset,
|
|
74725
|
+
amount: base_transfer_amount,
|
|
74726
|
+
symbol,
|
|
74727
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74728
|
+
transTo: "SPOT"
|
|
74729
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74730
|
+
type: "MARGIN_MAIN",
|
|
74731
|
+
asset: base_asset,
|
|
74732
|
+
amount: base_transfer_amount
|
|
74733
|
+
});
|
|
74734
|
+
}
|
|
74735
|
+
}
|
|
74736
|
+
const spot_quote_available = refreshed_snapshot.spot_quote_free + quote_transfer_amount;
|
|
74737
|
+
const requested_max_spot_buy_orders = requested_input.max_spot_buy_orders ?? 5;
|
|
74738
|
+
const sorted_long_orders = [...requested_input.long_orders].sort((a, b) => a.price - b.price);
|
|
74739
|
+
let affordable_spot_buy_orders = 0;
|
|
74740
|
+
let affordable_spot_buy_notional = 0;
|
|
74741
|
+
for (const order of sorted_long_orders.slice(0, requested_max_spot_buy_orders)) {
|
|
74742
|
+
const next_notional = affordable_spot_buy_notional + order.price * order.quantity;
|
|
74743
|
+
if (next_notional <= spot_quote_available + 0.00000001) {
|
|
74744
|
+
affordable_spot_buy_orders += 1;
|
|
74745
|
+
affordable_spot_buy_notional = next_notional;
|
|
74746
|
+
} else {
|
|
74747
|
+
break;
|
|
74748
|
+
}
|
|
74700
74749
|
}
|
|
74701
|
-
const
|
|
74750
|
+
const placement_plan = buildSpotMarginHedgePlan({
|
|
74751
|
+
borrow_amount: requested_input.borrow_amount,
|
|
74752
|
+
symbol: requested_input.symbol,
|
|
74753
|
+
margin_type: requested_input.margin_type,
|
|
74754
|
+
long_orders: requested_input.long_orders,
|
|
74755
|
+
short_orders: requested_input.short_orders,
|
|
74756
|
+
current_price: refreshed_snapshot.current_price,
|
|
74757
|
+
max_spot_buy_orders: affordable_spot_buy_orders,
|
|
74758
|
+
snapshot: {
|
|
74759
|
+
...refreshed_snapshot,
|
|
74760
|
+
borrowed_quote_amount: refreshed_snapshot.borrowed_quote_amount + execution_plan.borrow_delta,
|
|
74761
|
+
spot_quote_free: spot_quote_available,
|
|
74762
|
+
spot_base_free: refreshed_snapshot.spot_base_free + base_transfer_amount
|
|
74763
|
+
}
|
|
74764
|
+
});
|
|
74765
|
+
const spot_result = placement_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
74702
74766
|
symbol,
|
|
74703
|
-
orders:
|
|
74767
|
+
orders: placement_plan.orders_to_create.spot,
|
|
74704
74768
|
price_places: symbol_formats.price_places,
|
|
74705
74769
|
decimal_places: symbol_formats.decimal_places
|
|
74706
74770
|
}) : [];
|
|
74707
|
-
const margin_result =
|
|
74771
|
+
const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
74708
74772
|
symbol,
|
|
74709
|
-
orders:
|
|
74710
|
-
isIsolated:
|
|
74773
|
+
orders: placement_plan.orders_to_create.margin,
|
|
74774
|
+
isIsolated: is_isolated,
|
|
74711
74775
|
price_places: symbol_formats.price_places,
|
|
74712
74776
|
decimal_places: symbol_formats.decimal_places
|
|
74713
74777
|
}) : [];
|
|
74714
74778
|
return {
|
|
74715
74779
|
...preview,
|
|
74716
|
-
execution_plan
|
|
74780
|
+
execution_plan: {
|
|
74781
|
+
...placement_plan,
|
|
74782
|
+
borrow_amount: execution_plan.borrow_amount,
|
|
74783
|
+
borrow_delta: execution_plan.borrow_delta,
|
|
74784
|
+
transfer_to_spot_amount: quote_transfer_amount,
|
|
74785
|
+
transfer_base_to_spot_amount: base_transfer_amount
|
|
74786
|
+
},
|
|
74717
74787
|
refreshed_snapshot,
|
|
74718
74788
|
execution: {
|
|
74719
74789
|
borrow: borrow_result ? {
|
|
@@ -74723,12 +74793,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74723
74793
|
} : null,
|
|
74724
74794
|
quote_transfer: quote_transfer_result ? {
|
|
74725
74795
|
asset: quote_asset,
|
|
74726
|
-
amount:
|
|
74796
|
+
amount: quote_transfer_amount,
|
|
74727
74797
|
response: quote_transfer_result
|
|
74728
74798
|
} : null,
|
|
74729
74799
|
base_transfer: base_transfer_result ? {
|
|
74730
74800
|
asset: base_asset,
|
|
74731
|
-
amount:
|
|
74801
|
+
amount: base_transfer_amount,
|
|
74732
74802
|
response: base_transfer_result
|
|
74733
74803
|
} : null,
|
|
74734
74804
|
spot_orders: spot_result,
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -76510,7 +76510,7 @@ function buildSpotMarginHedgePlan(options) {
|
|
|
76510
76510
|
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
76511
76511
|
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
76512
76512
|
const spot_long_notional = sumNotional(spot_long_orders);
|
|
76513
|
-
const transfer_to_spot_amount = roundNumber2(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
76513
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, options.margin_type === "cross" ? options.borrow_amount : spot_long_notional - options.snapshot.spot_quote_free));
|
|
76514
76514
|
const short_quantity = sumQuantity(short_orders);
|
|
76515
76515
|
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
76516
76516
|
return {
|
|
@@ -77014,17 +77014,17 @@ async function createMarginLimitOrdersParallel(client, symbol, priceFormat, quan
|
|
|
77014
77014
|
return Object.fromEntries(Object.entries(v).filter(([, value2]) => value2 !== undefined));
|
|
77015
77015
|
};
|
|
77016
77016
|
const newOrders = orders.map(createMarginOrder);
|
|
77017
|
-
const
|
|
77018
|
-
const
|
|
77017
|
+
const results = [];
|
|
77018
|
+
for (const orderPayload of newOrders) {
|
|
77019
77019
|
try {
|
|
77020
77020
|
const result = await client.marginAccountNewOrder(orderPayload);
|
|
77021
77021
|
console.log("Margin order result:", result);
|
|
77022
|
-
|
|
77022
|
+
results.push(result);
|
|
77023
77023
|
} catch (error) {
|
|
77024
77024
|
console.error("Error processing margin order:", error);
|
|
77025
77025
|
throw error;
|
|
77026
77026
|
}
|
|
77027
|
-
}
|
|
77027
|
+
}
|
|
77028
77028
|
return results;
|
|
77029
77029
|
}
|
|
77030
77030
|
async function getIsolatedMarginAccountInfo(client, symbol) {
|
|
@@ -78370,24 +78370,25 @@ class BinanceExchange extends BaseExchange {
|
|
|
78370
78370
|
if (!this.main_client) {
|
|
78371
78371
|
throw new Error("Main client not available for spot and margin trading");
|
|
78372
78372
|
}
|
|
78373
|
-
if (payload.margin_type !== "isolated") {
|
|
78374
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
78375
|
-
}
|
|
78376
78373
|
const symbol = payload.symbol.toUpperCase();
|
|
78377
78374
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
78378
78375
|
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
78379
78376
|
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
78380
|
-
const
|
|
78377
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
78378
|
+
const [spot_orders, margin_orders, spot_balances, margin_account] = await Promise.all([
|
|
78381
78379
|
this.getSpotOpenOrders(symbol),
|
|
78382
|
-
this.getMarginOpenOrders(symbol,
|
|
78380
|
+
this.getMarginOpenOrders(symbol, is_isolated),
|
|
78383
78381
|
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
78384
|
-
this.getIsolatedMarginPosition(symbol)
|
|
78382
|
+
is_isolated ? this.getIsolatedMarginPosition(symbol) : this.getCrossMarginAccount()
|
|
78385
78383
|
]);
|
|
78386
78384
|
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
78387
78385
|
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
78388
78386
|
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
78389
|
-
const
|
|
78390
|
-
const
|
|
78387
|
+
const isolated_margin_account = is_isolated ? margin_account : undefined;
|
|
78388
|
+
const cross_margin_account = !is_isolated ? margin_account : undefined;
|
|
78389
|
+
const isolated_asset = isolated_margin_account?.assets?.[0];
|
|
78390
|
+
const cross_quote_asset = !is_isolated ? cross_margin_account?.userAssets?.find((asset) => asset.asset?.toUpperCase?.() === quoteAsset) : undefined;
|
|
78391
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || cross_quote_asset?.borrowed?.toString?.() || "0");
|
|
78391
78392
|
return {
|
|
78392
78393
|
symbol,
|
|
78393
78394
|
margin_type: payload.margin_type,
|
|
@@ -78400,7 +78401,8 @@ class BinanceExchange extends BaseExchange {
|
|
|
78400
78401
|
margin_orders,
|
|
78401
78402
|
current_price,
|
|
78402
78403
|
spot_balances,
|
|
78403
|
-
isolated_margin_position
|
|
78404
|
+
isolated_margin_position: isolated_margin_account,
|
|
78405
|
+
cross_margin_account
|
|
78404
78406
|
};
|
|
78405
78407
|
}
|
|
78406
78408
|
async previewSpotMarginHedge(payload) {
|
|
@@ -78457,14 +78459,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
78457
78459
|
if (!this.main_client) {
|
|
78458
78460
|
throw new Error("Main client not available for spot and margin trading");
|
|
78459
78461
|
}
|
|
78460
|
-
if (payload.margin_type !== "isolated") {
|
|
78461
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
78462
|
-
}
|
|
78463
78462
|
const symbol = payload.symbol.toUpperCase();
|
|
78464
78463
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
78465
78464
|
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
78466
78465
|
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
78467
78466
|
const requested_input = preview.requested_input;
|
|
78467
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
78468
78468
|
if (preview.snapshot.spot_orders.length > 0) {
|
|
78469
78469
|
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
78470
78470
|
orderId: order.orderId || order.order_id || order.id,
|
|
@@ -78472,7 +78472,7 @@ class BinanceExchange extends BaseExchange {
|
|
|
78472
78472
|
})));
|
|
78473
78473
|
}
|
|
78474
78474
|
if (preview.snapshot.margin_orders.length > 0) {
|
|
78475
|
-
await this.cancelMarginOrders(symbol,
|
|
78475
|
+
await this.cancelMarginOrders(symbol, is_isolated, preview.snapshot.margin_orders.map((order) => ({
|
|
78476
78476
|
orderId: order.orderId || order.order_id || order.id,
|
|
78477
78477
|
origClientOrderId: order.origClientOrderId
|
|
78478
78478
|
})));
|
|
@@ -78494,50 +78494,120 @@ class BinanceExchange extends BaseExchange {
|
|
|
78494
78494
|
});
|
|
78495
78495
|
let borrow_result = null;
|
|
78496
78496
|
if (execution_plan.borrow_delta > 0) {
|
|
78497
|
-
|
|
78497
|
+
const borrow_payload = {
|
|
78498
78498
|
asset: quote_asset,
|
|
78499
78499
|
amount: execution_plan.borrow_delta,
|
|
78500
|
-
isIsolated: "TRUE",
|
|
78501
|
-
symbol,
|
|
78502
78500
|
type: "BORROW"
|
|
78501
|
+
};
|
|
78502
|
+
if (is_isolated) {
|
|
78503
|
+
borrow_payload.isIsolated = "TRUE";
|
|
78504
|
+
borrow_payload.symbol = symbol;
|
|
78505
|
+
}
|
|
78506
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay(borrow_payload);
|
|
78507
|
+
}
|
|
78508
|
+
async function getCrossTransferAmount(asset, amount) {
|
|
78509
|
+
if (is_isolated) {
|
|
78510
|
+
return amount;
|
|
78511
|
+
}
|
|
78512
|
+
if (!this.main_client || typeof this.main_client.queryMaxTransferOutAmount !== "function") {
|
|
78513
|
+
return amount;
|
|
78514
|
+
}
|
|
78515
|
+
const response = await this.main_client.queryMaxTransferOutAmount({
|
|
78516
|
+
asset
|
|
78503
78517
|
});
|
|
78518
|
+
const allowed_amount = parseFloat(response?.amount?.toString?.() || "0");
|
|
78519
|
+
if (!Number.isFinite(allowed_amount)) {
|
|
78520
|
+
return amount;
|
|
78521
|
+
}
|
|
78522
|
+
return Math.max(0, Math.min(amount, allowed_amount));
|
|
78504
78523
|
}
|
|
78505
78524
|
let quote_transfer_result = null;
|
|
78525
|
+
let quote_transfer_amount = execution_plan.transfer_to_spot_amount;
|
|
78506
78526
|
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
78507
|
-
|
|
78508
|
-
|
|
78509
|
-
|
|
78510
|
-
|
|
78511
|
-
|
|
78512
|
-
|
|
78513
|
-
|
|
78527
|
+
quote_transfer_amount = await getCrossTransferAmount.call(this, quote_asset, execution_plan.transfer_to_spot_amount);
|
|
78528
|
+
if (quote_transfer_amount > 0) {
|
|
78529
|
+
quote_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
78530
|
+
asset: quote_asset,
|
|
78531
|
+
amount: quote_transfer_amount,
|
|
78532
|
+
symbol,
|
|
78533
|
+
transFrom: "ISOLATED_MARGIN",
|
|
78534
|
+
transTo: "SPOT"
|
|
78535
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
78536
|
+
type: "MARGIN_MAIN",
|
|
78537
|
+
asset: quote_asset,
|
|
78538
|
+
amount: quote_transfer_amount
|
|
78539
|
+
});
|
|
78540
|
+
}
|
|
78514
78541
|
}
|
|
78515
78542
|
let base_transfer_result = null;
|
|
78543
|
+
let base_transfer_amount = execution_plan.transfer_base_to_spot_amount;
|
|
78516
78544
|
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
78517
|
-
|
|
78518
|
-
|
|
78519
|
-
|
|
78520
|
-
|
|
78521
|
-
|
|
78522
|
-
|
|
78523
|
-
|
|
78545
|
+
base_transfer_amount = await getCrossTransferAmount.call(this, base_asset, execution_plan.transfer_base_to_spot_amount);
|
|
78546
|
+
if (base_transfer_amount > 0) {
|
|
78547
|
+
base_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
78548
|
+
asset: base_asset,
|
|
78549
|
+
amount: base_transfer_amount,
|
|
78550
|
+
symbol,
|
|
78551
|
+
transFrom: "ISOLATED_MARGIN",
|
|
78552
|
+
transTo: "SPOT"
|
|
78553
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
78554
|
+
type: "MARGIN_MAIN",
|
|
78555
|
+
asset: base_asset,
|
|
78556
|
+
amount: base_transfer_amount
|
|
78557
|
+
});
|
|
78558
|
+
}
|
|
78559
|
+
}
|
|
78560
|
+
const spot_quote_available = refreshed_snapshot.spot_quote_free + quote_transfer_amount;
|
|
78561
|
+
const requested_max_spot_buy_orders = requested_input.max_spot_buy_orders ?? 5;
|
|
78562
|
+
const sorted_long_orders = [...requested_input.long_orders].sort((a, b) => a.price - b.price);
|
|
78563
|
+
let affordable_spot_buy_orders = 0;
|
|
78564
|
+
let affordable_spot_buy_notional = 0;
|
|
78565
|
+
for (const order of sorted_long_orders.slice(0, requested_max_spot_buy_orders)) {
|
|
78566
|
+
const next_notional = affordable_spot_buy_notional + order.price * order.quantity;
|
|
78567
|
+
if (next_notional <= spot_quote_available + 0.00000001) {
|
|
78568
|
+
affordable_spot_buy_orders += 1;
|
|
78569
|
+
affordable_spot_buy_notional = next_notional;
|
|
78570
|
+
} else {
|
|
78571
|
+
break;
|
|
78572
|
+
}
|
|
78524
78573
|
}
|
|
78525
|
-
const
|
|
78574
|
+
const placement_plan = buildSpotMarginHedgePlan({
|
|
78575
|
+
borrow_amount: requested_input.borrow_amount,
|
|
78576
|
+
symbol: requested_input.symbol,
|
|
78577
|
+
margin_type: requested_input.margin_type,
|
|
78578
|
+
long_orders: requested_input.long_orders,
|
|
78579
|
+
short_orders: requested_input.short_orders,
|
|
78580
|
+
current_price: refreshed_snapshot.current_price,
|
|
78581
|
+
max_spot_buy_orders: affordable_spot_buy_orders,
|
|
78582
|
+
snapshot: {
|
|
78583
|
+
...refreshed_snapshot,
|
|
78584
|
+
borrowed_quote_amount: refreshed_snapshot.borrowed_quote_amount + execution_plan.borrow_delta,
|
|
78585
|
+
spot_quote_free: spot_quote_available,
|
|
78586
|
+
spot_base_free: refreshed_snapshot.spot_base_free + base_transfer_amount
|
|
78587
|
+
}
|
|
78588
|
+
});
|
|
78589
|
+
const spot_result = placement_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
78526
78590
|
symbol,
|
|
78527
|
-
orders:
|
|
78591
|
+
orders: placement_plan.orders_to_create.spot,
|
|
78528
78592
|
price_places: symbol_formats.price_places,
|
|
78529
78593
|
decimal_places: symbol_formats.decimal_places
|
|
78530
78594
|
}) : [];
|
|
78531
|
-
const margin_result =
|
|
78595
|
+
const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
78532
78596
|
symbol,
|
|
78533
|
-
orders:
|
|
78534
|
-
isIsolated:
|
|
78597
|
+
orders: placement_plan.orders_to_create.margin,
|
|
78598
|
+
isIsolated: is_isolated,
|
|
78535
78599
|
price_places: symbol_formats.price_places,
|
|
78536
78600
|
decimal_places: symbol_formats.decimal_places
|
|
78537
78601
|
}) : [];
|
|
78538
78602
|
return {
|
|
78539
78603
|
...preview,
|
|
78540
|
-
execution_plan
|
|
78604
|
+
execution_plan: {
|
|
78605
|
+
...placement_plan,
|
|
78606
|
+
borrow_amount: execution_plan.borrow_amount,
|
|
78607
|
+
borrow_delta: execution_plan.borrow_delta,
|
|
78608
|
+
transfer_to_spot_amount: quote_transfer_amount,
|
|
78609
|
+
transfer_base_to_spot_amount: base_transfer_amount
|
|
78610
|
+
},
|
|
78541
78611
|
refreshed_snapshot,
|
|
78542
78612
|
execution: {
|
|
78543
78613
|
borrow: borrow_result ? {
|
|
@@ -78547,12 +78617,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
78547
78617
|
} : null,
|
|
78548
78618
|
quote_transfer: quote_transfer_result ? {
|
|
78549
78619
|
asset: quote_asset,
|
|
78550
|
-
amount:
|
|
78620
|
+
amount: quote_transfer_amount,
|
|
78551
78621
|
response: quote_transfer_result
|
|
78552
78622
|
} : null,
|
|
78553
78623
|
base_transfer: base_transfer_result ? {
|
|
78554
78624
|
asset: base_asset,
|
|
78555
|
-
amount:
|
|
78625
|
+
amount: base_transfer_amount,
|
|
78556
78626
|
response: base_transfer_result
|
|
78557
78627
|
} : null,
|
|
78558
78628
|
spot_orders: spot_result,
|
package/dist/mcp-server.js
CHANGED
|
@@ -76469,7 +76469,7 @@ function buildSpotMarginHedgePlan(options) {
|
|
|
76469
76469
|
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
76470
76470
|
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
76471
76471
|
const spot_long_notional = sumNotional(spot_long_orders);
|
|
76472
|
-
const transfer_to_spot_amount = roundNumber2(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
76472
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, options.margin_type === "cross" ? options.borrow_amount : spot_long_notional - options.snapshot.spot_quote_free));
|
|
76473
76473
|
const short_quantity = sumQuantity(short_orders);
|
|
76474
76474
|
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
76475
76475
|
return {
|
|
@@ -76973,17 +76973,17 @@ async function createMarginLimitOrdersParallel(client, symbol, priceFormat, quan
|
|
|
76973
76973
|
return Object.fromEntries(Object.entries(v).filter(([, value2]) => value2 !== undefined));
|
|
76974
76974
|
};
|
|
76975
76975
|
const newOrders = orders.map(createMarginOrder);
|
|
76976
|
-
const
|
|
76977
|
-
const
|
|
76976
|
+
const results = [];
|
|
76977
|
+
for (const orderPayload of newOrders) {
|
|
76978
76978
|
try {
|
|
76979
76979
|
const result = await client.marginAccountNewOrder(orderPayload);
|
|
76980
76980
|
console.log("Margin order result:", result);
|
|
76981
|
-
|
|
76981
|
+
results.push(result);
|
|
76982
76982
|
} catch (error) {
|
|
76983
76983
|
console.error("Error processing margin order:", error);
|
|
76984
76984
|
throw error;
|
|
76985
76985
|
}
|
|
76986
|
-
}
|
|
76986
|
+
}
|
|
76987
76987
|
return results;
|
|
76988
76988
|
}
|
|
76989
76989
|
async function getIsolatedMarginAccountInfo(client, symbol) {
|
|
@@ -78329,24 +78329,25 @@ class BinanceExchange extends BaseExchange {
|
|
|
78329
78329
|
if (!this.main_client) {
|
|
78330
78330
|
throw new Error("Main client not available for spot and margin trading");
|
|
78331
78331
|
}
|
|
78332
|
-
if (payload.margin_type !== "isolated") {
|
|
78333
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
78334
|
-
}
|
|
78335
78332
|
const symbol = payload.symbol.toUpperCase();
|
|
78336
78333
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
78337
78334
|
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
78338
78335
|
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
78339
|
-
const
|
|
78336
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
78337
|
+
const [spot_orders, margin_orders, spot_balances, margin_account] = await Promise.all([
|
|
78340
78338
|
this.getSpotOpenOrders(symbol),
|
|
78341
|
-
this.getMarginOpenOrders(symbol,
|
|
78339
|
+
this.getMarginOpenOrders(symbol, is_isolated),
|
|
78342
78340
|
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
78343
|
-
this.getIsolatedMarginPosition(symbol)
|
|
78341
|
+
is_isolated ? this.getIsolatedMarginPosition(symbol) : this.getCrossMarginAccount()
|
|
78344
78342
|
]);
|
|
78345
78343
|
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
78346
78344
|
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
78347
78345
|
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
78348
|
-
const
|
|
78349
|
-
const
|
|
78346
|
+
const isolated_margin_account = is_isolated ? margin_account : undefined;
|
|
78347
|
+
const cross_margin_account = !is_isolated ? margin_account : undefined;
|
|
78348
|
+
const isolated_asset = isolated_margin_account?.assets?.[0];
|
|
78349
|
+
const cross_quote_asset = !is_isolated ? cross_margin_account?.userAssets?.find((asset) => asset.asset?.toUpperCase?.() === quoteAsset) : undefined;
|
|
78350
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || cross_quote_asset?.borrowed?.toString?.() || "0");
|
|
78350
78351
|
return {
|
|
78351
78352
|
symbol,
|
|
78352
78353
|
margin_type: payload.margin_type,
|
|
@@ -78359,7 +78360,8 @@ class BinanceExchange extends BaseExchange {
|
|
|
78359
78360
|
margin_orders,
|
|
78360
78361
|
current_price,
|
|
78361
78362
|
spot_balances,
|
|
78362
|
-
isolated_margin_position
|
|
78363
|
+
isolated_margin_position: isolated_margin_account,
|
|
78364
|
+
cross_margin_account
|
|
78363
78365
|
};
|
|
78364
78366
|
}
|
|
78365
78367
|
async previewSpotMarginHedge(payload) {
|
|
@@ -78416,14 +78418,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
78416
78418
|
if (!this.main_client) {
|
|
78417
78419
|
throw new Error("Main client not available for spot and margin trading");
|
|
78418
78420
|
}
|
|
78419
|
-
if (payload.margin_type !== "isolated") {
|
|
78420
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
78421
|
-
}
|
|
78422
78421
|
const symbol = payload.symbol.toUpperCase();
|
|
78423
78422
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
78424
78423
|
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
78425
78424
|
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
78426
78425
|
const requested_input = preview.requested_input;
|
|
78426
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
78427
78427
|
if (preview.snapshot.spot_orders.length > 0) {
|
|
78428
78428
|
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
78429
78429
|
orderId: order.orderId || order.order_id || order.id,
|
|
@@ -78431,7 +78431,7 @@ class BinanceExchange extends BaseExchange {
|
|
|
78431
78431
|
})));
|
|
78432
78432
|
}
|
|
78433
78433
|
if (preview.snapshot.margin_orders.length > 0) {
|
|
78434
|
-
await this.cancelMarginOrders(symbol,
|
|
78434
|
+
await this.cancelMarginOrders(symbol, is_isolated, preview.snapshot.margin_orders.map((order) => ({
|
|
78435
78435
|
orderId: order.orderId || order.order_id || order.id,
|
|
78436
78436
|
origClientOrderId: order.origClientOrderId
|
|
78437
78437
|
})));
|
|
@@ -78453,50 +78453,120 @@ class BinanceExchange extends BaseExchange {
|
|
|
78453
78453
|
});
|
|
78454
78454
|
let borrow_result = null;
|
|
78455
78455
|
if (execution_plan.borrow_delta > 0) {
|
|
78456
|
-
|
|
78456
|
+
const borrow_payload = {
|
|
78457
78457
|
asset: quote_asset,
|
|
78458
78458
|
amount: execution_plan.borrow_delta,
|
|
78459
|
-
isIsolated: "TRUE",
|
|
78460
|
-
symbol,
|
|
78461
78459
|
type: "BORROW"
|
|
78460
|
+
};
|
|
78461
|
+
if (is_isolated) {
|
|
78462
|
+
borrow_payload.isIsolated = "TRUE";
|
|
78463
|
+
borrow_payload.symbol = symbol;
|
|
78464
|
+
}
|
|
78465
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay(borrow_payload);
|
|
78466
|
+
}
|
|
78467
|
+
async function getCrossTransferAmount(asset, amount) {
|
|
78468
|
+
if (is_isolated) {
|
|
78469
|
+
return amount;
|
|
78470
|
+
}
|
|
78471
|
+
if (!this.main_client || typeof this.main_client.queryMaxTransferOutAmount !== "function") {
|
|
78472
|
+
return amount;
|
|
78473
|
+
}
|
|
78474
|
+
const response = await this.main_client.queryMaxTransferOutAmount({
|
|
78475
|
+
asset
|
|
78462
78476
|
});
|
|
78477
|
+
const allowed_amount = parseFloat(response?.amount?.toString?.() || "0");
|
|
78478
|
+
if (!Number.isFinite(allowed_amount)) {
|
|
78479
|
+
return amount;
|
|
78480
|
+
}
|
|
78481
|
+
return Math.max(0, Math.min(amount, allowed_amount));
|
|
78463
78482
|
}
|
|
78464
78483
|
let quote_transfer_result = null;
|
|
78484
|
+
let quote_transfer_amount = execution_plan.transfer_to_spot_amount;
|
|
78465
78485
|
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
78466
|
-
|
|
78467
|
-
|
|
78468
|
-
|
|
78469
|
-
|
|
78470
|
-
|
|
78471
|
-
|
|
78472
|
-
|
|
78486
|
+
quote_transfer_amount = await getCrossTransferAmount.call(this, quote_asset, execution_plan.transfer_to_spot_amount);
|
|
78487
|
+
if (quote_transfer_amount > 0) {
|
|
78488
|
+
quote_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
78489
|
+
asset: quote_asset,
|
|
78490
|
+
amount: quote_transfer_amount,
|
|
78491
|
+
symbol,
|
|
78492
|
+
transFrom: "ISOLATED_MARGIN",
|
|
78493
|
+
transTo: "SPOT"
|
|
78494
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
78495
|
+
type: "MARGIN_MAIN",
|
|
78496
|
+
asset: quote_asset,
|
|
78497
|
+
amount: quote_transfer_amount
|
|
78498
|
+
});
|
|
78499
|
+
}
|
|
78473
78500
|
}
|
|
78474
78501
|
let base_transfer_result = null;
|
|
78502
|
+
let base_transfer_amount = execution_plan.transfer_base_to_spot_amount;
|
|
78475
78503
|
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
78476
|
-
|
|
78477
|
-
|
|
78478
|
-
|
|
78479
|
-
|
|
78480
|
-
|
|
78481
|
-
|
|
78482
|
-
|
|
78504
|
+
base_transfer_amount = await getCrossTransferAmount.call(this, base_asset, execution_plan.transfer_base_to_spot_amount);
|
|
78505
|
+
if (base_transfer_amount > 0) {
|
|
78506
|
+
base_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
78507
|
+
asset: base_asset,
|
|
78508
|
+
amount: base_transfer_amount,
|
|
78509
|
+
symbol,
|
|
78510
|
+
transFrom: "ISOLATED_MARGIN",
|
|
78511
|
+
transTo: "SPOT"
|
|
78512
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
78513
|
+
type: "MARGIN_MAIN",
|
|
78514
|
+
asset: base_asset,
|
|
78515
|
+
amount: base_transfer_amount
|
|
78516
|
+
});
|
|
78517
|
+
}
|
|
78518
|
+
}
|
|
78519
|
+
const spot_quote_available = refreshed_snapshot.spot_quote_free + quote_transfer_amount;
|
|
78520
|
+
const requested_max_spot_buy_orders = requested_input.max_spot_buy_orders ?? 5;
|
|
78521
|
+
const sorted_long_orders = [...requested_input.long_orders].sort((a, b) => a.price - b.price);
|
|
78522
|
+
let affordable_spot_buy_orders = 0;
|
|
78523
|
+
let affordable_spot_buy_notional = 0;
|
|
78524
|
+
for (const order of sorted_long_orders.slice(0, requested_max_spot_buy_orders)) {
|
|
78525
|
+
const next_notional = affordable_spot_buy_notional + order.price * order.quantity;
|
|
78526
|
+
if (next_notional <= spot_quote_available + 0.00000001) {
|
|
78527
|
+
affordable_spot_buy_orders += 1;
|
|
78528
|
+
affordable_spot_buy_notional = next_notional;
|
|
78529
|
+
} else {
|
|
78530
|
+
break;
|
|
78531
|
+
}
|
|
78483
78532
|
}
|
|
78484
|
-
const
|
|
78533
|
+
const placement_plan = buildSpotMarginHedgePlan({
|
|
78534
|
+
borrow_amount: requested_input.borrow_amount,
|
|
78535
|
+
symbol: requested_input.symbol,
|
|
78536
|
+
margin_type: requested_input.margin_type,
|
|
78537
|
+
long_orders: requested_input.long_orders,
|
|
78538
|
+
short_orders: requested_input.short_orders,
|
|
78539
|
+
current_price: refreshed_snapshot.current_price,
|
|
78540
|
+
max_spot_buy_orders: affordable_spot_buy_orders,
|
|
78541
|
+
snapshot: {
|
|
78542
|
+
...refreshed_snapshot,
|
|
78543
|
+
borrowed_quote_amount: refreshed_snapshot.borrowed_quote_amount + execution_plan.borrow_delta,
|
|
78544
|
+
spot_quote_free: spot_quote_available,
|
|
78545
|
+
spot_base_free: refreshed_snapshot.spot_base_free + base_transfer_amount
|
|
78546
|
+
}
|
|
78547
|
+
});
|
|
78548
|
+
const spot_result = placement_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
78485
78549
|
symbol,
|
|
78486
|
-
orders:
|
|
78550
|
+
orders: placement_plan.orders_to_create.spot,
|
|
78487
78551
|
price_places: symbol_formats.price_places,
|
|
78488
78552
|
decimal_places: symbol_formats.decimal_places
|
|
78489
78553
|
}) : [];
|
|
78490
|
-
const margin_result =
|
|
78554
|
+
const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
78491
78555
|
symbol,
|
|
78492
|
-
orders:
|
|
78493
|
-
isIsolated:
|
|
78556
|
+
orders: placement_plan.orders_to_create.margin,
|
|
78557
|
+
isIsolated: is_isolated,
|
|
78494
78558
|
price_places: symbol_formats.price_places,
|
|
78495
78559
|
decimal_places: symbol_formats.decimal_places
|
|
78496
78560
|
}) : [];
|
|
78497
78561
|
return {
|
|
78498
78562
|
...preview,
|
|
78499
|
-
execution_plan
|
|
78563
|
+
execution_plan: {
|
|
78564
|
+
...placement_plan,
|
|
78565
|
+
borrow_amount: execution_plan.borrow_amount,
|
|
78566
|
+
borrow_delta: execution_plan.borrow_delta,
|
|
78567
|
+
transfer_to_spot_amount: quote_transfer_amount,
|
|
78568
|
+
transfer_base_to_spot_amount: base_transfer_amount
|
|
78569
|
+
},
|
|
78500
78570
|
refreshed_snapshot,
|
|
78501
78571
|
execution: {
|
|
78502
78572
|
borrow: borrow_result ? {
|
|
@@ -78506,12 +78576,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
78506
78576
|
} : null,
|
|
78507
78577
|
quote_transfer: quote_transfer_result ? {
|
|
78508
78578
|
asset: quote_asset,
|
|
78509
|
-
amount:
|
|
78579
|
+
amount: quote_transfer_amount,
|
|
78510
78580
|
response: quote_transfer_result
|
|
78511
78581
|
} : null,
|
|
78512
78582
|
base_transfer: base_transfer_result ? {
|
|
78513
78583
|
asset: base_asset,
|
|
78514
|
-
amount:
|
|
78584
|
+
amount: base_transfer_amount,
|
|
78515
78585
|
response: base_transfer_result
|
|
78516
78586
|
} : null,
|
|
78517
78587
|
spot_orders: spot_result,
|