@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 +183 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +183 -3
- package/dist/mcp-server.cjs +181 -3
- package/dist/mcp-server.js +181 -3
- package/package.json +1 -1
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:
|
|
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
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:
|
|
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,
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -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:
|
|
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({
|
package/dist/mcp-server.js
CHANGED
|
@@ -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:
|
|
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({
|