@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.js
CHANGED
|
@@ -67464,6 +67464,8 @@ class AppDatabase {
|
|
|
67464
67464
|
var exports_exchange_account = {};
|
|
67465
67465
|
__export(exports_exchange_account, {
|
|
67466
67466
|
getExchangeAccount: () => getExchangeAccount,
|
|
67467
|
+
TakeProfitReplaceRollbackSucceededError: () => TakeProfitReplaceRollbackSucceededError,
|
|
67468
|
+
TakeProfitReplaceRollbackFailedError: () => TakeProfitReplaceRollbackFailedError,
|
|
67467
67469
|
ExchangeAccount: () => ExchangeAccount
|
|
67468
67470
|
});
|
|
67469
67471
|
|
|
@@ -72686,7 +72688,7 @@ function buildSpotMarginHedgePlan(options) {
|
|
|
72686
72688
|
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
72687
72689
|
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
72688
72690
|
const spot_long_notional = sumNotional(spot_long_orders);
|
|
72689
|
-
const transfer_to_spot_amount = roundNumber2(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
72691
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, options.margin_type === "cross" ? options.borrow_amount : spot_long_notional - options.snapshot.spot_quote_free));
|
|
72690
72692
|
const short_quantity = sumQuantity(short_orders);
|
|
72691
72693
|
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
72692
72694
|
return {
|
|
@@ -73190,17 +73192,17 @@ async function createMarginLimitOrdersParallel(client, symbol, priceFormat, quan
|
|
|
73190
73192
|
return Object.fromEntries(Object.entries(v).filter(([, value2]) => value2 !== undefined));
|
|
73191
73193
|
};
|
|
73192
73194
|
const newOrders = orders.map(createMarginOrder);
|
|
73193
|
-
const
|
|
73194
|
-
const
|
|
73195
|
+
const results = [];
|
|
73196
|
+
for (const orderPayload of newOrders) {
|
|
73195
73197
|
try {
|
|
73196
73198
|
const result = await client.marginAccountNewOrder(orderPayload);
|
|
73197
73199
|
console.log("Margin order result:", result);
|
|
73198
|
-
|
|
73200
|
+
results.push(result);
|
|
73199
73201
|
} catch (error) {
|
|
73200
73202
|
console.error("Error processing margin order:", error);
|
|
73201
73203
|
throw error;
|
|
73202
73204
|
}
|
|
73203
|
-
}
|
|
73205
|
+
}
|
|
73204
73206
|
return results;
|
|
73205
73207
|
}
|
|
73206
73208
|
async function getIsolatedMarginAccountInfo(client, symbol) {
|
|
@@ -74546,24 +74548,25 @@ class BinanceExchange extends BaseExchange {
|
|
|
74546
74548
|
if (!this.main_client) {
|
|
74547
74549
|
throw new Error("Main client not available for spot and margin trading");
|
|
74548
74550
|
}
|
|
74549
|
-
if (payload.margin_type !== "isolated") {
|
|
74550
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
74551
|
-
}
|
|
74552
74551
|
const symbol = payload.symbol.toUpperCase();
|
|
74553
74552
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74554
74553
|
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74555
74554
|
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
74556
|
-
const
|
|
74555
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74556
|
+
const [spot_orders, margin_orders, spot_balances, margin_account] = await Promise.all([
|
|
74557
74557
|
this.getSpotOpenOrders(symbol),
|
|
74558
|
-
this.getMarginOpenOrders(symbol,
|
|
74558
|
+
this.getMarginOpenOrders(symbol, is_isolated),
|
|
74559
74559
|
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
74560
|
-
this.getIsolatedMarginPosition(symbol)
|
|
74560
|
+
is_isolated ? this.getIsolatedMarginPosition(symbol) : this.getCrossMarginAccount()
|
|
74561
74561
|
]);
|
|
74562
74562
|
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
74563
74563
|
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
74564
74564
|
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
74565
|
-
const
|
|
74566
|
-
const
|
|
74565
|
+
const isolated_margin_account = is_isolated ? margin_account : undefined;
|
|
74566
|
+
const cross_margin_account = !is_isolated ? margin_account : undefined;
|
|
74567
|
+
const isolated_asset = isolated_margin_account?.assets?.[0];
|
|
74568
|
+
const cross_quote_asset = !is_isolated ? cross_margin_account?.userAssets?.find((asset) => asset.asset?.toUpperCase?.() === quoteAsset) : undefined;
|
|
74569
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || cross_quote_asset?.borrowed?.toString?.() || "0");
|
|
74567
74570
|
return {
|
|
74568
74571
|
symbol,
|
|
74569
74572
|
margin_type: payload.margin_type,
|
|
@@ -74576,7 +74579,8 @@ class BinanceExchange extends BaseExchange {
|
|
|
74576
74579
|
margin_orders,
|
|
74577
74580
|
current_price,
|
|
74578
74581
|
spot_balances,
|
|
74579
|
-
isolated_margin_position
|
|
74582
|
+
isolated_margin_position: isolated_margin_account,
|
|
74583
|
+
cross_margin_account
|
|
74580
74584
|
};
|
|
74581
74585
|
}
|
|
74582
74586
|
async previewSpotMarginHedge(payload) {
|
|
@@ -74633,14 +74637,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74633
74637
|
if (!this.main_client) {
|
|
74634
74638
|
throw new Error("Main client not available for spot and margin trading");
|
|
74635
74639
|
}
|
|
74636
|
-
if (payload.margin_type !== "isolated") {
|
|
74637
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
74638
|
-
}
|
|
74639
74640
|
const symbol = payload.symbol.toUpperCase();
|
|
74640
74641
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
74641
74642
|
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
74642
74643
|
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
74643
74644
|
const requested_input = preview.requested_input;
|
|
74645
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
74644
74646
|
if (preview.snapshot.spot_orders.length > 0) {
|
|
74645
74647
|
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
74646
74648
|
orderId: order.orderId || order.order_id || order.id,
|
|
@@ -74648,7 +74650,7 @@ class BinanceExchange extends BaseExchange {
|
|
|
74648
74650
|
})));
|
|
74649
74651
|
}
|
|
74650
74652
|
if (preview.snapshot.margin_orders.length > 0) {
|
|
74651
|
-
await this.cancelMarginOrders(symbol,
|
|
74653
|
+
await this.cancelMarginOrders(symbol, is_isolated, preview.snapshot.margin_orders.map((order) => ({
|
|
74652
74654
|
orderId: order.orderId || order.order_id || order.id,
|
|
74653
74655
|
origClientOrderId: order.origClientOrderId
|
|
74654
74656
|
})));
|
|
@@ -74670,50 +74672,120 @@ class BinanceExchange extends BaseExchange {
|
|
|
74670
74672
|
});
|
|
74671
74673
|
let borrow_result = null;
|
|
74672
74674
|
if (execution_plan.borrow_delta > 0) {
|
|
74673
|
-
|
|
74675
|
+
const borrow_payload = {
|
|
74674
74676
|
asset: quote_asset,
|
|
74675
74677
|
amount: execution_plan.borrow_delta,
|
|
74676
|
-
isIsolated: "TRUE",
|
|
74677
|
-
symbol,
|
|
74678
74678
|
type: "BORROW"
|
|
74679
|
+
};
|
|
74680
|
+
if (is_isolated) {
|
|
74681
|
+
borrow_payload.isIsolated = "TRUE";
|
|
74682
|
+
borrow_payload.symbol = symbol;
|
|
74683
|
+
}
|
|
74684
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay(borrow_payload);
|
|
74685
|
+
}
|
|
74686
|
+
async function getCrossTransferAmount(asset, amount) {
|
|
74687
|
+
if (is_isolated) {
|
|
74688
|
+
return amount;
|
|
74689
|
+
}
|
|
74690
|
+
if (!this.main_client || typeof this.main_client.queryMaxTransferOutAmount !== "function") {
|
|
74691
|
+
return amount;
|
|
74692
|
+
}
|
|
74693
|
+
const response = await this.main_client.queryMaxTransferOutAmount({
|
|
74694
|
+
asset
|
|
74679
74695
|
});
|
|
74696
|
+
const allowed_amount = parseFloat(response?.amount?.toString?.() || "0");
|
|
74697
|
+
if (!Number.isFinite(allowed_amount)) {
|
|
74698
|
+
return amount;
|
|
74699
|
+
}
|
|
74700
|
+
return Math.max(0, Math.min(amount, allowed_amount));
|
|
74680
74701
|
}
|
|
74681
74702
|
let quote_transfer_result = null;
|
|
74703
|
+
let quote_transfer_amount = execution_plan.transfer_to_spot_amount;
|
|
74682
74704
|
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
74683
|
-
|
|
74684
|
-
|
|
74685
|
-
|
|
74686
|
-
|
|
74687
|
-
|
|
74688
|
-
|
|
74689
|
-
|
|
74705
|
+
quote_transfer_amount = await getCrossTransferAmount.call(this, quote_asset, execution_plan.transfer_to_spot_amount);
|
|
74706
|
+
if (quote_transfer_amount > 0) {
|
|
74707
|
+
quote_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74708
|
+
asset: quote_asset,
|
|
74709
|
+
amount: quote_transfer_amount,
|
|
74710
|
+
symbol,
|
|
74711
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74712
|
+
transTo: "SPOT"
|
|
74713
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74714
|
+
type: "MARGIN_MAIN",
|
|
74715
|
+
asset: quote_asset,
|
|
74716
|
+
amount: quote_transfer_amount
|
|
74717
|
+
});
|
|
74718
|
+
}
|
|
74690
74719
|
}
|
|
74691
74720
|
let base_transfer_result = null;
|
|
74721
|
+
let base_transfer_amount = execution_plan.transfer_base_to_spot_amount;
|
|
74692
74722
|
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
74693
|
-
|
|
74694
|
-
|
|
74695
|
-
|
|
74696
|
-
|
|
74697
|
-
|
|
74698
|
-
|
|
74699
|
-
|
|
74723
|
+
base_transfer_amount = await getCrossTransferAmount.call(this, base_asset, execution_plan.transfer_base_to_spot_amount);
|
|
74724
|
+
if (base_transfer_amount > 0) {
|
|
74725
|
+
base_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
74726
|
+
asset: base_asset,
|
|
74727
|
+
amount: base_transfer_amount,
|
|
74728
|
+
symbol,
|
|
74729
|
+
transFrom: "ISOLATED_MARGIN",
|
|
74730
|
+
transTo: "SPOT"
|
|
74731
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
74732
|
+
type: "MARGIN_MAIN",
|
|
74733
|
+
asset: base_asset,
|
|
74734
|
+
amount: base_transfer_amount
|
|
74735
|
+
});
|
|
74736
|
+
}
|
|
74700
74737
|
}
|
|
74701
|
-
const
|
|
74738
|
+
const spot_quote_available = refreshed_snapshot.spot_quote_free + quote_transfer_amount;
|
|
74739
|
+
const requested_max_spot_buy_orders = requested_input.max_spot_buy_orders ?? 5;
|
|
74740
|
+
const sorted_long_orders = [...requested_input.long_orders].sort((a, b) => a.price - b.price);
|
|
74741
|
+
let affordable_spot_buy_orders = 0;
|
|
74742
|
+
let affordable_spot_buy_notional = 0;
|
|
74743
|
+
for (const order of sorted_long_orders.slice(0, requested_max_spot_buy_orders)) {
|
|
74744
|
+
const next_notional = affordable_spot_buy_notional + order.price * order.quantity;
|
|
74745
|
+
if (next_notional <= spot_quote_available + 0.00000001) {
|
|
74746
|
+
affordable_spot_buy_orders += 1;
|
|
74747
|
+
affordable_spot_buy_notional = next_notional;
|
|
74748
|
+
} else {
|
|
74749
|
+
break;
|
|
74750
|
+
}
|
|
74751
|
+
}
|
|
74752
|
+
const placement_plan = buildSpotMarginHedgePlan({
|
|
74753
|
+
borrow_amount: requested_input.borrow_amount,
|
|
74754
|
+
symbol: requested_input.symbol,
|
|
74755
|
+
margin_type: requested_input.margin_type,
|
|
74756
|
+
long_orders: requested_input.long_orders,
|
|
74757
|
+
short_orders: requested_input.short_orders,
|
|
74758
|
+
current_price: refreshed_snapshot.current_price,
|
|
74759
|
+
max_spot_buy_orders: affordable_spot_buy_orders,
|
|
74760
|
+
snapshot: {
|
|
74761
|
+
...refreshed_snapshot,
|
|
74762
|
+
borrowed_quote_amount: refreshed_snapshot.borrowed_quote_amount + execution_plan.borrow_delta,
|
|
74763
|
+
spot_quote_free: spot_quote_available,
|
|
74764
|
+
spot_base_free: refreshed_snapshot.spot_base_free + base_transfer_amount
|
|
74765
|
+
}
|
|
74766
|
+
});
|
|
74767
|
+
const spot_result = placement_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
74702
74768
|
symbol,
|
|
74703
|
-
orders:
|
|
74769
|
+
orders: placement_plan.orders_to_create.spot,
|
|
74704
74770
|
price_places: symbol_formats.price_places,
|
|
74705
74771
|
decimal_places: symbol_formats.decimal_places
|
|
74706
74772
|
}) : [];
|
|
74707
|
-
const margin_result =
|
|
74773
|
+
const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
74708
74774
|
symbol,
|
|
74709
|
-
orders:
|
|
74710
|
-
isIsolated:
|
|
74775
|
+
orders: placement_plan.orders_to_create.margin,
|
|
74776
|
+
isIsolated: is_isolated,
|
|
74711
74777
|
price_places: symbol_formats.price_places,
|
|
74712
74778
|
decimal_places: symbol_formats.decimal_places
|
|
74713
74779
|
}) : [];
|
|
74714
74780
|
return {
|
|
74715
74781
|
...preview,
|
|
74716
|
-
execution_plan
|
|
74782
|
+
execution_plan: {
|
|
74783
|
+
...placement_plan,
|
|
74784
|
+
borrow_amount: execution_plan.borrow_amount,
|
|
74785
|
+
borrow_delta: execution_plan.borrow_delta,
|
|
74786
|
+
transfer_to_spot_amount: quote_transfer_amount,
|
|
74787
|
+
transfer_base_to_spot_amount: base_transfer_amount
|
|
74788
|
+
},
|
|
74717
74789
|
refreshed_snapshot,
|
|
74718
74790
|
execution: {
|
|
74719
74791
|
borrow: borrow_result ? {
|
|
@@ -74723,12 +74795,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
74723
74795
|
} : null,
|
|
74724
74796
|
quote_transfer: quote_transfer_result ? {
|
|
74725
74797
|
asset: quote_asset,
|
|
74726
|
-
amount:
|
|
74798
|
+
amount: quote_transfer_amount,
|
|
74727
74799
|
response: quote_transfer_result
|
|
74728
74800
|
} : null,
|
|
74729
74801
|
base_transfer: base_transfer_result ? {
|
|
74730
74802
|
asset: base_asset,
|
|
74731
|
-
amount:
|
|
74803
|
+
amount: base_transfer_amount,
|
|
74732
74804
|
response: base_transfer_result
|
|
74733
74805
|
} : null,
|
|
74734
74806
|
spot_orders: spot_result,
|
|
@@ -78223,6 +78295,76 @@ async function forceClosePosition2(client, symbol, options) {
|
|
|
78223
78295
|
}
|
|
78224
78296
|
|
|
78225
78297
|
// src/exchange-account.ts
|
|
78298
|
+
function getFormatPrecision(places) {
|
|
78299
|
+
if (!places) {
|
|
78300
|
+
return 0;
|
|
78301
|
+
}
|
|
78302
|
+
const match = places.match(/%\.(\d+)f/);
|
|
78303
|
+
return match ? Number(match[1]) : 0;
|
|
78304
|
+
}
|
|
78305
|
+
function floorToFormat(value2, places) {
|
|
78306
|
+
if (!Number.isFinite(value2) || value2 <= 0) {
|
|
78307
|
+
return 0;
|
|
78308
|
+
}
|
|
78309
|
+
const precision = getFormatPrecision(places);
|
|
78310
|
+
const factor = 10 ** precision;
|
|
78311
|
+
return Math.floor((value2 + Number.EPSILON) * factor) / factor;
|
|
78312
|
+
}
|
|
78313
|
+
|
|
78314
|
+
class TakeProfitReplaceRollbackSucceededError extends Error {
|
|
78315
|
+
status = "rollback_succeeded";
|
|
78316
|
+
restored = true;
|
|
78317
|
+
symbol;
|
|
78318
|
+
kind;
|
|
78319
|
+
takeProfitPrice;
|
|
78320
|
+
quantity;
|
|
78321
|
+
previousTakeProfitPrice;
|
|
78322
|
+
previousQuantity;
|
|
78323
|
+
restoreAttempted;
|
|
78324
|
+
cause;
|
|
78325
|
+
rollbackError;
|
|
78326
|
+
constructor(metadata3) {
|
|
78327
|
+
super("Take profit replacement failed and previous take profit was restored");
|
|
78328
|
+
this.name = "TakeProfitReplaceRollbackSucceededError";
|
|
78329
|
+
this.symbol = metadata3.symbol;
|
|
78330
|
+
this.kind = metadata3.kind;
|
|
78331
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78332
|
+
this.quantity = metadata3.quantity;
|
|
78333
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78334
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78335
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78336
|
+
this.cause = metadata3.cause;
|
|
78337
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78338
|
+
}
|
|
78339
|
+
}
|
|
78340
|
+
|
|
78341
|
+
class TakeProfitReplaceRollbackFailedError extends Error {
|
|
78342
|
+
status = "rollback_failed";
|
|
78343
|
+
restored = false;
|
|
78344
|
+
symbol;
|
|
78345
|
+
kind;
|
|
78346
|
+
takeProfitPrice;
|
|
78347
|
+
quantity;
|
|
78348
|
+
previousTakeProfitPrice;
|
|
78349
|
+
previousQuantity;
|
|
78350
|
+
restoreAttempted;
|
|
78351
|
+
cause;
|
|
78352
|
+
rollbackError;
|
|
78353
|
+
constructor(metadata3) {
|
|
78354
|
+
super("Take profit replacement failed and previous take profit could not be restored");
|
|
78355
|
+
this.name = "TakeProfitReplaceRollbackFailedError";
|
|
78356
|
+
this.symbol = metadata3.symbol;
|
|
78357
|
+
this.kind = metadata3.kind;
|
|
78358
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78359
|
+
this.quantity = metadata3.quantity;
|
|
78360
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78361
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78362
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78363
|
+
this.cause = metadata3.cause;
|
|
78364
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78365
|
+
}
|
|
78366
|
+
}
|
|
78367
|
+
|
|
78226
78368
|
class ExchangeAccount {
|
|
78227
78369
|
instance;
|
|
78228
78370
|
exchange;
|
|
@@ -78319,8 +78461,6 @@ class ExchangeAccount {
|
|
|
78319
78461
|
refresh
|
|
78320
78462
|
});
|
|
78321
78463
|
const raw_active_account = live_exchange_instance.data;
|
|
78322
|
-
console.log("raw_active", raw_active_account);
|
|
78323
|
-
console.log("symbol_config", symbol_config);
|
|
78324
78464
|
const _all = get_active_accounts({
|
|
78325
78465
|
active_account: raw_active_account,
|
|
78326
78466
|
symbol_config
|
|
@@ -78454,6 +78594,132 @@ class ExchangeAccount {
|
|
|
78454
78594
|
});
|
|
78455
78595
|
return await focus_position.cancelOrders(payload);
|
|
78456
78596
|
}
|
|
78597
|
+
async replaceTakeProfitForPosition(payload) {
|
|
78598
|
+
const { symbol, kind, takeProfitPrice, quantity } = payload;
|
|
78599
|
+
const focusPosition = await this.getFocusPosition({
|
|
78600
|
+
symbol,
|
|
78601
|
+
kind,
|
|
78602
|
+
update: true
|
|
78603
|
+
});
|
|
78604
|
+
const position2 = focusPosition.getInstance();
|
|
78605
|
+
const symbol_config = focusPosition.symbol_config;
|
|
78606
|
+
if (!symbol_config) {
|
|
78607
|
+
throw new Error(`Missing symbol config for ${symbol}`);
|
|
78608
|
+
}
|
|
78609
|
+
const normalizedQuantity = floorToFormat(quantity, symbol_config.decimal_places);
|
|
78610
|
+
const minSize = Number(symbol_config.min_size || 0);
|
|
78611
|
+
const previousTakeProfitPrice = Number(position2?.take_profit || 0);
|
|
78612
|
+
const previousQuantity = floorToFormat(Number(position2?.tp_quantity || 0), symbol_config.decimal_places);
|
|
78613
|
+
if (normalizedQuantity <= 0) {
|
|
78614
|
+
return {
|
|
78615
|
+
status: "below_min_lot",
|
|
78616
|
+
symbol,
|
|
78617
|
+
kind,
|
|
78618
|
+
takeProfitPrice,
|
|
78619
|
+
quantity: 0,
|
|
78620
|
+
minSize,
|
|
78621
|
+
reason: "quantity_floored_to_zero"
|
|
78622
|
+
};
|
|
78623
|
+
}
|
|
78624
|
+
if (minSize > 0 && normalizedQuantity < minSize) {
|
|
78625
|
+
return {
|
|
78626
|
+
status: "below_min_lot",
|
|
78627
|
+
symbol,
|
|
78628
|
+
kind,
|
|
78629
|
+
takeProfitPrice,
|
|
78630
|
+
quantity: normalizedQuantity,
|
|
78631
|
+
minSize,
|
|
78632
|
+
reason: "below_venue_minimum"
|
|
78633
|
+
};
|
|
78634
|
+
}
|
|
78635
|
+
const exchangeSeams = this.exchange;
|
|
78636
|
+
if (exchangeSeams.replaceTakeProfitForPosition) {
|
|
78637
|
+
await exchangeSeams.replaceTakeProfitForPosition({
|
|
78638
|
+
symbol,
|
|
78639
|
+
kind,
|
|
78640
|
+
takeProfitPrice,
|
|
78641
|
+
quantity: normalizedQuantity,
|
|
78642
|
+
price_places: symbol_config.price_places,
|
|
78643
|
+
decimal_places: symbol_config.decimal_places,
|
|
78644
|
+
previousTakeProfitPrice,
|
|
78645
|
+
previousQuantity
|
|
78646
|
+
});
|
|
78647
|
+
return {
|
|
78648
|
+
status: "replaced",
|
|
78649
|
+
symbol,
|
|
78650
|
+
kind,
|
|
78651
|
+
takeProfitPrice,
|
|
78652
|
+
quantity: normalizedQuantity,
|
|
78653
|
+
previousTakeProfitPrice,
|
|
78654
|
+
previousQuantity,
|
|
78655
|
+
restoreAttempted: false,
|
|
78656
|
+
via: "native_replace"
|
|
78657
|
+
};
|
|
78658
|
+
}
|
|
78659
|
+
const placeTpWithoutCancelling = exchangeSeams._placeTpOrder;
|
|
78660
|
+
if (!placeTpWithoutCancelling) {
|
|
78661
|
+
throw new Error(`Exchange ${this.instance.exchange} does not expose a TP replace or raw TP placement path`);
|
|
78662
|
+
}
|
|
78663
|
+
if (previousTakeProfitPrice > 0) {
|
|
78664
|
+
await this.cancelOrders({
|
|
78665
|
+
symbol,
|
|
78666
|
+
kind,
|
|
78667
|
+
price: previousTakeProfitPrice
|
|
78668
|
+
});
|
|
78669
|
+
}
|
|
78670
|
+
try {
|
|
78671
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78672
|
+
symbol,
|
|
78673
|
+
tp: takeProfitPrice,
|
|
78674
|
+
kind,
|
|
78675
|
+
quantity: normalizedQuantity,
|
|
78676
|
+
cancel: false,
|
|
78677
|
+
price_places: symbol_config.price_places,
|
|
78678
|
+
decimal_places: symbol_config.decimal_places
|
|
78679
|
+
});
|
|
78680
|
+
return {
|
|
78681
|
+
status: "replaced",
|
|
78682
|
+
symbol,
|
|
78683
|
+
kind,
|
|
78684
|
+
takeProfitPrice,
|
|
78685
|
+
quantity: normalizedQuantity,
|
|
78686
|
+
previousTakeProfitPrice,
|
|
78687
|
+
previousQuantity,
|
|
78688
|
+
restoreAttempted: false
|
|
78689
|
+
};
|
|
78690
|
+
} catch (cause) {
|
|
78691
|
+
const metadata3 = {
|
|
78692
|
+
symbol,
|
|
78693
|
+
kind,
|
|
78694
|
+
takeProfitPrice,
|
|
78695
|
+
quantity: normalizedQuantity,
|
|
78696
|
+
previousTakeProfitPrice,
|
|
78697
|
+
previousQuantity,
|
|
78698
|
+
restoreAttempted: previousTakeProfitPrice > 0 && previousQuantity > 0,
|
|
78699
|
+
cause
|
|
78700
|
+
};
|
|
78701
|
+
if (!metadata3.restoreAttempted) {
|
|
78702
|
+
throw new TakeProfitReplaceRollbackFailedError(metadata3);
|
|
78703
|
+
}
|
|
78704
|
+
try {
|
|
78705
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78706
|
+
symbol,
|
|
78707
|
+
tp: previousTakeProfitPrice,
|
|
78708
|
+
kind,
|
|
78709
|
+
quantity: previousQuantity,
|
|
78710
|
+
cancel: false,
|
|
78711
|
+
price_places: symbol_config.price_places,
|
|
78712
|
+
decimal_places: symbol_config.decimal_places
|
|
78713
|
+
});
|
|
78714
|
+
} catch (rollbackError) {
|
|
78715
|
+
throw new TakeProfitReplaceRollbackFailedError({
|
|
78716
|
+
...metadata3,
|
|
78717
|
+
rollbackError
|
|
78718
|
+
});
|
|
78719
|
+
}
|
|
78720
|
+
throw new TakeProfitReplaceRollbackSucceededError(metadata3);
|
|
78721
|
+
}
|
|
78722
|
+
}
|
|
78457
78723
|
async cancelExchangeOrders(payload) {
|
|
78458
78724
|
return this.exchange.cancelOrders(payload);
|
|
78459
78725
|
}
|
|
@@ -78738,8 +79004,8 @@ class ExchangeAccount {
|
|
|
78738
79004
|
if (payload.trigger && !long_pause_tp && !short_pause_tp && config2) {
|
|
78739
79005
|
return await this.reduceMajorPositionEntry({
|
|
78740
79006
|
symbol,
|
|
78741
|
-
long: config2.long,
|
|
78742
|
-
short: config2.short,
|
|
79007
|
+
long: kind === "long" ? config2.long : undefined,
|
|
79008
|
+
short: kind === "short" ? config2.short : undefined,
|
|
78743
79009
|
trigger: config2.trigger
|
|
78744
79010
|
});
|
|
78745
79011
|
}
|