@gbozee/ultimate 0.0.2-next.77 → 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 +190 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +190 -5
- package/dist/mcp-server.cjs +188 -5
- package/dist/mcp-server.js +188 -5
- 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,
|
|
@@ -73728,7 +73728,13 @@ function buildPosition(position2, orders, options) {
|
|
|
73728
73728
|
{ price: entry, quantity },
|
|
73729
73729
|
...limitOrders.map((o) => ({ price: o.price, quantity: o.qty }))
|
|
73730
73730
|
], decimal_places, price_places);
|
|
73731
|
-
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop)
|
|
73731
|
+
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop).sort((a, b) => {
|
|
73732
|
+
if (kind === "long") {
|
|
73733
|
+
return b.triggerPrice - a.triggerPrice;
|
|
73734
|
+
} else {
|
|
73735
|
+
return a.triggerPrice - b.triggerPrice;
|
|
73736
|
+
}
|
|
73737
|
+
})[0];
|
|
73732
73738
|
const tpOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((x) => x.kind === "long" ? x.side === "sell" : x.side === "buy")[0];
|
|
73733
73739
|
const avg_entry = avg.entry;
|
|
73734
73740
|
const stop_loss = stopOrders?.triggerPrice || 0;
|
|
@@ -75089,6 +75095,50 @@ function titleCase(str) {
|
|
|
75089
75095
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
75090
75096
|
}).join(" ");
|
|
75091
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
|
+
}
|
|
75092
75142
|
async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
|
|
75093
75143
|
const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
|
|
75094
75144
|
const splitOrders = (inputOrders) => {
|
|
@@ -75158,7 +75208,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
|
|
|
75158
75208
|
const _res = await client.batchSubmitOrders("linear", batch2);
|
|
75159
75209
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
75160
75210
|
console.log(_res.retExtInfo.list);
|
|
75161
|
-
res.concat(_res?.result?.list || []);
|
|
75211
|
+
res = res.concat(_res?.result?.list || []);
|
|
75162
75212
|
}
|
|
75163
75213
|
return res;
|
|
75164
75214
|
}
|
|
@@ -75600,6 +75650,23 @@ class BybitExchange extends BaseExchange {
|
|
|
75600
75650
|
async getPositionInfo(symbol) {
|
|
75601
75651
|
return await getPositionInfo2(this.client, symbol);
|
|
75602
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
|
+
}
|
|
75603
75670
|
async cancelAllOrders(symbol, payload) {
|
|
75604
75671
|
return await cancelAllOrders2(this.client, symbol, payload);
|
|
75605
75672
|
}
|
|
@@ -75667,6 +75734,124 @@ class BybitExchange extends BaseExchange {
|
|
|
75667
75734
|
getOpenOrders(payload) {
|
|
75668
75735
|
return getOpenOrders2(this.client, payload.symbol);
|
|
75669
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
|
+
}
|
|
75670
75855
|
async placeBadStopEntry(payload) {}
|
|
75671
75856
|
async getTransferableAmount(options) {
|
|
75672
75857
|
const { asset, maxTransferLimit } = options;
|
|
@@ -78527,7 +78712,6 @@ class ExchangeAccount {
|
|
|
78527
78712
|
const active_account = await this.getActiveAccount({
|
|
78528
78713
|
symbol: payload.symbol
|
|
78529
78714
|
});
|
|
78530
|
-
console.log("positions", raw_positions);
|
|
78531
78715
|
const long_position = positions.find((x) => x.kind === "long");
|
|
78532
78716
|
const short_position = positions.find((x) => x.kind === "short");
|
|
78533
78717
|
this.long_position = new ExchangePosition({
|
|
@@ -78578,7 +78762,6 @@ class ExchangeAccount {
|
|
|
78578
78762
|
refresh: live_refresh,
|
|
78579
78763
|
symbol
|
|
78580
78764
|
});
|
|
78581
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
78582
78765
|
if (leverage) {
|
|
78583
78766
|
this.exchange.setLeverage({ symbol, leverage });
|
|
78584
78767
|
}
|
|
@@ -81315,6 +81498,8 @@ add_avg as (
|
|
|
81315
81498
|
AND s.symbol = p.symbol
|
|
81316
81499
|
AND s.account = p.account
|
|
81317
81500
|
AND s.stop > 0
|
|
81501
|
+
ORDER BY
|
|
81502
|
+
CASE WHEN p.kind = 'long' THEN -s.stop ELSE s.stop END ASC
|
|
81318
81503
|
LIMIT
|
|
81319
81504
|
1
|
|
81320
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,
|
|
@@ -73626,7 +73626,13 @@ function buildPosition(position2, orders, options) {
|
|
|
73626
73626
|
{ price: entry, quantity },
|
|
73627
73627
|
...limitOrders.map((o) => ({ price: o.price, quantity: o.qty }))
|
|
73628
73628
|
], decimal_places, price_places);
|
|
73629
|
-
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop)
|
|
73629
|
+
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop).sort((a, b) => {
|
|
73630
|
+
if (kind === "long") {
|
|
73631
|
+
return b.triggerPrice - a.triggerPrice;
|
|
73632
|
+
} else {
|
|
73633
|
+
return a.triggerPrice - b.triggerPrice;
|
|
73634
|
+
}
|
|
73635
|
+
})[0];
|
|
73630
73636
|
const tpOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((x) => x.kind === "long" ? x.side === "sell" : x.side === "buy")[0];
|
|
73631
73637
|
const avg_entry = avg.entry;
|
|
73632
73638
|
const stop_loss = stopOrders?.triggerPrice || 0;
|
|
@@ -74987,6 +74993,50 @@ function titleCase(str) {
|
|
|
74987
74993
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
74988
74994
|
}).join(" ");
|
|
74989
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
|
+
}
|
|
74990
75040
|
async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
|
|
74991
75041
|
const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
|
|
74992
75042
|
const splitOrders = (inputOrders) => {
|
|
@@ -75056,7 +75106,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
|
|
|
75056
75106
|
const _res = await client.batchSubmitOrders("linear", batch2);
|
|
75057
75107
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
75058
75108
|
console.log(_res.retExtInfo.list);
|
|
75059
|
-
res.concat(_res?.result?.list || []);
|
|
75109
|
+
res = res.concat(_res?.result?.list || []);
|
|
75060
75110
|
}
|
|
75061
75111
|
return res;
|
|
75062
75112
|
}
|
|
@@ -75498,6 +75548,23 @@ class BybitExchange extends BaseExchange {
|
|
|
75498
75548
|
async getPositionInfo(symbol) {
|
|
75499
75549
|
return await getPositionInfo2(this.client, symbol);
|
|
75500
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
|
+
}
|
|
75501
75568
|
async cancelAllOrders(symbol, payload) {
|
|
75502
75569
|
return await cancelAllOrders2(this.client, symbol, payload);
|
|
75503
75570
|
}
|
|
@@ -75565,6 +75632,124 @@ class BybitExchange extends BaseExchange {
|
|
|
75565
75632
|
getOpenOrders(payload) {
|
|
75566
75633
|
return getOpenOrders2(this.client, payload.symbol);
|
|
75567
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
|
+
}
|
|
75568
75753
|
async placeBadStopEntry(payload) {}
|
|
75569
75754
|
async getTransferableAmount(options) {
|
|
75570
75755
|
const { asset, maxTransferLimit } = options;
|
|
@@ -78425,7 +78610,6 @@ class ExchangeAccount {
|
|
|
78425
78610
|
const active_account = await this.getActiveAccount({
|
|
78426
78611
|
symbol: payload.symbol
|
|
78427
78612
|
});
|
|
78428
|
-
console.log("positions", raw_positions);
|
|
78429
78613
|
const long_position = positions.find((x) => x.kind === "long");
|
|
78430
78614
|
const short_position = positions.find((x) => x.kind === "short");
|
|
78431
78615
|
this.long_position = new ExchangePosition({
|
|
@@ -78476,7 +78660,6 @@ class ExchangeAccount {
|
|
|
78476
78660
|
refresh: live_refresh,
|
|
78477
78661
|
symbol
|
|
78478
78662
|
});
|
|
78479
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
78480
78663
|
if (leverage) {
|
|
78481
78664
|
this.exchange.setLeverage({ symbol, leverage });
|
|
78482
78665
|
}
|
|
@@ -81213,6 +81396,8 @@ add_avg as (
|
|
|
81213
81396
|
AND s.symbol = p.symbol
|
|
81214
81397
|
AND s.account = p.account
|
|
81215
81398
|
AND s.stop > 0
|
|
81399
|
+
ORDER BY
|
|
81400
|
+
CASE WHEN p.kind = 'long' THEN -s.stop ELSE s.stop END ASC
|
|
81216
81401
|
LIMIT
|
|
81217
81402
|
1
|
|
81218
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,
|
|
@@ -77448,7 +77448,13 @@ function buildPosition(position2, orders, options) {
|
|
|
77448
77448
|
{ price: entry, quantity },
|
|
77449
77449
|
...limitOrders.map((o) => ({ price: o.price, quantity: o.qty }))
|
|
77450
77450
|
], decimal_places, price_places);
|
|
77451
|
-
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop)
|
|
77451
|
+
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop).sort((a, b) => {
|
|
77452
|
+
if (kind === "long") {
|
|
77453
|
+
return b.triggerPrice - a.triggerPrice;
|
|
77454
|
+
} else {
|
|
77455
|
+
return a.triggerPrice - b.triggerPrice;
|
|
77456
|
+
}
|
|
77457
|
+
})[0];
|
|
77452
77458
|
const tpOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((x) => x.kind === "long" ? x.side === "sell" : x.side === "buy")[0];
|
|
77453
77459
|
const avg_entry = avg.entry;
|
|
77454
77460
|
const stop_loss = stopOrders?.triggerPrice || 0;
|
|
@@ -78809,6 +78815,50 @@ function titleCase(str) {
|
|
|
78809
78815
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
78810
78816
|
}).join(" ");
|
|
78811
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
|
+
}
|
|
78812
78862
|
async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
|
|
78813
78863
|
const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
|
|
78814
78864
|
const splitOrders = (inputOrders) => {
|
|
@@ -78878,7 +78928,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
|
|
|
78878
78928
|
const _res = await client.batchSubmitOrders("linear", batch2);
|
|
78879
78929
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
78880
78930
|
console.log(_res.retExtInfo.list);
|
|
78881
|
-
res.concat(_res?.result?.list || []);
|
|
78931
|
+
res = res.concat(_res?.result?.list || []);
|
|
78882
78932
|
}
|
|
78883
78933
|
return res;
|
|
78884
78934
|
}
|
|
@@ -79320,6 +79370,23 @@ class BybitExchange extends BaseExchange {
|
|
|
79320
79370
|
async getPositionInfo(symbol) {
|
|
79321
79371
|
return await getPositionInfo2(this.client, symbol);
|
|
79322
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
|
+
}
|
|
79323
79390
|
async cancelAllOrders(symbol, payload) {
|
|
79324
79391
|
return await cancelAllOrders2(this.client, symbol, payload);
|
|
79325
79392
|
}
|
|
@@ -79387,6 +79454,124 @@ class BybitExchange extends BaseExchange {
|
|
|
79387
79454
|
getOpenOrders(payload) {
|
|
79388
79455
|
return getOpenOrders2(this.client, payload.symbol);
|
|
79389
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
|
+
}
|
|
79390
79575
|
async placeBadStopEntry(payload) {}
|
|
79391
79576
|
async getTransferableAmount(options) {
|
|
79392
79577
|
const { asset, maxTransferLimit } = options;
|
|
@@ -82247,7 +82432,6 @@ class ExchangeAccount {
|
|
|
82247
82432
|
const active_account = await this.getActiveAccount({
|
|
82248
82433
|
symbol: payload.symbol
|
|
82249
82434
|
});
|
|
82250
|
-
console.log("positions", raw_positions);
|
|
82251
82435
|
const long_position = positions.find((x) => x.kind === "long");
|
|
82252
82436
|
const short_position = positions.find((x) => x.kind === "short");
|
|
82253
82437
|
this.long_position = new ExchangePosition({
|
|
@@ -82298,7 +82482,6 @@ class ExchangeAccount {
|
|
|
82298
82482
|
refresh: live_refresh,
|
|
82299
82483
|
symbol
|
|
82300
82484
|
});
|
|
82301
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
82302
82485
|
if (leverage) {
|
|
82303
82486
|
this.exchange.setLeverage({ symbol, leverage });
|
|
82304
82487
|
}
|
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,
|
|
@@ -77407,7 +77407,13 @@ function buildPosition(position2, orders, options) {
|
|
|
77407
77407
|
{ price: entry, quantity },
|
|
77408
77408
|
...limitOrders.map((o) => ({ price: o.price, quantity: o.qty }))
|
|
77409
77409
|
], decimal_places, price_places);
|
|
77410
|
-
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop)
|
|
77410
|
+
const stopOrders = orders.filter((x) => x.kind === kind && x.isStop).sort((a, b) => {
|
|
77411
|
+
if (kind === "long") {
|
|
77412
|
+
return b.triggerPrice - a.triggerPrice;
|
|
77413
|
+
} else {
|
|
77414
|
+
return a.triggerPrice - b.triggerPrice;
|
|
77415
|
+
}
|
|
77416
|
+
})[0];
|
|
77411
77417
|
const tpOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((x) => x.kind === "long" ? x.side === "sell" : x.side === "buy")[0];
|
|
77412
77418
|
const avg_entry = avg.entry;
|
|
77413
77419
|
const stop_loss = stopOrders?.triggerPrice || 0;
|
|
@@ -78768,6 +78774,50 @@ function titleCase(str) {
|
|
|
78768
78774
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
78769
78775
|
}).join(" ");
|
|
78770
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
|
+
}
|
|
78771
78821
|
async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFormat, orders, currentPrice, workingType = "last", realClose = false) {
|
|
78772
78822
|
const workingTypeValue = workingType === "mark" ? "MarkPrice" : "LastPrice";
|
|
78773
78823
|
const splitOrders = (inputOrders) => {
|
|
@@ -78837,7 +78887,7 @@ async function createLimitPurchaseOrders(client, symbol, priceFormat, quantityFo
|
|
|
78837
78887
|
const _res = await client.batchSubmitOrders("linear", batch2);
|
|
78838
78888
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
78839
78889
|
console.log(_res.retExtInfo.list);
|
|
78840
|
-
res.concat(_res?.result?.list || []);
|
|
78890
|
+
res = res.concat(_res?.result?.list || []);
|
|
78841
78891
|
}
|
|
78842
78892
|
return res;
|
|
78843
78893
|
}
|
|
@@ -79279,6 +79329,23 @@ class BybitExchange extends BaseExchange {
|
|
|
79279
79329
|
async getPositionInfo(symbol) {
|
|
79280
79330
|
return await getPositionInfo2(this.client, symbol);
|
|
79281
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
|
+
}
|
|
79282
79349
|
async cancelAllOrders(symbol, payload) {
|
|
79283
79350
|
return await cancelAllOrders2(this.client, symbol, payload);
|
|
79284
79351
|
}
|
|
@@ -79346,6 +79413,124 @@ class BybitExchange extends BaseExchange {
|
|
|
79346
79413
|
getOpenOrders(payload) {
|
|
79347
79414
|
return getOpenOrders2(this.client, payload.symbol);
|
|
79348
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
|
+
}
|
|
79349
79534
|
async placeBadStopEntry(payload) {}
|
|
79350
79535
|
async getTransferableAmount(options) {
|
|
79351
79536
|
const { asset, maxTransferLimit } = options;
|
|
@@ -82206,7 +82391,6 @@ class ExchangeAccount {
|
|
|
82206
82391
|
const active_account = await this.getActiveAccount({
|
|
82207
82392
|
symbol: payload.symbol
|
|
82208
82393
|
});
|
|
82209
|
-
console.log("positions", raw_positions);
|
|
82210
82394
|
const long_position = positions.find((x) => x.kind === "long");
|
|
82211
82395
|
const short_position = positions.find((x) => x.kind === "short");
|
|
82212
82396
|
this.long_position = new ExchangePosition({
|
|
@@ -82257,7 +82441,6 @@ class ExchangeAccount {
|
|
|
82257
82441
|
refresh: live_refresh,
|
|
82258
82442
|
symbol
|
|
82259
82443
|
});
|
|
82260
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
82261
82444
|
if (leverage) {
|
|
82262
82445
|
this.exchange.setLeverage({ symbol, leverage });
|
|
82263
82446
|
}
|