@gbozee/ultimate 0.0.2-next.74 → 0.0.2-next.77
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 +313 -47
- package/dist/index.d.ts +96 -1
- package/dist/index.js +313 -47
- package/dist/mcp-server.cjs +311 -47
- package/dist/mcp-server.js +311 -47
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -67566,6 +67566,8 @@ class AppDatabase {
|
|
|
67566
67566
|
var exports_exchange_account = {};
|
|
67567
67567
|
__export(exports_exchange_account, {
|
|
67568
67568
|
getExchangeAccount: () => getExchangeAccount,
|
|
67569
|
+
TakeProfitReplaceRollbackSucceededError: () => TakeProfitReplaceRollbackSucceededError,
|
|
67570
|
+
TakeProfitReplaceRollbackFailedError: () => TakeProfitReplaceRollbackFailedError,
|
|
67569
67571
|
ExchangeAccount: () => ExchangeAccount
|
|
67570
67572
|
});
|
|
67571
67573
|
|
|
@@ -72788,7 +72790,7 @@ function buildSpotMarginHedgePlan(options) {
|
|
|
72788
72790
|
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
72789
72791
|
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
72790
72792
|
const spot_long_notional = sumNotional(spot_long_orders);
|
|
72791
|
-
const transfer_to_spot_amount = roundNumber2(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
72793
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, options.margin_type === "cross" ? options.borrow_amount : spot_long_notional - options.snapshot.spot_quote_free));
|
|
72792
72794
|
const short_quantity = sumQuantity(short_orders);
|
|
72793
72795
|
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
72794
72796
|
return {
|
|
@@ -73292,17 +73294,17 @@ async function createMarginLimitOrdersParallel(client, symbol, priceFormat, quan
|
|
|
73292
73294
|
return Object.fromEntries(Object.entries(v).filter(([, value2]) => value2 !== undefined));
|
|
73293
73295
|
};
|
|
73294
73296
|
const newOrders = orders.map(createMarginOrder);
|
|
73295
|
-
const
|
|
73296
|
-
const
|
|
73297
|
+
const results = [];
|
|
73298
|
+
for (const orderPayload of newOrders) {
|
|
73297
73299
|
try {
|
|
73298
73300
|
const result = await client.marginAccountNewOrder(orderPayload);
|
|
73299
73301
|
console.log("Margin order result:", result);
|
|
73300
|
-
|
|
73302
|
+
results.push(result);
|
|
73301
73303
|
} catch (error) {
|
|
73302
73304
|
console.error("Error processing margin order:", error);
|
|
73303
73305
|
throw error;
|
|
73304
73306
|
}
|
|
73305
|
-
}
|
|
73307
|
+
}
|
|
73306
73308
|
return results;
|
|
73307
73309
|
}
|
|
73308
73310
|
async function getIsolatedMarginAccountInfo(client, symbol) {
|
|
@@ -74648,24 +74650,25 @@ class BinanceExchange extends BaseExchange {
|
|
|
74648
74650
|
if (!this.main_client) {
|
|
74649
74651
|
throw new Error("Main client not available for spot and margin trading");
|
|
74650
74652
|
}
|
|
74651
|
-
if (payload.margin_type !== "isolated") {
|
|
74652
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
74653
|
-
}
|
|
74654
74653
|
const symbol = payload.symbol.toUpperCase();
|
|
74655
74654
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74656
74655
|
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74657
74656
|
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
74658
|
-
const
|
|
74657
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74658
|
+
const [spot_orders, margin_orders, spot_balances, margin_account] = await Promise.all([
|
|
74659
74659
|
this.getSpotOpenOrders(symbol),
|
|
74660
|
-
this.getMarginOpenOrders(symbol,
|
|
74660
|
+
this.getMarginOpenOrders(symbol, is_isolated),
|
|
74661
74661
|
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
74662
|
-
this.getIsolatedMarginPosition(symbol)
|
|
74662
|
+
is_isolated ? this.getIsolatedMarginPosition(symbol) : this.getCrossMarginAccount()
|
|
74663
74663
|
]);
|
|
74664
74664
|
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
74665
74665
|
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
74666
74666
|
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
74667
|
-
const
|
|
74668
|
-
const
|
|
74667
|
+
const isolated_margin_account = is_isolated ? margin_account : undefined;
|
|
74668
|
+
const cross_margin_account = !is_isolated ? margin_account : undefined;
|
|
74669
|
+
const isolated_asset = isolated_margin_account?.assets?.[0];
|
|
74670
|
+
const cross_quote_asset = !is_isolated ? cross_margin_account?.userAssets?.find((asset) => asset.asset?.toUpperCase?.() === quoteAsset) : undefined;
|
|
74671
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || cross_quote_asset?.borrowed?.toString?.() || "0");
|
|
74669
74672
|
return {
|
|
74670
74673
|
symbol,
|
|
74671
74674
|
margin_type: payload.margin_type,
|
|
@@ -74678,7 +74681,8 @@ class BinanceExchange extends BaseExchange {
|
|
|
74678
74681
|
margin_orders,
|
|
74679
74682
|
current_price,
|
|
74680
74683
|
spot_balances,
|
|
74681
|
-
isolated_margin_position
|
|
74684
|
+
isolated_margin_position: isolated_margin_account,
|
|
74685
|
+
cross_margin_account
|
|
74682
74686
|
};
|
|
74683
74687
|
}
|
|
74684
74688
|
async previewSpotMarginHedge(payload) {
|
|
@@ -74735,14 +74739,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74735
74739
|
if (!this.main_client) {
|
|
74736
74740
|
throw new Error("Main client not available for spot and margin trading");
|
|
74737
74741
|
}
|
|
74738
|
-
if (payload.margin_type !== "isolated") {
|
|
74739
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
74740
|
-
}
|
|
74741
74742
|
const symbol = payload.symbol.toUpperCase();
|
|
74742
74743
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74743
74744
|
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74744
74745
|
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
74745
74746
|
const requested_input = preview.requested_input;
|
|
74747
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74746
74748
|
if (preview.snapshot.spot_orders.length > 0) {
|
|
74747
74749
|
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
74748
74750
|
orderId: order.orderId || order.order_id || order.id,
|
|
@@ -74750,7 +74752,7 @@ class BinanceExchange extends BaseExchange {
|
|
|
74750
74752
|
})));
|
|
74751
74753
|
}
|
|
74752
74754
|
if (preview.snapshot.margin_orders.length > 0) {
|
|
74753
|
-
await this.cancelMarginOrders(symbol,
|
|
74755
|
+
await this.cancelMarginOrders(symbol, is_isolated, preview.snapshot.margin_orders.map((order) => ({
|
|
74754
74756
|
orderId: order.orderId || order.order_id || order.id,
|
|
74755
74757
|
origClientOrderId: order.origClientOrderId
|
|
74756
74758
|
})));
|
|
@@ -74772,50 +74774,120 @@ class BinanceExchange extends BaseExchange {
|
|
|
74772
74774
|
});
|
|
74773
74775
|
let borrow_result = null;
|
|
74774
74776
|
if (execution_plan.borrow_delta > 0) {
|
|
74775
|
-
|
|
74777
|
+
const borrow_payload = {
|
|
74776
74778
|
asset: quote_asset,
|
|
74777
74779
|
amount: execution_plan.borrow_delta,
|
|
74778
|
-
isIsolated: "TRUE",
|
|
74779
|
-
symbol,
|
|
74780
74780
|
type: "BORROW"
|
|
74781
|
+
};
|
|
74782
|
+
if (is_isolated) {
|
|
74783
|
+
borrow_payload.isIsolated = "TRUE";
|
|
74784
|
+
borrow_payload.symbol = symbol;
|
|
74785
|
+
}
|
|
74786
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay(borrow_payload);
|
|
74787
|
+
}
|
|
74788
|
+
async function getCrossTransferAmount(asset, amount) {
|
|
74789
|
+
if (is_isolated) {
|
|
74790
|
+
return amount;
|
|
74791
|
+
}
|
|
74792
|
+
if (!this.main_client || typeof this.main_client.queryMaxTransferOutAmount !== "function") {
|
|
74793
|
+
return amount;
|
|
74794
|
+
}
|
|
74795
|
+
const response = await this.main_client.queryMaxTransferOutAmount({
|
|
74796
|
+
asset
|
|
74781
74797
|
});
|
|
74798
|
+
const allowed_amount = parseFloat(response?.amount?.toString?.() || "0");
|
|
74799
|
+
if (!Number.isFinite(allowed_amount)) {
|
|
74800
|
+
return amount;
|
|
74801
|
+
}
|
|
74802
|
+
return Math.max(0, Math.min(amount, allowed_amount));
|
|
74782
74803
|
}
|
|
74783
74804
|
let quote_transfer_result = null;
|
|
74805
|
+
let quote_transfer_amount = execution_plan.transfer_to_spot_amount;
|
|
74784
74806
|
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
74785
|
-
|
|
74786
|
-
|
|
74787
|
-
|
|
74788
|
-
|
|
74789
|
-
|
|
74790
|
-
|
|
74791
|
-
|
|
74807
|
+
quote_transfer_amount = await getCrossTransferAmount.call(this, quote_asset, execution_plan.transfer_to_spot_amount);
|
|
74808
|
+
if (quote_transfer_amount > 0) {
|
|
74809
|
+
quote_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74810
|
+
asset: quote_asset,
|
|
74811
|
+
amount: quote_transfer_amount,
|
|
74812
|
+
symbol,
|
|
74813
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74814
|
+
transTo: "SPOT"
|
|
74815
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74816
|
+
type: "MARGIN_MAIN",
|
|
74817
|
+
asset: quote_asset,
|
|
74818
|
+
amount: quote_transfer_amount
|
|
74819
|
+
});
|
|
74820
|
+
}
|
|
74792
74821
|
}
|
|
74793
74822
|
let base_transfer_result = null;
|
|
74823
|
+
let base_transfer_amount = execution_plan.transfer_base_to_spot_amount;
|
|
74794
74824
|
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
74795
|
-
|
|
74796
|
-
|
|
74797
|
-
|
|
74798
|
-
|
|
74799
|
-
|
|
74800
|
-
|
|
74801
|
-
|
|
74825
|
+
base_transfer_amount = await getCrossTransferAmount.call(this, base_asset, execution_plan.transfer_base_to_spot_amount);
|
|
74826
|
+
if (base_transfer_amount > 0) {
|
|
74827
|
+
base_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74828
|
+
asset: base_asset,
|
|
74829
|
+
amount: base_transfer_amount,
|
|
74830
|
+
symbol,
|
|
74831
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74832
|
+
transTo: "SPOT"
|
|
74833
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74834
|
+
type: "MARGIN_MAIN",
|
|
74835
|
+
asset: base_asset,
|
|
74836
|
+
amount: base_transfer_amount
|
|
74837
|
+
});
|
|
74838
|
+
}
|
|
74802
74839
|
}
|
|
74803
|
-
const
|
|
74840
|
+
const spot_quote_available = refreshed_snapshot.spot_quote_free + quote_transfer_amount;
|
|
74841
|
+
const requested_max_spot_buy_orders = requested_input.max_spot_buy_orders ?? 5;
|
|
74842
|
+
const sorted_long_orders = [...requested_input.long_orders].sort((a, b) => a.price - b.price);
|
|
74843
|
+
let affordable_spot_buy_orders = 0;
|
|
74844
|
+
let affordable_spot_buy_notional = 0;
|
|
74845
|
+
for (const order of sorted_long_orders.slice(0, requested_max_spot_buy_orders)) {
|
|
74846
|
+
const next_notional = affordable_spot_buy_notional + order.price * order.quantity;
|
|
74847
|
+
if (next_notional <= spot_quote_available + 0.00000001) {
|
|
74848
|
+
affordable_spot_buy_orders += 1;
|
|
74849
|
+
affordable_spot_buy_notional = next_notional;
|
|
74850
|
+
} else {
|
|
74851
|
+
break;
|
|
74852
|
+
}
|
|
74853
|
+
}
|
|
74854
|
+
const placement_plan = buildSpotMarginHedgePlan({
|
|
74855
|
+
borrow_amount: requested_input.borrow_amount,
|
|
74856
|
+
symbol: requested_input.symbol,
|
|
74857
|
+
margin_type: requested_input.margin_type,
|
|
74858
|
+
long_orders: requested_input.long_orders,
|
|
74859
|
+
short_orders: requested_input.short_orders,
|
|
74860
|
+
current_price: refreshed_snapshot.current_price,
|
|
74861
|
+
max_spot_buy_orders: affordable_spot_buy_orders,
|
|
74862
|
+
snapshot: {
|
|
74863
|
+
...refreshed_snapshot,
|
|
74864
|
+
borrowed_quote_amount: refreshed_snapshot.borrowed_quote_amount + execution_plan.borrow_delta,
|
|
74865
|
+
spot_quote_free: spot_quote_available,
|
|
74866
|
+
spot_base_free: refreshed_snapshot.spot_base_free + base_transfer_amount
|
|
74867
|
+
}
|
|
74868
|
+
});
|
|
74869
|
+
const spot_result = placement_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
74804
74870
|
symbol,
|
|
74805
|
-
orders:
|
|
74871
|
+
orders: placement_plan.orders_to_create.spot,
|
|
74806
74872
|
price_places: symbol_formats.price_places,
|
|
74807
74873
|
decimal_places: symbol_formats.decimal_places
|
|
74808
74874
|
}) : [];
|
|
74809
|
-
const margin_result =
|
|
74875
|
+
const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
74810
74876
|
symbol,
|
|
74811
|
-
orders:
|
|
74812
|
-
isIsolated:
|
|
74877
|
+
orders: placement_plan.orders_to_create.margin,
|
|
74878
|
+
isIsolated: is_isolated,
|
|
74813
74879
|
price_places: symbol_formats.price_places,
|
|
74814
74880
|
decimal_places: symbol_formats.decimal_places
|
|
74815
74881
|
}) : [];
|
|
74816
74882
|
return {
|
|
74817
74883
|
...preview,
|
|
74818
|
-
execution_plan
|
|
74884
|
+
execution_plan: {
|
|
74885
|
+
...placement_plan,
|
|
74886
|
+
borrow_amount: execution_plan.borrow_amount,
|
|
74887
|
+
borrow_delta: execution_plan.borrow_delta,
|
|
74888
|
+
transfer_to_spot_amount: quote_transfer_amount,
|
|
74889
|
+
transfer_base_to_spot_amount: base_transfer_amount
|
|
74890
|
+
},
|
|
74819
74891
|
refreshed_snapshot,
|
|
74820
74892
|
execution: {
|
|
74821
74893
|
borrow: borrow_result ? {
|
|
@@ -74825,12 +74897,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74825
74897
|
} : null,
|
|
74826
74898
|
quote_transfer: quote_transfer_result ? {
|
|
74827
74899
|
asset: quote_asset,
|
|
74828
|
-
amount:
|
|
74900
|
+
amount: quote_transfer_amount,
|
|
74829
74901
|
response: quote_transfer_result
|
|
74830
74902
|
} : null,
|
|
74831
74903
|
base_transfer: base_transfer_result ? {
|
|
74832
74904
|
asset: base_asset,
|
|
74833
|
-
amount:
|
|
74905
|
+
amount: base_transfer_amount,
|
|
74834
74906
|
response: base_transfer_result
|
|
74835
74907
|
} : null,
|
|
74836
74908
|
spot_orders: spot_result,
|
|
@@ -78325,6 +78397,76 @@ async function forceClosePosition2(client, symbol, options) {
|
|
|
78325
78397
|
}
|
|
78326
78398
|
|
|
78327
78399
|
// src/exchange-account.ts
|
|
78400
|
+
function getFormatPrecision(places) {
|
|
78401
|
+
if (!places) {
|
|
78402
|
+
return 0;
|
|
78403
|
+
}
|
|
78404
|
+
const match = places.match(/%\.(\d+)f/);
|
|
78405
|
+
return match ? Number(match[1]) : 0;
|
|
78406
|
+
}
|
|
78407
|
+
function floorToFormat(value2, places) {
|
|
78408
|
+
if (!Number.isFinite(value2) || value2 <= 0) {
|
|
78409
|
+
return 0;
|
|
78410
|
+
}
|
|
78411
|
+
const precision = getFormatPrecision(places);
|
|
78412
|
+
const factor = 10 ** precision;
|
|
78413
|
+
return Math.floor((value2 + Number.EPSILON) * factor) / factor;
|
|
78414
|
+
}
|
|
78415
|
+
|
|
78416
|
+
class TakeProfitReplaceRollbackSucceededError extends Error {
|
|
78417
|
+
status = "rollback_succeeded";
|
|
78418
|
+
restored = true;
|
|
78419
|
+
symbol;
|
|
78420
|
+
kind;
|
|
78421
|
+
takeProfitPrice;
|
|
78422
|
+
quantity;
|
|
78423
|
+
previousTakeProfitPrice;
|
|
78424
|
+
previousQuantity;
|
|
78425
|
+
restoreAttempted;
|
|
78426
|
+
cause;
|
|
78427
|
+
rollbackError;
|
|
78428
|
+
constructor(metadata3) {
|
|
78429
|
+
super("Take profit replacement failed and previous take profit was restored");
|
|
78430
|
+
this.name = "TakeProfitReplaceRollbackSucceededError";
|
|
78431
|
+
this.symbol = metadata3.symbol;
|
|
78432
|
+
this.kind = metadata3.kind;
|
|
78433
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78434
|
+
this.quantity = metadata3.quantity;
|
|
78435
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78436
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78437
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78438
|
+
this.cause = metadata3.cause;
|
|
78439
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78440
|
+
}
|
|
78441
|
+
}
|
|
78442
|
+
|
|
78443
|
+
class TakeProfitReplaceRollbackFailedError extends Error {
|
|
78444
|
+
status = "rollback_failed";
|
|
78445
|
+
restored = false;
|
|
78446
|
+
symbol;
|
|
78447
|
+
kind;
|
|
78448
|
+
takeProfitPrice;
|
|
78449
|
+
quantity;
|
|
78450
|
+
previousTakeProfitPrice;
|
|
78451
|
+
previousQuantity;
|
|
78452
|
+
restoreAttempted;
|
|
78453
|
+
cause;
|
|
78454
|
+
rollbackError;
|
|
78455
|
+
constructor(metadata3) {
|
|
78456
|
+
super("Take profit replacement failed and previous take profit could not be restored");
|
|
78457
|
+
this.name = "TakeProfitReplaceRollbackFailedError";
|
|
78458
|
+
this.symbol = metadata3.symbol;
|
|
78459
|
+
this.kind = metadata3.kind;
|
|
78460
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78461
|
+
this.quantity = metadata3.quantity;
|
|
78462
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78463
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78464
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78465
|
+
this.cause = metadata3.cause;
|
|
78466
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78467
|
+
}
|
|
78468
|
+
}
|
|
78469
|
+
|
|
78328
78470
|
class ExchangeAccount {
|
|
78329
78471
|
instance;
|
|
78330
78472
|
exchange;
|
|
@@ -78421,8 +78563,6 @@ class ExchangeAccount {
|
|
|
78421
78563
|
refresh
|
|
78422
78564
|
});
|
|
78423
78565
|
const raw_active_account = live_exchange_instance.data;
|
|
78424
|
-
console.log("raw_active", raw_active_account);
|
|
78425
|
-
console.log("symbol_config", symbol_config);
|
|
78426
78566
|
const _all = get_active_accounts({
|
|
78427
78567
|
active_account: raw_active_account,
|
|
78428
78568
|
symbol_config
|
|
@@ -78556,6 +78696,132 @@ class ExchangeAccount {
|
|
|
78556
78696
|
});
|
|
78557
78697
|
return await focus_position.cancelOrders(payload);
|
|
78558
78698
|
}
|
|
78699
|
+
async replaceTakeProfitForPosition(payload) {
|
|
78700
|
+
const { symbol, kind, takeProfitPrice, quantity } = payload;
|
|
78701
|
+
const focusPosition = await this.getFocusPosition({
|
|
78702
|
+
symbol,
|
|
78703
|
+
kind,
|
|
78704
|
+
update: true
|
|
78705
|
+
});
|
|
78706
|
+
const position2 = focusPosition.getInstance();
|
|
78707
|
+
const symbol_config = focusPosition.symbol_config;
|
|
78708
|
+
if (!symbol_config) {
|
|
78709
|
+
throw new Error(`Missing symbol config for ${symbol}`);
|
|
78710
|
+
}
|
|
78711
|
+
const normalizedQuantity = floorToFormat(quantity, symbol_config.decimal_places);
|
|
78712
|
+
const minSize = Number(symbol_config.min_size || 0);
|
|
78713
|
+
const previousTakeProfitPrice = Number(position2?.take_profit || 0);
|
|
78714
|
+
const previousQuantity = floorToFormat(Number(position2?.tp_quantity || 0), symbol_config.decimal_places);
|
|
78715
|
+
if (normalizedQuantity <= 0) {
|
|
78716
|
+
return {
|
|
78717
|
+
status: "below_min_lot",
|
|
78718
|
+
symbol,
|
|
78719
|
+
kind,
|
|
78720
|
+
takeProfitPrice,
|
|
78721
|
+
quantity: 0,
|
|
78722
|
+
minSize,
|
|
78723
|
+
reason: "quantity_floored_to_zero"
|
|
78724
|
+
};
|
|
78725
|
+
}
|
|
78726
|
+
if (minSize > 0 && normalizedQuantity < minSize) {
|
|
78727
|
+
return {
|
|
78728
|
+
status: "below_min_lot",
|
|
78729
|
+
symbol,
|
|
78730
|
+
kind,
|
|
78731
|
+
takeProfitPrice,
|
|
78732
|
+
quantity: normalizedQuantity,
|
|
78733
|
+
minSize,
|
|
78734
|
+
reason: "below_venue_minimum"
|
|
78735
|
+
};
|
|
78736
|
+
}
|
|
78737
|
+
const exchangeSeams = this.exchange;
|
|
78738
|
+
if (exchangeSeams.replaceTakeProfitForPosition) {
|
|
78739
|
+
await exchangeSeams.replaceTakeProfitForPosition({
|
|
78740
|
+
symbol,
|
|
78741
|
+
kind,
|
|
78742
|
+
takeProfitPrice,
|
|
78743
|
+
quantity: normalizedQuantity,
|
|
78744
|
+
price_places: symbol_config.price_places,
|
|
78745
|
+
decimal_places: symbol_config.decimal_places,
|
|
78746
|
+
previousTakeProfitPrice,
|
|
78747
|
+
previousQuantity
|
|
78748
|
+
});
|
|
78749
|
+
return {
|
|
78750
|
+
status: "replaced",
|
|
78751
|
+
symbol,
|
|
78752
|
+
kind,
|
|
78753
|
+
takeProfitPrice,
|
|
78754
|
+
quantity: normalizedQuantity,
|
|
78755
|
+
previousTakeProfitPrice,
|
|
78756
|
+
previousQuantity,
|
|
78757
|
+
restoreAttempted: false,
|
|
78758
|
+
via: "native_replace"
|
|
78759
|
+
};
|
|
78760
|
+
}
|
|
78761
|
+
const placeTpWithoutCancelling = exchangeSeams._placeTpOrder;
|
|
78762
|
+
if (!placeTpWithoutCancelling) {
|
|
78763
|
+
throw new Error(`Exchange ${this.instance.exchange} does not expose a TP replace or raw TP placement path`);
|
|
78764
|
+
}
|
|
78765
|
+
if (previousTakeProfitPrice > 0) {
|
|
78766
|
+
await this.cancelOrders({
|
|
78767
|
+
symbol,
|
|
78768
|
+
kind,
|
|
78769
|
+
price: previousTakeProfitPrice
|
|
78770
|
+
});
|
|
78771
|
+
}
|
|
78772
|
+
try {
|
|
78773
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78774
|
+
symbol,
|
|
78775
|
+
tp: takeProfitPrice,
|
|
78776
|
+
kind,
|
|
78777
|
+
quantity: normalizedQuantity,
|
|
78778
|
+
cancel: false,
|
|
78779
|
+
price_places: symbol_config.price_places,
|
|
78780
|
+
decimal_places: symbol_config.decimal_places
|
|
78781
|
+
});
|
|
78782
|
+
return {
|
|
78783
|
+
status: "replaced",
|
|
78784
|
+
symbol,
|
|
78785
|
+
kind,
|
|
78786
|
+
takeProfitPrice,
|
|
78787
|
+
quantity: normalizedQuantity,
|
|
78788
|
+
previousTakeProfitPrice,
|
|
78789
|
+
previousQuantity,
|
|
78790
|
+
restoreAttempted: false
|
|
78791
|
+
};
|
|
78792
|
+
} catch (cause) {
|
|
78793
|
+
const metadata3 = {
|
|
78794
|
+
symbol,
|
|
78795
|
+
kind,
|
|
78796
|
+
takeProfitPrice,
|
|
78797
|
+
quantity: normalizedQuantity,
|
|
78798
|
+
previousTakeProfitPrice,
|
|
78799
|
+
previousQuantity,
|
|
78800
|
+
restoreAttempted: previousTakeProfitPrice > 0 && previousQuantity > 0,
|
|
78801
|
+
cause
|
|
78802
|
+
};
|
|
78803
|
+
if (!metadata3.restoreAttempted) {
|
|
78804
|
+
throw new TakeProfitReplaceRollbackFailedError(metadata3);
|
|
78805
|
+
}
|
|
78806
|
+
try {
|
|
78807
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78808
|
+
symbol,
|
|
78809
|
+
tp: previousTakeProfitPrice,
|
|
78810
|
+
kind,
|
|
78811
|
+
quantity: previousQuantity,
|
|
78812
|
+
cancel: false,
|
|
78813
|
+
price_places: symbol_config.price_places,
|
|
78814
|
+
decimal_places: symbol_config.decimal_places
|
|
78815
|
+
});
|
|
78816
|
+
} catch (rollbackError) {
|
|
78817
|
+
throw new TakeProfitReplaceRollbackFailedError({
|
|
78818
|
+
...metadata3,
|
|
78819
|
+
rollbackError
|
|
78820
|
+
});
|
|
78821
|
+
}
|
|
78822
|
+
throw new TakeProfitReplaceRollbackSucceededError(metadata3);
|
|
78823
|
+
}
|
|
78824
|
+
}
|
|
78559
78825
|
async cancelExchangeOrders(payload) {
|
|
78560
78826
|
return this.exchange.cancelOrders(payload);
|
|
78561
78827
|
}
|
|
@@ -78840,8 +79106,8 @@ class ExchangeAccount {
|
|
|
78840
79106
|
if (payload.trigger && !long_pause_tp && !short_pause_tp && config2) {
|
|
78841
79107
|
return await this.reduceMajorPositionEntry({
|
|
78842
79108
|
symbol,
|
|
78843
|
-
long: config2.long,
|
|
78844
|
-
short: config2.short,
|
|
79109
|
+
long: kind === "long" ? config2.long : undefined,
|
|
79110
|
+
short: kind === "short" ? config2.short : undefined,
|
|
78845
79111
|
trigger: config2.trigger
|
|
78846
79112
|
});
|
|
78847
79113
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7212,6 +7212,47 @@ export declare class ExchangePosition {
|
|
|
7212
7212
|
}, isIsolated?: boolean): Promise<void>;
|
|
7213
7213
|
buildMarginData(place?: boolean): Promise<any | null>;
|
|
7214
7214
|
}
|
|
7215
|
+
export type TakeProfitReplaceMetadata = {
|
|
7216
|
+
status: "rollback_succeeded" | "rollback_failed";
|
|
7217
|
+
restored: boolean;
|
|
7218
|
+
symbol: string;
|
|
7219
|
+
kind: "long" | "short";
|
|
7220
|
+
takeProfitPrice: number;
|
|
7221
|
+
quantity: number;
|
|
7222
|
+
previousTakeProfitPrice: number;
|
|
7223
|
+
previousQuantity: number;
|
|
7224
|
+
restoreAttempted: boolean;
|
|
7225
|
+
cause: unknown;
|
|
7226
|
+
rollbackError?: unknown;
|
|
7227
|
+
};
|
|
7228
|
+
declare class TakeProfitReplaceRollbackSucceededError extends Error implements TakeProfitReplaceMetadata {
|
|
7229
|
+
status: "rollback_succeeded";
|
|
7230
|
+
restored: true;
|
|
7231
|
+
symbol: string;
|
|
7232
|
+
kind: "long" | "short";
|
|
7233
|
+
takeProfitPrice: number;
|
|
7234
|
+
quantity: number;
|
|
7235
|
+
previousTakeProfitPrice: number;
|
|
7236
|
+
previousQuantity: number;
|
|
7237
|
+
restoreAttempted: boolean;
|
|
7238
|
+
cause: unknown;
|
|
7239
|
+
rollbackError?: unknown;
|
|
7240
|
+
constructor(metadata: Omit<TakeProfitReplaceMetadata, "status" | "restored">);
|
|
7241
|
+
}
|
|
7242
|
+
declare class TakeProfitReplaceRollbackFailedError extends Error implements TakeProfitReplaceMetadata {
|
|
7243
|
+
status: "rollback_failed";
|
|
7244
|
+
restored: false;
|
|
7245
|
+
symbol: string;
|
|
7246
|
+
kind: "long" | "short";
|
|
7247
|
+
takeProfitPrice: number;
|
|
7248
|
+
quantity: number;
|
|
7249
|
+
previousTakeProfitPrice: number;
|
|
7250
|
+
previousQuantity: number;
|
|
7251
|
+
restoreAttempted: boolean;
|
|
7252
|
+
cause: unknown;
|
|
7253
|
+
rollbackError?: unknown;
|
|
7254
|
+
constructor(metadata: Omit<TakeProfitReplaceMetadata, "status" | "restored">);
|
|
7255
|
+
}
|
|
7215
7256
|
declare class ExchangeAccount$1 {
|
|
7216
7257
|
instance: {
|
|
7217
7258
|
owner: string;
|
|
@@ -7398,6 +7439,60 @@ declare class ExchangeAccount$1 {
|
|
|
7398
7439
|
message?: undefined;
|
|
7399
7440
|
exchange_result?: undefined;
|
|
7400
7441
|
}>;
|
|
7442
|
+
replaceTakeProfitForPosition(payload: {
|
|
7443
|
+
symbol: string;
|
|
7444
|
+
kind: "long" | "short";
|
|
7445
|
+
takeProfitPrice: number;
|
|
7446
|
+
quantity: number;
|
|
7447
|
+
}): Promise<{
|
|
7448
|
+
status: "below_min_lot";
|
|
7449
|
+
symbol: string;
|
|
7450
|
+
kind: "long" | "short";
|
|
7451
|
+
takeProfitPrice: number;
|
|
7452
|
+
quantity: number;
|
|
7453
|
+
minSize: number;
|
|
7454
|
+
reason: "quantity_floored_to_zero";
|
|
7455
|
+
previousTakeProfitPrice?: undefined;
|
|
7456
|
+
previousQuantity?: undefined;
|
|
7457
|
+
restoreAttempted?: undefined;
|
|
7458
|
+
via?: undefined;
|
|
7459
|
+
} | {
|
|
7460
|
+
status: "below_min_lot";
|
|
7461
|
+
symbol: string;
|
|
7462
|
+
kind: "long" | "short";
|
|
7463
|
+
takeProfitPrice: number;
|
|
7464
|
+
quantity: number;
|
|
7465
|
+
minSize: number;
|
|
7466
|
+
reason: "below_venue_minimum";
|
|
7467
|
+
previousTakeProfitPrice?: undefined;
|
|
7468
|
+
previousQuantity?: undefined;
|
|
7469
|
+
restoreAttempted?: undefined;
|
|
7470
|
+
via?: undefined;
|
|
7471
|
+
} | {
|
|
7472
|
+
status: "replaced";
|
|
7473
|
+
symbol: string;
|
|
7474
|
+
kind: "long" | "short";
|
|
7475
|
+
takeProfitPrice: number;
|
|
7476
|
+
quantity: number;
|
|
7477
|
+
previousTakeProfitPrice: number;
|
|
7478
|
+
previousQuantity: number;
|
|
7479
|
+
restoreAttempted: boolean;
|
|
7480
|
+
via: "native_replace";
|
|
7481
|
+
minSize?: undefined;
|
|
7482
|
+
reason?: undefined;
|
|
7483
|
+
} | {
|
|
7484
|
+
status: "replaced";
|
|
7485
|
+
symbol: string;
|
|
7486
|
+
kind: "long" | "short";
|
|
7487
|
+
takeProfitPrice: number;
|
|
7488
|
+
quantity: number;
|
|
7489
|
+
previousTakeProfitPrice: number;
|
|
7490
|
+
previousQuantity: number;
|
|
7491
|
+
restoreAttempted: boolean;
|
|
7492
|
+
minSize?: undefined;
|
|
7493
|
+
reason?: undefined;
|
|
7494
|
+
via?: undefined;
|
|
7495
|
+
}>;
|
|
7401
7496
|
cancelExchangeOrders(payload: {
|
|
7402
7497
|
symbol: string;
|
|
7403
7498
|
orders: number[];
|
|
@@ -8327,7 +8422,7 @@ declare namespace database {
|
|
|
8327
8422
|
export { AppDatabase, ExchangeType, decryptObject, encryptObject, initPocketBaseClient };
|
|
8328
8423
|
}
|
|
8329
8424
|
declare namespace exchange_account {
|
|
8330
|
-
export { ExchangeAccount$1 as ExchangeAccount, getExchangeAccount };
|
|
8425
|
+
export { ExchangeAccount$1 as ExchangeAccount, TakeProfitReplaceRollbackFailedError, TakeProfitReplaceRollbackSucceededError, getExchangeAccount };
|
|
8331
8426
|
}
|
|
8332
8427
|
declare namespace app {
|
|
8333
8428
|
export { App, getCredentials, initApp, initialize };
|