@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 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 limit = import_p_limit.default(ORDERS_PER_SECOND);
73296
- const results = await Promise.all(newOrders.map((orderPayload) => limit(async () => {
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
- return result;
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 [spot_orders, margin_orders, spot_balances, isolated_margin_position] = await Promise.all([
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, true),
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 isolated_asset = isolated_margin_position?.assets?.[0];
74668
- const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || "0");
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, true, preview.snapshot.margin_orders.map((order) => ({
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
- borrow_result = await this.main_client.submitMarginAccountBorrowRepay({
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
- quote_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
74786
- asset: quote_asset,
74787
- amount: execution_plan.transfer_to_spot_amount,
74788
- symbol,
74789
- transFrom: "ISOLATED_MARGIN",
74790
- transTo: "SPOT"
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
- base_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
74796
- asset: base_asset,
74797
- amount: execution_plan.transfer_base_to_spot_amount,
74798
- symbol,
74799
- transFrom: "ISOLATED_MARGIN",
74800
- transTo: "SPOT"
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 spot_result = execution_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
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: execution_plan.orders_to_create.spot,
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 = execution_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
74873
+ const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
74810
74874
  symbol,
74811
- orders: execution_plan.orders_to_create.margin,
74812
- isIsolated: true,
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: execution_plan.transfer_to_spot_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: execution_plan.transfer_base_to_spot_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 limit = import_p_limit.default(ORDERS_PER_SECOND);
73194
- const results = await Promise.all(newOrders.map((orderPayload) => limit(async () => {
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
- return result;
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 [spot_orders, margin_orders, spot_balances, isolated_margin_position] = await Promise.all([
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, true),
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 isolated_asset = isolated_margin_position?.assets?.[0];
74566
- const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || "0");
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, true, preview.snapshot.margin_orders.map((order) => ({
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
- borrow_result = await this.main_client.submitMarginAccountBorrowRepay({
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
- 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
- });
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
- 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
- });
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 spot_result = execution_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
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: execution_plan.orders_to_create.spot,
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 = execution_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
74771
+ const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
74708
74772
  symbol,
74709
- orders: execution_plan.orders_to_create.margin,
74710
- isIsolated: true,
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: execution_plan.transfer_to_spot_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: execution_plan.transfer_base_to_spot_amount,
74801
+ amount: base_transfer_amount,
74732
74802
  response: base_transfer_result
74733
74803
  } : null,
74734
74804
  spot_orders: spot_result,
@@ -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 limit = import_p_limit.default(ORDERS_PER_SECOND);
77018
- const results = await Promise.all(newOrders.map((orderPayload) => limit(async () => {
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
- return result;
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 [spot_orders, margin_orders, spot_balances, isolated_margin_position] = await Promise.all([
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, true),
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 isolated_asset = isolated_margin_position?.assets?.[0];
78390
- const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || "0");
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, true, preview.snapshot.margin_orders.map((order) => ({
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
- borrow_result = await this.main_client.submitMarginAccountBorrowRepay({
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
- quote_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
78508
- asset: quote_asset,
78509
- amount: execution_plan.transfer_to_spot_amount,
78510
- symbol,
78511
- transFrom: "ISOLATED_MARGIN",
78512
- transTo: "SPOT"
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
- base_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
78518
- asset: base_asset,
78519
- amount: execution_plan.transfer_base_to_spot_amount,
78520
- symbol,
78521
- transFrom: "ISOLATED_MARGIN",
78522
- transTo: "SPOT"
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 spot_result = execution_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
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: execution_plan.orders_to_create.spot,
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 = execution_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
78595
+ const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
78532
78596
  symbol,
78533
- orders: execution_plan.orders_to_create.margin,
78534
- isIsolated: true,
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: execution_plan.transfer_to_spot_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: execution_plan.transfer_base_to_spot_amount,
78625
+ amount: base_transfer_amount,
78556
78626
  response: base_transfer_result
78557
78627
  } : null,
78558
78628
  spot_orders: spot_result,
@@ -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 limit = import_p_limit.default(ORDERS_PER_SECOND);
76977
- const results = await Promise.all(newOrders.map((orderPayload) => limit(async () => {
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
- return result;
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 [spot_orders, margin_orders, spot_balances, isolated_margin_position] = await Promise.all([
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, true),
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 isolated_asset = isolated_margin_position?.assets?.[0];
78349
- const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || "0");
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, true, preview.snapshot.margin_orders.map((order) => ({
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
- borrow_result = await this.main_client.submitMarginAccountBorrowRepay({
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
- quote_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
78467
- asset: quote_asset,
78468
- amount: execution_plan.transfer_to_spot_amount,
78469
- symbol,
78470
- transFrom: "ISOLATED_MARGIN",
78471
- transTo: "SPOT"
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
- base_transfer_result = await this.main_client.isolatedMarginAccountTransfer({
78477
- asset: base_asset,
78478
- amount: execution_plan.transfer_base_to_spot_amount,
78479
- symbol,
78480
- transFrom: "ISOLATED_MARGIN",
78481
- transTo: "SPOT"
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 spot_result = execution_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
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: execution_plan.orders_to_create.spot,
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 = execution_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
78554
+ const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
78491
78555
  symbol,
78492
- orders: execution_plan.orders_to_create.margin,
78493
- isIsolated: true,
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: execution_plan.transfer_to_spot_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: execution_plan.transfer_base_to_spot_amount,
78584
+ amount: base_transfer_amount,
78515
78585
  response: base_transfer_result
78516
78586
  } : null,
78517
78587
  spot_orders: spot_result,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gbozee/ultimate",
3
3
  "type": "module",
4
- "version": "0.0.2-next.74",
4
+ "version": "0.0.2-next.75",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",