@gbozee/ultimate 0.0.2-next.78 → 0.0.2-next.79

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
@@ -72180,7 +72180,7 @@ class BaseExchange {
72180
72180
  quantity: Math.abs(payload.quantity),
72181
72181
  kind: payload.kind,
72182
72182
  cancel: true,
72183
- is_limit: true,
72183
+ is_limit: !payload.is_market,
72184
72184
  hedge: payload.hedge,
72185
72185
  price_places: payload.price_places,
72186
72186
  decimal_places: payload.decimal_places,
@@ -75095,6 +75095,50 @@ function titleCase(str) {
75095
75095
  return word.charAt(0).toUpperCase() + word.slice(1);
75096
75096
  }).join(" ");
75097
75097
  }
75098
+ function getPrintfFormatFromStepSize2(stepSize, fallback) {
75099
+ if (stepSize === undefined || stepSize === null) {
75100
+ return fallback;
75101
+ }
75102
+ const normalized = String(stepSize);
75103
+ const [, decimalPart = ""] = normalized.split(".");
75104
+ const trimmed = decimalPart.replace(/0+$/, "");
75105
+ const digits = trimmed.length;
75106
+ return `%.${digits}f`;
75107
+ }
75108
+ function normalizeBybitReplayType(type) {
75109
+ const normalized = (type || "LIMIT").replace(/([a-z])([A-Z])/g, "$1_$2");
75110
+ const upper = normalized.toUpperCase();
75111
+ if (upper.includes("TAKE_PROFIT")) {
75112
+ return upper;
75113
+ }
75114
+ if (upper === "TAKEPROFIT") {
75115
+ return "TAKE_PROFIT";
75116
+ }
75117
+ if (upper === "TAKEPROFIT_MARKET") {
75118
+ return "TAKE_PROFIT_MARKET";
75119
+ }
75120
+ if (upper === "STOPLOSS") {
75121
+ return "STOP";
75122
+ }
75123
+ if (upper === "STOP_LOSS") {
75124
+ return "STOP";
75125
+ }
75126
+ return upper;
75127
+ }
75128
+ function normalizeBybitReplayOrder(order) {
75129
+ return {
75130
+ ...order,
75131
+ side: order.side?.toLowerCase(),
75132
+ kind: order.positionIdx === 1 ? "long" : "short",
75133
+ type: normalizeBybitReplayType(order.type || order.orderType),
75134
+ price: typeof order.price === "number" ? order.price : parseFloat(order.price || "0"),
75135
+ quantity: typeof order.quantity === "number" ? order.quantity : parseFloat(order.qty || order.quantity || "0"),
75136
+ qty: typeof order.qty === "number" ? order.qty : parseFloat(order.qty || "0"),
75137
+ triggerPrice: typeof order.triggerPrice === "number" ? order.triggerPrice : parseFloat(order.triggerPrice || "0"),
75138
+ id: order.id || order.orderId,
75139
+ order_id: order.order_id || order.orderId
75140
+ };
75141
+ }
75098
75142
  async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
75099
75143
  const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
75100
75144
  const splitOrders = (inputOrders) => {
@@ -75164,7 +75208,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
75164
75208
  const _res = await client.batchSubmitOrders("linear", batch2);
75165
75209
  await new Promise((resolve) => setTimeout(resolve, 1000));
75166
75210
  console.log(_res.retExtInfo.list);
75167
- res.concat(_res?.result?.list || []);
75211
+ res = res.concat(_res?.result?.list || []);
75168
75212
  }
75169
75213
  return res;
75170
75214
  }
@@ -75606,6 +75650,23 @@ class BybitExchange extends BaseExchange {
75606
75650
  async getPositionInfo(symbol) {
75607
75651
  return await getPositionInfo2(this.client, symbol);
75608
75652
  }
75653
+ async getFuturesSymbolFormats(symbol) {
75654
+ if (typeof this.client.getInstrumentsInfo !== "function") {
75655
+ throw new Error("Bybit instruments info client is not available");
75656
+ }
75657
+ const response = await this.client.getInstrumentsInfo({
75658
+ category: "linear",
75659
+ symbol: symbol.toUpperCase()
75660
+ });
75661
+ const symbol_info = response?.result?.list?.find((item) => item.symbol === symbol.toUpperCase());
75662
+ if (!symbol_info) {
75663
+ throw new Error(`Bybit symbol formats not found for ${symbol.toUpperCase()}`);
75664
+ }
75665
+ return {
75666
+ price_places: getPrintfFormatFromStepSize2(symbol_info.priceFilter?.tickSize, "%.8f"),
75667
+ decimal_places: getPrintfFormatFromStepSize2(symbol_info.lotSizeFilter?.qtyStep, "%.8f")
75668
+ };
75669
+ }
75609
75670
  async cancelAllOrders(symbol, payload) {
75610
75671
  return await cancelAllOrders2(this.client, symbol, payload);
75611
75672
  }
@@ -75673,6 +75734,124 @@ class BybitExchange extends BaseExchange {
75673
75734
  getOpenOrders(payload) {
75674
75735
  return getOpenOrders2(this.client, payload.symbol);
75675
75736
  }
75737
+ async getFuturesReplaySnapshot(payload) {
75738
+ const symbol = payload.symbol.toUpperCase();
75739
+ const [open_orders, position_info, current_price] = await Promise.all([
75740
+ this.getOpenOrders({ symbol }),
75741
+ this.getPositionInfo(symbol),
75742
+ this.getCurrentPrice(symbol)
75743
+ ]);
75744
+ const normalized_open_orders = open_orders.map(normalizeBybitReplayOrder);
75745
+ return buildFuturesReplaySnapshot({
75746
+ symbol,
75747
+ kind: payload.kind,
75748
+ current_price,
75749
+ position: {
75750
+ size: parseFloat(position_info[payload.kind]?.size || "0")
75751
+ },
75752
+ open_orders: normalized_open_orders
75753
+ });
75754
+ }
75755
+ async previewFuturesReplay(payload) {
75756
+ const snapshot = payload.snapshot ? {
75757
+ ...payload.snapshot,
75758
+ current_price: payload.snapshot.current_price ?? await this.getCurrentPrice(payload.symbol)
75759
+ } : await this.getFuturesReplaySnapshot({
75760
+ symbol: payload.symbol,
75761
+ kind: payload.kind
75762
+ });
75763
+ const shouldInferEntryOrders = !payload.entry_orders || payload.entry_orders.length === 0;
75764
+ const shouldInferStopOrders = !payload.stop_orders || payload.stop_orders.length === 0;
75765
+ const shouldInferTakeProfitOrders = !payload.take_profit_orders || payload.take_profit_orders.length === 0;
75766
+ const inferred_input = shouldInferEntryOrders || shouldInferStopOrders || shouldInferTakeProfitOrders ? inferFuturesReplayInputFromLiveOrders({
75767
+ symbol: payload.symbol,
75768
+ kind: payload.kind,
75769
+ current_price: snapshot.current_price,
75770
+ position: snapshot.position,
75771
+ open_orders: snapshot.open_orders
75772
+ }) : undefined;
75773
+ const explicit_input = normalizeFuturesReplayInput({
75774
+ kind: payload.kind,
75775
+ entry_orders: payload.entry_orders || [],
75776
+ stop_orders: payload.stop_orders || [],
75777
+ take_profit_orders: payload.take_profit_orders || []
75778
+ });
75779
+ const entry_orders = shouldInferEntryOrders ? inferred_input?.entry_orders || [] : explicit_input.entry_orders;
75780
+ const stop_orders = shouldInferStopOrders ? inferred_input?.stop_orders || [] : explicit_input.stop_orders;
75781
+ const take_profit_orders = shouldInferTakeProfitOrders ? inferred_input?.take_profit_orders || [] : explicit_input.take_profit_orders;
75782
+ const requested_input = {
75783
+ symbol: payload.symbol,
75784
+ kind: payload.kind,
75785
+ entry_orders,
75786
+ stop_orders,
75787
+ take_profit_orders
75788
+ };
75789
+ const plan = buildFuturesReplayPlan({
75790
+ ...requested_input,
75791
+ snapshot
75792
+ });
75793
+ const comparison = compareFuturesReplayOrders({
75794
+ ...requested_input,
75795
+ snapshot
75796
+ });
75797
+ return {
75798
+ snapshot,
75799
+ inferred_input,
75800
+ requested_input,
75801
+ plan,
75802
+ comparison
75803
+ };
75804
+ }
75805
+ async placeFuturesReplay(payload) {
75806
+ const preview = await this.previewFuturesReplay(payload);
75807
+ if (!payload.place) {
75808
+ return preview;
75809
+ }
75810
+ const symbol = payload.symbol.toUpperCase();
75811
+ await cancelAllOrders2(this.client, symbol, {
75812
+ kind: payload.kind
75813
+ });
75814
+ const refreshed_snapshot = await this.getFuturesReplaySnapshot({
75815
+ symbol,
75816
+ kind: payload.kind
75817
+ });
75818
+ const execution_plan = buildFuturesReplayPlan({
75819
+ symbol,
75820
+ kind: payload.kind,
75821
+ entry_orders: preview.requested_input.entry_orders,
75822
+ stop_orders: preview.requested_input.stop_orders,
75823
+ take_profit_orders: preview.requested_input.take_profit_orders,
75824
+ snapshot: refreshed_snapshot
75825
+ });
75826
+ const { price_places, decimal_places } = await this.getFuturesSymbolFormats(symbol);
75827
+ const entry_orders = execution_plan.orders_to_create.entry.map((order) => ({
75828
+ ...order,
75829
+ side: payload.kind === "long" ? "buy" : "sell",
75830
+ kind: payload.kind
75831
+ }));
75832
+ const stop_orders = execution_plan.orders_to_create.stop.map((order) => ({
75833
+ ...order,
75834
+ side: payload.kind === "long" ? "sell" : "buy",
75835
+ kind: payload.kind,
75836
+ type: order.type || "STOP"
75837
+ }));
75838
+ const take_profit_orders = execution_plan.orders_to_create.take_profit.map((order) => ({
75839
+ ...order,
75840
+ side: payload.kind === "long" ? "sell" : "buy",
75841
+ kind: payload.kind,
75842
+ ...order.stop ? { type: order.type || "TAKE_PROFIT" } : {}
75843
+ }));
75844
+ const execution = entry_orders.length > 0 || stop_orders.length > 0 || take_profit_orders.length > 0 ? await createLimitPurchaseOrders(this.client, symbol, price_places, decimal_places, [...entry_orders, ...stop_orders, ...take_profit_orders]) : [];
75845
+ return {
75846
+ ...preview,
75847
+ execution_plan,
75848
+ refreshed_snapshot,
75849
+ execution
75850
+ };
75851
+ }
75852
+ async placeFuturesExactTrade(payload) {
75853
+ return await this.placeFuturesReplay(payload);
75854
+ }
75676
75855
  async placeBadStopEntry(payload) {}
75677
75856
  async getTransferableAmount(options) {
75678
75857
  const { asset, maxTransferLimit } = options;
@@ -78533,7 +78712,6 @@ class ExchangeAccount {
78533
78712
  const active_account = await this.getActiveAccount({
78534
78713
  symbol: payload.symbol
78535
78714
  });
78536
- console.log("positions", raw_positions);
78537
78715
  const long_position = positions.find((x) => x.kind === "long");
78538
78716
  const short_position = positions.find((x) => x.kind === "short");
78539
78717
  this.long_position = new ExchangePosition({
@@ -81320,6 +81498,8 @@ add_avg as (
81320
81498
  AND s.symbol = p.symbol
81321
81499
  AND s.account = p.account
81322
81500
  AND s.stop > 0
81501
+ ORDER BY
81502
+ CASE WHEN p.kind = 'long' THEN -s.stop ELSE s.stop END ASC
81323
81503
  LIMIT
81324
81504
  1
81325
81505
  ) AS stop_loss,
package/dist/index.d.ts CHANGED
@@ -577,6 +577,7 @@ declare abstract class BaseExchange {
577
577
  price_places?: string;
578
578
  decimal_places?: string;
579
579
  hedge?: boolean;
580
+ is_market?: boolean;
580
581
  }): Promise<any>;
581
582
  abstract setLeverage(payload: {
582
583
  symbol: string;
package/dist/index.js CHANGED
@@ -72078,7 +72078,7 @@ class BaseExchange {
72078
72078
  quantity: Math.abs(payload.quantity),
72079
72079
  kind: payload.kind,
72080
72080
  cancel: true,
72081
- is_limit: true,
72081
+ is_limit: !payload.is_market,
72082
72082
  hedge: payload.hedge,
72083
72083
  price_places: payload.price_places,
72084
72084
  decimal_places: payload.decimal_places,
@@ -74993,6 +74993,50 @@ function titleCase(str) {
74993
74993
  return word.charAt(0).toUpperCase() + word.slice(1);
74994
74994
  }).join(" ");
74995
74995
  }
74996
+ function getPrintfFormatFromStepSize2(stepSize, fallback) {
74997
+ if (stepSize === undefined || stepSize === null) {
74998
+ return fallback;
74999
+ }
75000
+ const normalized = String(stepSize);
75001
+ const [, decimalPart = ""] = normalized.split(".");
75002
+ const trimmed = decimalPart.replace(/0+$/, "");
75003
+ const digits = trimmed.length;
75004
+ return `%.${digits}f`;
75005
+ }
75006
+ function normalizeBybitReplayType(type) {
75007
+ const normalized = (type || "LIMIT").replace(/([a-z])([A-Z])/g, "$1_$2");
75008
+ const upper = normalized.toUpperCase();
75009
+ if (upper.includes("TAKE_PROFIT")) {
75010
+ return upper;
75011
+ }
75012
+ if (upper === "TAKEPROFIT") {
75013
+ return "TAKE_PROFIT";
75014
+ }
75015
+ if (upper === "TAKEPROFIT_MARKET") {
75016
+ return "TAKE_PROFIT_MARKET";
75017
+ }
75018
+ if (upper === "STOPLOSS") {
75019
+ return "STOP";
75020
+ }
75021
+ if (upper === "STOP_LOSS") {
75022
+ return "STOP";
75023
+ }
75024
+ return upper;
75025
+ }
75026
+ function normalizeBybitReplayOrder(order) {
75027
+ return {
75028
+ ...order,
75029
+ side: order.side?.toLowerCase(),
75030
+ kind: order.positionIdx === 1 ? "long" : "short",
75031
+ type: normalizeBybitReplayType(order.type || order.orderType),
75032
+ price: typeof order.price === "number" ? order.price : parseFloat(order.price || "0"),
75033
+ quantity: typeof order.quantity === "number" ? order.quantity : parseFloat(order.qty || order.quantity || "0"),
75034
+ qty: typeof order.qty === "number" ? order.qty : parseFloat(order.qty || "0"),
75035
+ triggerPrice: typeof order.triggerPrice === "number" ? order.triggerPrice : parseFloat(order.triggerPrice || "0"),
75036
+ id: order.id || order.orderId,
75037
+ order_id: order.order_id || order.orderId
75038
+ };
75039
+ }
74996
75040
  async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
74997
75041
  const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
74998
75042
  const splitOrders = (inputOrders) => {
@@ -75062,7 +75106,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
75062
75106
  const _res = await client.batchSubmitOrders("linear", batch2);
75063
75107
  await new Promise((resolve) => setTimeout(resolve, 1000));
75064
75108
  console.log(_res.retExtInfo.list);
75065
- res.concat(_res?.result?.list || []);
75109
+ res = res.concat(_res?.result?.list || []);
75066
75110
  }
75067
75111
  return res;
75068
75112
  }
@@ -75504,6 +75548,23 @@ class BybitExchange extends BaseExchange {
75504
75548
  async getPositionInfo(symbol) {
75505
75549
  return await getPositionInfo2(this.client, symbol);
75506
75550
  }
75551
+ async getFuturesSymbolFormats(symbol) {
75552
+ if (typeof this.client.getInstrumentsInfo !== "function") {
75553
+ throw new Error("Bybit instruments info client is not available");
75554
+ }
75555
+ const response = await this.client.getInstrumentsInfo({
75556
+ category: "linear",
75557
+ symbol: symbol.toUpperCase()
75558
+ });
75559
+ const symbol_info = response?.result?.list?.find((item) => item.symbol === symbol.toUpperCase());
75560
+ if (!symbol_info) {
75561
+ throw new Error(`Bybit symbol formats not found for ${symbol.toUpperCase()}`);
75562
+ }
75563
+ return {
75564
+ price_places: getPrintfFormatFromStepSize2(symbol_info.priceFilter?.tickSize, "%.8f"),
75565
+ decimal_places: getPrintfFormatFromStepSize2(symbol_info.lotSizeFilter?.qtyStep, "%.8f")
75566
+ };
75567
+ }
75507
75568
  async cancelAllOrders(symbol, payload) {
75508
75569
  return await cancelAllOrders2(this.client, symbol, payload);
75509
75570
  }
@@ -75571,6 +75632,124 @@ class BybitExchange extends BaseExchange {
75571
75632
  getOpenOrders(payload) {
75572
75633
  return getOpenOrders2(this.client, payload.symbol);
75573
75634
  }
75635
+ async getFuturesReplaySnapshot(payload) {
75636
+ const symbol = payload.symbol.toUpperCase();
75637
+ const [open_orders, position_info, current_price] = await Promise.all([
75638
+ this.getOpenOrders({ symbol }),
75639
+ this.getPositionInfo(symbol),
75640
+ this.getCurrentPrice(symbol)
75641
+ ]);
75642
+ const normalized_open_orders = open_orders.map(normalizeBybitReplayOrder);
75643
+ return buildFuturesReplaySnapshot({
75644
+ symbol,
75645
+ kind: payload.kind,
75646
+ current_price,
75647
+ position: {
75648
+ size: parseFloat(position_info[payload.kind]?.size || "0")
75649
+ },
75650
+ open_orders: normalized_open_orders
75651
+ });
75652
+ }
75653
+ async previewFuturesReplay(payload) {
75654
+ const snapshot = payload.snapshot ? {
75655
+ ...payload.snapshot,
75656
+ current_price: payload.snapshot.current_price ?? await this.getCurrentPrice(payload.symbol)
75657
+ } : await this.getFuturesReplaySnapshot({
75658
+ symbol: payload.symbol,
75659
+ kind: payload.kind
75660
+ });
75661
+ const shouldInferEntryOrders = !payload.entry_orders || payload.entry_orders.length === 0;
75662
+ const shouldInferStopOrders = !payload.stop_orders || payload.stop_orders.length === 0;
75663
+ const shouldInferTakeProfitOrders = !payload.take_profit_orders || payload.take_profit_orders.length === 0;
75664
+ const inferred_input = shouldInferEntryOrders || shouldInferStopOrders || shouldInferTakeProfitOrders ? inferFuturesReplayInputFromLiveOrders({
75665
+ symbol: payload.symbol,
75666
+ kind: payload.kind,
75667
+ current_price: snapshot.current_price,
75668
+ position: snapshot.position,
75669
+ open_orders: snapshot.open_orders
75670
+ }) : undefined;
75671
+ const explicit_input = normalizeFuturesReplayInput({
75672
+ kind: payload.kind,
75673
+ entry_orders: payload.entry_orders || [],
75674
+ stop_orders: payload.stop_orders || [],
75675
+ take_profit_orders: payload.take_profit_orders || []
75676
+ });
75677
+ const entry_orders = shouldInferEntryOrders ? inferred_input?.entry_orders || [] : explicit_input.entry_orders;
75678
+ const stop_orders = shouldInferStopOrders ? inferred_input?.stop_orders || [] : explicit_input.stop_orders;
75679
+ const take_profit_orders = shouldInferTakeProfitOrders ? inferred_input?.take_profit_orders || [] : explicit_input.take_profit_orders;
75680
+ const requested_input = {
75681
+ symbol: payload.symbol,
75682
+ kind: payload.kind,
75683
+ entry_orders,
75684
+ stop_orders,
75685
+ take_profit_orders
75686
+ };
75687
+ const plan = buildFuturesReplayPlan({
75688
+ ...requested_input,
75689
+ snapshot
75690
+ });
75691
+ const comparison = compareFuturesReplayOrders({
75692
+ ...requested_input,
75693
+ snapshot
75694
+ });
75695
+ return {
75696
+ snapshot,
75697
+ inferred_input,
75698
+ requested_input,
75699
+ plan,
75700
+ comparison
75701
+ };
75702
+ }
75703
+ async placeFuturesReplay(payload) {
75704
+ const preview = await this.previewFuturesReplay(payload);
75705
+ if (!payload.place) {
75706
+ return preview;
75707
+ }
75708
+ const symbol = payload.symbol.toUpperCase();
75709
+ await cancelAllOrders2(this.client, symbol, {
75710
+ kind: payload.kind
75711
+ });
75712
+ const refreshed_snapshot = await this.getFuturesReplaySnapshot({
75713
+ symbol,
75714
+ kind: payload.kind
75715
+ });
75716
+ const execution_plan = buildFuturesReplayPlan({
75717
+ symbol,
75718
+ kind: payload.kind,
75719
+ entry_orders: preview.requested_input.entry_orders,
75720
+ stop_orders: preview.requested_input.stop_orders,
75721
+ take_profit_orders: preview.requested_input.take_profit_orders,
75722
+ snapshot: refreshed_snapshot
75723
+ });
75724
+ const { price_places, decimal_places } = await this.getFuturesSymbolFormats(symbol);
75725
+ const entry_orders = execution_plan.orders_to_create.entry.map((order) => ({
75726
+ ...order,
75727
+ side: payload.kind === "long" ? "buy" : "sell",
75728
+ kind: payload.kind
75729
+ }));
75730
+ const stop_orders = execution_plan.orders_to_create.stop.map((order) => ({
75731
+ ...order,
75732
+ side: payload.kind === "long" ? "sell" : "buy",
75733
+ kind: payload.kind,
75734
+ type: order.type || "STOP"
75735
+ }));
75736
+ const take_profit_orders = execution_plan.orders_to_create.take_profit.map((order) => ({
75737
+ ...order,
75738
+ side: payload.kind === "long" ? "sell" : "buy",
75739
+ kind: payload.kind,
75740
+ ...order.stop ? { type: order.type || "TAKE_PROFIT" } : {}
75741
+ }));
75742
+ const execution = entry_orders.length > 0 || stop_orders.length > 0 || take_profit_orders.length > 0 ? await createLimitPurchaseOrders(this.client, symbol, price_places, decimal_places, [...entry_orders, ...stop_orders, ...take_profit_orders]) : [];
75743
+ return {
75744
+ ...preview,
75745
+ execution_plan,
75746
+ refreshed_snapshot,
75747
+ execution
75748
+ };
75749
+ }
75750
+ async placeFuturesExactTrade(payload) {
75751
+ return await this.placeFuturesReplay(payload);
75752
+ }
75574
75753
  async placeBadStopEntry(payload) {}
75575
75754
  async getTransferableAmount(options) {
75576
75755
  const { asset, maxTransferLimit } = options;
@@ -78431,7 +78610,6 @@ class ExchangeAccount {
78431
78610
  const active_account = await this.getActiveAccount({
78432
78611
  symbol: payload.symbol
78433
78612
  });
78434
- console.log("positions", raw_positions);
78435
78613
  const long_position = positions.find((x) => x.kind === "long");
78436
78614
  const short_position = positions.find((x) => x.kind === "short");
78437
78615
  this.long_position = new ExchangePosition({
@@ -81218,6 +81396,8 @@ add_avg as (
81218
81396
  AND s.symbol = p.symbol
81219
81397
  AND s.account = p.account
81220
81398
  AND s.stop > 0
81399
+ ORDER BY
81400
+ CASE WHEN p.kind = 'long' THEN -s.stop ELSE s.stop END ASC
81221
81401
  LIMIT
81222
81402
  1
81223
81403
  ) AS stop_loss,
@@ -75900,7 +75900,7 @@ class BaseExchange {
75900
75900
  quantity: Math.abs(payload.quantity),
75901
75901
  kind: payload.kind,
75902
75902
  cancel: true,
75903
- is_limit: true,
75903
+ is_limit: !payload.is_market,
75904
75904
  hedge: payload.hedge,
75905
75905
  price_places: payload.price_places,
75906
75906
  decimal_places: payload.decimal_places,
@@ -78815,6 +78815,50 @@ function titleCase(str) {
78815
78815
  return word.charAt(0).toUpperCase() + word.slice(1);
78816
78816
  }).join(" ");
78817
78817
  }
78818
+ function getPrintfFormatFromStepSize2(stepSize, fallback) {
78819
+ if (stepSize === undefined || stepSize === null) {
78820
+ return fallback;
78821
+ }
78822
+ const normalized = String(stepSize);
78823
+ const [, decimalPart = ""] = normalized.split(".");
78824
+ const trimmed = decimalPart.replace(/0+$/, "");
78825
+ const digits = trimmed.length;
78826
+ return `%.${digits}f`;
78827
+ }
78828
+ function normalizeBybitReplayType(type) {
78829
+ const normalized = (type || "LIMIT").replace(/([a-z])([A-Z])/g, "$1_$2");
78830
+ const upper = normalized.toUpperCase();
78831
+ if (upper.includes("TAKE_PROFIT")) {
78832
+ return upper;
78833
+ }
78834
+ if (upper === "TAKEPROFIT") {
78835
+ return "TAKE_PROFIT";
78836
+ }
78837
+ if (upper === "TAKEPROFIT_MARKET") {
78838
+ return "TAKE_PROFIT_MARKET";
78839
+ }
78840
+ if (upper === "STOPLOSS") {
78841
+ return "STOP";
78842
+ }
78843
+ if (upper === "STOP_LOSS") {
78844
+ return "STOP";
78845
+ }
78846
+ return upper;
78847
+ }
78848
+ function normalizeBybitReplayOrder(order) {
78849
+ return {
78850
+ ...order,
78851
+ side: order.side?.toLowerCase(),
78852
+ kind: order.positionIdx === 1 ? "long" : "short",
78853
+ type: normalizeBybitReplayType(order.type || order.orderType),
78854
+ price: typeof order.price === "number" ? order.price : parseFloat(order.price || "0"),
78855
+ quantity: typeof order.quantity === "number" ? order.quantity : parseFloat(order.qty || order.quantity || "0"),
78856
+ qty: typeof order.qty === "number" ? order.qty : parseFloat(order.qty || "0"),
78857
+ triggerPrice: typeof order.triggerPrice === "number" ? order.triggerPrice : parseFloat(order.triggerPrice || "0"),
78858
+ id: order.id || order.orderId,
78859
+ order_id: order.order_id || order.orderId
78860
+ };
78861
+ }
78818
78862
  async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
78819
78863
  const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
78820
78864
  const splitOrders = (inputOrders) => {
@@ -78884,7 +78928,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
78884
78928
  const _res = await client.batchSubmitOrders("linear", batch2);
78885
78929
  await new Promise((resolve) => setTimeout(resolve, 1000));
78886
78930
  console.log(_res.retExtInfo.list);
78887
- res.concat(_res?.result?.list || []);
78931
+ res = res.concat(_res?.result?.list || []);
78888
78932
  }
78889
78933
  return res;
78890
78934
  }
@@ -79326,6 +79370,23 @@ class BybitExchange extends BaseExchange {
79326
79370
  async getPositionInfo(symbol) {
79327
79371
  return await getPositionInfo2(this.client, symbol);
79328
79372
  }
79373
+ async getFuturesSymbolFormats(symbol) {
79374
+ if (typeof this.client.getInstrumentsInfo !== "function") {
79375
+ throw new Error("Bybit instruments info client is not available");
79376
+ }
79377
+ const response = await this.client.getInstrumentsInfo({
79378
+ category: "linear",
79379
+ symbol: symbol.toUpperCase()
79380
+ });
79381
+ const symbol_info = response?.result?.list?.find((item) => item.symbol === symbol.toUpperCase());
79382
+ if (!symbol_info) {
79383
+ throw new Error(`Bybit symbol formats not found for ${symbol.toUpperCase()}`);
79384
+ }
79385
+ return {
79386
+ price_places: getPrintfFormatFromStepSize2(symbol_info.priceFilter?.tickSize, "%.8f"),
79387
+ decimal_places: getPrintfFormatFromStepSize2(symbol_info.lotSizeFilter?.qtyStep, "%.8f")
79388
+ };
79389
+ }
79329
79390
  async cancelAllOrders(symbol, payload) {
79330
79391
  return await cancelAllOrders2(this.client, symbol, payload);
79331
79392
  }
@@ -79393,6 +79454,124 @@ class BybitExchange extends BaseExchange {
79393
79454
  getOpenOrders(payload) {
79394
79455
  return getOpenOrders2(this.client, payload.symbol);
79395
79456
  }
79457
+ async getFuturesReplaySnapshot(payload) {
79458
+ const symbol = payload.symbol.toUpperCase();
79459
+ const [open_orders, position_info, current_price] = await Promise.all([
79460
+ this.getOpenOrders({ symbol }),
79461
+ this.getPositionInfo(symbol),
79462
+ this.getCurrentPrice(symbol)
79463
+ ]);
79464
+ const normalized_open_orders = open_orders.map(normalizeBybitReplayOrder);
79465
+ return buildFuturesReplaySnapshot({
79466
+ symbol,
79467
+ kind: payload.kind,
79468
+ current_price,
79469
+ position: {
79470
+ size: parseFloat(position_info[payload.kind]?.size || "0")
79471
+ },
79472
+ open_orders: normalized_open_orders
79473
+ });
79474
+ }
79475
+ async previewFuturesReplay(payload) {
79476
+ const snapshot = payload.snapshot ? {
79477
+ ...payload.snapshot,
79478
+ current_price: payload.snapshot.current_price ?? await this.getCurrentPrice(payload.symbol)
79479
+ } : await this.getFuturesReplaySnapshot({
79480
+ symbol: payload.symbol,
79481
+ kind: payload.kind
79482
+ });
79483
+ const shouldInferEntryOrders = !payload.entry_orders || payload.entry_orders.length === 0;
79484
+ const shouldInferStopOrders = !payload.stop_orders || payload.stop_orders.length === 0;
79485
+ const shouldInferTakeProfitOrders = !payload.take_profit_orders || payload.take_profit_orders.length === 0;
79486
+ const inferred_input = shouldInferEntryOrders || shouldInferStopOrders || shouldInferTakeProfitOrders ? inferFuturesReplayInputFromLiveOrders({
79487
+ symbol: payload.symbol,
79488
+ kind: payload.kind,
79489
+ current_price: snapshot.current_price,
79490
+ position: snapshot.position,
79491
+ open_orders: snapshot.open_orders
79492
+ }) : undefined;
79493
+ const explicit_input = normalizeFuturesReplayInput({
79494
+ kind: payload.kind,
79495
+ entry_orders: payload.entry_orders || [],
79496
+ stop_orders: payload.stop_orders || [],
79497
+ take_profit_orders: payload.take_profit_orders || []
79498
+ });
79499
+ const entry_orders = shouldInferEntryOrders ? inferred_input?.entry_orders || [] : explicit_input.entry_orders;
79500
+ const stop_orders = shouldInferStopOrders ? inferred_input?.stop_orders || [] : explicit_input.stop_orders;
79501
+ const take_profit_orders = shouldInferTakeProfitOrders ? inferred_input?.take_profit_orders || [] : explicit_input.take_profit_orders;
79502
+ const requested_input = {
79503
+ symbol: payload.symbol,
79504
+ kind: payload.kind,
79505
+ entry_orders,
79506
+ stop_orders,
79507
+ take_profit_orders
79508
+ };
79509
+ const plan = buildFuturesReplayPlan({
79510
+ ...requested_input,
79511
+ snapshot
79512
+ });
79513
+ const comparison = compareFuturesReplayOrders({
79514
+ ...requested_input,
79515
+ snapshot
79516
+ });
79517
+ return {
79518
+ snapshot,
79519
+ inferred_input,
79520
+ requested_input,
79521
+ plan,
79522
+ comparison
79523
+ };
79524
+ }
79525
+ async placeFuturesReplay(payload) {
79526
+ const preview = await this.previewFuturesReplay(payload);
79527
+ if (!payload.place) {
79528
+ return preview;
79529
+ }
79530
+ const symbol = payload.symbol.toUpperCase();
79531
+ await cancelAllOrders2(this.client, symbol, {
79532
+ kind: payload.kind
79533
+ });
79534
+ const refreshed_snapshot = await this.getFuturesReplaySnapshot({
79535
+ symbol,
79536
+ kind: payload.kind
79537
+ });
79538
+ const execution_plan = buildFuturesReplayPlan({
79539
+ symbol,
79540
+ kind: payload.kind,
79541
+ entry_orders: preview.requested_input.entry_orders,
79542
+ stop_orders: preview.requested_input.stop_orders,
79543
+ take_profit_orders: preview.requested_input.take_profit_orders,
79544
+ snapshot: refreshed_snapshot
79545
+ });
79546
+ const { price_places, decimal_places } = await this.getFuturesSymbolFormats(symbol);
79547
+ const entry_orders = execution_plan.orders_to_create.entry.map((order) => ({
79548
+ ...order,
79549
+ side: payload.kind === "long" ? "buy" : "sell",
79550
+ kind: payload.kind
79551
+ }));
79552
+ const stop_orders = execution_plan.orders_to_create.stop.map((order) => ({
79553
+ ...order,
79554
+ side: payload.kind === "long" ? "sell" : "buy",
79555
+ kind: payload.kind,
79556
+ type: order.type || "STOP"
79557
+ }));
79558
+ const take_profit_orders = execution_plan.orders_to_create.take_profit.map((order) => ({
79559
+ ...order,
79560
+ side: payload.kind === "long" ? "sell" : "buy",
79561
+ kind: payload.kind,
79562
+ ...order.stop ? { type: order.type || "TAKE_PROFIT" } : {}
79563
+ }));
79564
+ const execution = entry_orders.length > 0 || stop_orders.length > 0 || take_profit_orders.length > 0 ? await createLimitPurchaseOrders(this.client, symbol, price_places, decimal_places, [...entry_orders, ...stop_orders, ...take_profit_orders]) : [];
79565
+ return {
79566
+ ...preview,
79567
+ execution_plan,
79568
+ refreshed_snapshot,
79569
+ execution
79570
+ };
79571
+ }
79572
+ async placeFuturesExactTrade(payload) {
79573
+ return await this.placeFuturesReplay(payload);
79574
+ }
79396
79575
  async placeBadStopEntry(payload) {}
79397
79576
  async getTransferableAmount(options) {
79398
79577
  const { asset, maxTransferLimit } = options;
@@ -82253,7 +82432,6 @@ class ExchangeAccount {
82253
82432
  const active_account = await this.getActiveAccount({
82254
82433
  symbol: payload.symbol
82255
82434
  });
82256
- console.log("positions", raw_positions);
82257
82435
  const long_position = positions.find((x) => x.kind === "long");
82258
82436
  const short_position = positions.find((x) => x.kind === "short");
82259
82437
  this.long_position = new ExchangePosition({
@@ -75859,7 +75859,7 @@ class BaseExchange {
75859
75859
  quantity: Math.abs(payload.quantity),
75860
75860
  kind: payload.kind,
75861
75861
  cancel: true,
75862
- is_limit: true,
75862
+ is_limit: !payload.is_market,
75863
75863
  hedge: payload.hedge,
75864
75864
  price_places: payload.price_places,
75865
75865
  decimal_places: payload.decimal_places,
@@ -78774,6 +78774,50 @@ function titleCase(str) {
78774
78774
  return word.charAt(0).toUpperCase() + word.slice(1);
78775
78775
  }).join(" ");
78776
78776
  }
78777
+ function getPrintfFormatFromStepSize2(stepSize, fallback) {
78778
+ if (stepSize === undefined || stepSize === null) {
78779
+ return fallback;
78780
+ }
78781
+ const normalized = String(stepSize);
78782
+ const [, decimalPart = ""] = normalized.split(".");
78783
+ const trimmed = decimalPart.replace(/0+$/, "");
78784
+ const digits = trimmed.length;
78785
+ return `%.${digits}f`;
78786
+ }
78787
+ function normalizeBybitReplayType(type) {
78788
+ const normalized = (type || "LIMIT").replace(/([a-z])([A-Z])/g, "$1_$2");
78789
+ const upper = normalized.toUpperCase();
78790
+ if (upper.includes("TAKE_PROFIT")) {
78791
+ return upper;
78792
+ }
78793
+ if (upper === "TAKEPROFIT") {
78794
+ return "TAKE_PROFIT";
78795
+ }
78796
+ if (upper === "TAKEPROFIT_MARKET") {
78797
+ return "TAKE_PROFIT_MARKET";
78798
+ }
78799
+ if (upper === "STOPLOSS") {
78800
+ return "STOP";
78801
+ }
78802
+ if (upper === "STOP_LOSS") {
78803
+ return "STOP";
78804
+ }
78805
+ return upper;
78806
+ }
78807
+ function normalizeBybitReplayOrder(order) {
78808
+ return {
78809
+ ...order,
78810
+ side: order.side?.toLowerCase(),
78811
+ kind: order.positionIdx === 1 ? "long" : "short",
78812
+ type: normalizeBybitReplayType(order.type || order.orderType),
78813
+ price: typeof order.price === "number" ? order.price : parseFloat(order.price || "0"),
78814
+ quantity: typeof order.quantity === "number" ? order.quantity : parseFloat(order.qty || order.quantity || "0"),
78815
+ qty: typeof order.qty === "number" ? order.qty : parseFloat(order.qty || "0"),
78816
+ triggerPrice: typeof order.triggerPrice === "number" ? order.triggerPrice : parseFloat(order.triggerPrice || "0"),
78817
+ id: order.id || order.orderId,
78818
+ order_id: order.order_id || order.orderId
78819
+ };
78820
+ }
78777
78821
  async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
78778
78822
  const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
78779
78823
  const splitOrders = (inputOrders) => {
@@ -78843,7 +78887,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
78843
78887
  const _res = await client.batchSubmitOrders("linear", batch2);
78844
78888
  await new Promise((resolve) => setTimeout(resolve, 1000));
78845
78889
  console.log(_res.retExtInfo.list);
78846
- res.concat(_res?.result?.list || []);
78890
+ res = res.concat(_res?.result?.list || []);
78847
78891
  }
78848
78892
  return res;
78849
78893
  }
@@ -79285,6 +79329,23 @@ class BybitExchange extends BaseExchange {
79285
79329
  async getPositionInfo(symbol) {
79286
79330
  return await getPositionInfo2(this.client, symbol);
79287
79331
  }
79332
+ async getFuturesSymbolFormats(symbol) {
79333
+ if (typeof this.client.getInstrumentsInfo !== "function") {
79334
+ throw new Error("Bybit instruments info client is not available");
79335
+ }
79336
+ const response = await this.client.getInstrumentsInfo({
79337
+ category: "linear",
79338
+ symbol: symbol.toUpperCase()
79339
+ });
79340
+ const symbol_info = response?.result?.list?.find((item) => item.symbol === symbol.toUpperCase());
79341
+ if (!symbol_info) {
79342
+ throw new Error(`Bybit symbol formats not found for ${symbol.toUpperCase()}`);
79343
+ }
79344
+ return {
79345
+ price_places: getPrintfFormatFromStepSize2(symbol_info.priceFilter?.tickSize, "%.8f"),
79346
+ decimal_places: getPrintfFormatFromStepSize2(symbol_info.lotSizeFilter?.qtyStep, "%.8f")
79347
+ };
79348
+ }
79288
79349
  async cancelAllOrders(symbol, payload) {
79289
79350
  return await cancelAllOrders2(this.client, symbol, payload);
79290
79351
  }
@@ -79352,6 +79413,124 @@ class BybitExchange extends BaseExchange {
79352
79413
  getOpenOrders(payload) {
79353
79414
  return getOpenOrders2(this.client, payload.symbol);
79354
79415
  }
79416
+ async getFuturesReplaySnapshot(payload) {
79417
+ const symbol = payload.symbol.toUpperCase();
79418
+ const [open_orders, position_info, current_price] = await Promise.all([
79419
+ this.getOpenOrders({ symbol }),
79420
+ this.getPositionInfo(symbol),
79421
+ this.getCurrentPrice(symbol)
79422
+ ]);
79423
+ const normalized_open_orders = open_orders.map(normalizeBybitReplayOrder);
79424
+ return buildFuturesReplaySnapshot({
79425
+ symbol,
79426
+ kind: payload.kind,
79427
+ current_price,
79428
+ position: {
79429
+ size: parseFloat(position_info[payload.kind]?.size || "0")
79430
+ },
79431
+ open_orders: normalized_open_orders
79432
+ });
79433
+ }
79434
+ async previewFuturesReplay(payload) {
79435
+ const snapshot = payload.snapshot ? {
79436
+ ...payload.snapshot,
79437
+ current_price: payload.snapshot.current_price ?? await this.getCurrentPrice(payload.symbol)
79438
+ } : await this.getFuturesReplaySnapshot({
79439
+ symbol: payload.symbol,
79440
+ kind: payload.kind
79441
+ });
79442
+ const shouldInferEntryOrders = !payload.entry_orders || payload.entry_orders.length === 0;
79443
+ const shouldInferStopOrders = !payload.stop_orders || payload.stop_orders.length === 0;
79444
+ const shouldInferTakeProfitOrders = !payload.take_profit_orders || payload.take_profit_orders.length === 0;
79445
+ const inferred_input = shouldInferEntryOrders || shouldInferStopOrders || shouldInferTakeProfitOrders ? inferFuturesReplayInputFromLiveOrders({
79446
+ symbol: payload.symbol,
79447
+ kind: payload.kind,
79448
+ current_price: snapshot.current_price,
79449
+ position: snapshot.position,
79450
+ open_orders: snapshot.open_orders
79451
+ }) : undefined;
79452
+ const explicit_input = normalizeFuturesReplayInput({
79453
+ kind: payload.kind,
79454
+ entry_orders: payload.entry_orders || [],
79455
+ stop_orders: payload.stop_orders || [],
79456
+ take_profit_orders: payload.take_profit_orders || []
79457
+ });
79458
+ const entry_orders = shouldInferEntryOrders ? inferred_input?.entry_orders || [] : explicit_input.entry_orders;
79459
+ const stop_orders = shouldInferStopOrders ? inferred_input?.stop_orders || [] : explicit_input.stop_orders;
79460
+ const take_profit_orders = shouldInferTakeProfitOrders ? inferred_input?.take_profit_orders || [] : explicit_input.take_profit_orders;
79461
+ const requested_input = {
79462
+ symbol: payload.symbol,
79463
+ kind: payload.kind,
79464
+ entry_orders,
79465
+ stop_orders,
79466
+ take_profit_orders
79467
+ };
79468
+ const plan = buildFuturesReplayPlan({
79469
+ ...requested_input,
79470
+ snapshot
79471
+ });
79472
+ const comparison = compareFuturesReplayOrders({
79473
+ ...requested_input,
79474
+ snapshot
79475
+ });
79476
+ return {
79477
+ snapshot,
79478
+ inferred_input,
79479
+ requested_input,
79480
+ plan,
79481
+ comparison
79482
+ };
79483
+ }
79484
+ async placeFuturesReplay(payload) {
79485
+ const preview = await this.previewFuturesReplay(payload);
79486
+ if (!payload.place) {
79487
+ return preview;
79488
+ }
79489
+ const symbol = payload.symbol.toUpperCase();
79490
+ await cancelAllOrders2(this.client, symbol, {
79491
+ kind: payload.kind
79492
+ });
79493
+ const refreshed_snapshot = await this.getFuturesReplaySnapshot({
79494
+ symbol,
79495
+ kind: payload.kind
79496
+ });
79497
+ const execution_plan = buildFuturesReplayPlan({
79498
+ symbol,
79499
+ kind: payload.kind,
79500
+ entry_orders: preview.requested_input.entry_orders,
79501
+ stop_orders: preview.requested_input.stop_orders,
79502
+ take_profit_orders: preview.requested_input.take_profit_orders,
79503
+ snapshot: refreshed_snapshot
79504
+ });
79505
+ const { price_places, decimal_places } = await this.getFuturesSymbolFormats(symbol);
79506
+ const entry_orders = execution_plan.orders_to_create.entry.map((order) => ({
79507
+ ...order,
79508
+ side: payload.kind === "long" ? "buy" : "sell",
79509
+ kind: payload.kind
79510
+ }));
79511
+ const stop_orders = execution_plan.orders_to_create.stop.map((order) => ({
79512
+ ...order,
79513
+ side: payload.kind === "long" ? "sell" : "buy",
79514
+ kind: payload.kind,
79515
+ type: order.type || "STOP"
79516
+ }));
79517
+ const take_profit_orders = execution_plan.orders_to_create.take_profit.map((order) => ({
79518
+ ...order,
79519
+ side: payload.kind === "long" ? "sell" : "buy",
79520
+ kind: payload.kind,
79521
+ ...order.stop ? { type: order.type || "TAKE_PROFIT" } : {}
79522
+ }));
79523
+ const execution = entry_orders.length > 0 || stop_orders.length > 0 || take_profit_orders.length > 0 ? await createLimitPurchaseOrders(this.client, symbol, price_places, decimal_places, [...entry_orders, ...stop_orders, ...take_profit_orders]) : [];
79524
+ return {
79525
+ ...preview,
79526
+ execution_plan,
79527
+ refreshed_snapshot,
79528
+ execution
79529
+ };
79530
+ }
79531
+ async placeFuturesExactTrade(payload) {
79532
+ return await this.placeFuturesReplay(payload);
79533
+ }
79355
79534
  async placeBadStopEntry(payload) {}
79356
79535
  async getTransferableAmount(options) {
79357
79536
  const { asset, maxTransferLimit } = options;
@@ -82212,7 +82391,6 @@ class ExchangeAccount {
82212
82391
  const active_account = await this.getActiveAccount({
82213
82392
  symbol: payload.symbol
82214
82393
  });
82215
- console.log("positions", raw_positions);
82216
82394
  const long_position = positions.find((x) => x.kind === "long");
82217
82395
  const short_position = positions.find((x) => x.kind === "short");
82218
82396
  this.long_position = new ExchangePosition({
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.78",
4
+ "version": "0.0.2-next.79",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",