@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/mcp-server.js
CHANGED
|
@@ -76469,7 +76469,7 @@ function buildSpotMarginHedgePlan(options) {
|
|
|
76469
76469
|
const margin_orders_to_create = options.placement_overrides?.margin || default_margin_orders_to_create;
|
|
76470
76470
|
const borrow_delta = roundNumber2(Math.max(0, options.borrow_amount - options.snapshot.borrowed_quote_amount));
|
|
76471
76471
|
const spot_long_notional = sumNotional(spot_long_orders);
|
|
76472
|
-
const transfer_to_spot_amount = roundNumber2(Math.max(0, spot_long_notional - options.snapshot.spot_quote_free));
|
|
76472
|
+
const transfer_to_spot_amount = roundNumber2(Math.max(0, options.margin_type === "cross" ? options.borrow_amount : spot_long_notional - options.snapshot.spot_quote_free));
|
|
76473
76473
|
const short_quantity = sumQuantity(short_orders);
|
|
76474
76474
|
const transfer_base_to_spot_amount = roundNumber2(Math.max(0, short_quantity - options.snapshot.spot_base_free));
|
|
76475
76475
|
return {
|
|
@@ -76973,17 +76973,17 @@ async function createMarginLimitOrdersParallel(client, symbol, priceFormat, quan
|
|
|
76973
76973
|
return Object.fromEntries(Object.entries(v).filter(([, value2]) => value2 !== undefined));
|
|
76974
76974
|
};
|
|
76975
76975
|
const newOrders = orders.map(createMarginOrder);
|
|
76976
|
-
const
|
|
76977
|
-
const
|
|
76976
|
+
const results = [];
|
|
76977
|
+
for (const orderPayload of newOrders) {
|
|
76978
76978
|
try {
|
|
76979
76979
|
const result = await client.marginAccountNewOrder(orderPayload);
|
|
76980
76980
|
console.log("Margin order result:", result);
|
|
76981
|
-
|
|
76981
|
+
results.push(result);
|
|
76982
76982
|
} catch (error) {
|
|
76983
76983
|
console.error("Error processing margin order:", error);
|
|
76984
76984
|
throw error;
|
|
76985
76985
|
}
|
|
76986
|
-
}
|
|
76986
|
+
}
|
|
76987
76987
|
return results;
|
|
76988
76988
|
}
|
|
76989
76989
|
async function getIsolatedMarginAccountInfo(client, symbol) {
|
|
@@ -78329,24 +78329,25 @@ class BinanceExchange extends BaseExchange {
|
|
|
78329
78329
|
if (!this.main_client) {
|
|
78330
78330
|
throw new Error("Main client not available for spot and margin trading");
|
|
78331
78331
|
}
|
|
78332
|
-
if (payload.margin_type !== "isolated") {
|
|
78333
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated is supported for replay right now.`);
|
|
78334
|
-
}
|
|
78335
78332
|
const symbol = payload.symbol.toUpperCase();
|
|
78336
78333
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
78337
78334
|
const quoteAsset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
78338
78335
|
const baseAsset = symbol.slice(0, symbol.length - quoteAsset.length);
|
|
78339
|
-
const
|
|
78336
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
78337
|
+
const [spot_orders, margin_orders, spot_balances, margin_account] = await Promise.all([
|
|
78340
78338
|
this.getSpotOpenOrders(symbol),
|
|
78341
|
-
this.getMarginOpenOrders(symbol,
|
|
78339
|
+
this.getMarginOpenOrders(symbol, is_isolated),
|
|
78342
78340
|
this.getSpotBalances([baseAsset, quoteAsset]),
|
|
78343
|
-
this.getIsolatedMarginPosition(symbol)
|
|
78341
|
+
is_isolated ? this.getIsolatedMarginPosition(symbol) : this.getCrossMarginAccount()
|
|
78344
78342
|
]);
|
|
78345
78343
|
const current_price = await this.getSpotCurrentPrice(symbol);
|
|
78346
78344
|
const spot_quote_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === quoteAsset);
|
|
78347
78345
|
const spot_base_balance = spot_balances.find((balance) => balance.asset.toUpperCase() === baseAsset);
|
|
78348
|
-
const
|
|
78349
|
-
const
|
|
78346
|
+
const isolated_margin_account = is_isolated ? margin_account : undefined;
|
|
78347
|
+
const cross_margin_account = !is_isolated ? margin_account : undefined;
|
|
78348
|
+
const isolated_asset = isolated_margin_account?.assets?.[0];
|
|
78349
|
+
const cross_quote_asset = !is_isolated ? cross_margin_account?.userAssets?.find((asset) => asset.asset?.toUpperCase?.() === quoteAsset) : undefined;
|
|
78350
|
+
const borrowed_quote_amount = parseFloat(isolated_asset?.quoteAsset?.borrowed?.toString?.() || cross_quote_asset?.borrowed?.toString?.() || "0");
|
|
78350
78351
|
return {
|
|
78351
78352
|
symbol,
|
|
78352
78353
|
margin_type: payload.margin_type,
|
|
@@ -78359,7 +78360,8 @@ class BinanceExchange extends BaseExchange {
|
|
|
78359
78360
|
margin_orders,
|
|
78360
78361
|
current_price,
|
|
78361
78362
|
spot_balances,
|
|
78362
|
-
isolated_margin_position
|
|
78363
|
+
isolated_margin_position: isolated_margin_account,
|
|
78364
|
+
cross_margin_account
|
|
78363
78365
|
};
|
|
78364
78366
|
}
|
|
78365
78367
|
async previewSpotMarginHedge(payload) {
|
|
@@ -78416,14 +78418,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
78416
78418
|
if (!this.main_client) {
|
|
78417
78419
|
throw new Error("Main client not available for spot and margin trading");
|
|
78418
78420
|
}
|
|
78419
|
-
if (payload.margin_type !== "isolated") {
|
|
78420
|
-
throw new Error(`Unsupported margin type: ${payload.margin_type}. Only isolated placement is supported right now.`);
|
|
78421
|
-
}
|
|
78422
78421
|
const symbol = payload.symbol.toUpperCase();
|
|
78423
78422
|
const quoteAssets = ["USDT", "USDC", "BUSD", "BTC", "ETH"];
|
|
78424
78423
|
const quote_asset = quoteAssets.find((asset) => symbol.endsWith(asset)) || symbol.slice(-4);
|
|
78425
78424
|
const base_asset = symbol.slice(0, symbol.length - quote_asset.length);
|
|
78426
78425
|
const requested_input = preview.requested_input;
|
|
78426
|
+
const is_isolated = payload.margin_type === "isolated";
|
|
78427
78427
|
if (preview.snapshot.spot_orders.length > 0) {
|
|
78428
78428
|
await this.cancelSpotOrders(symbol, preview.snapshot.spot_orders.map((order) => ({
|
|
78429
78429
|
orderId: order.orderId || order.order_id || order.id,
|
|
@@ -78431,7 +78431,7 @@ class BinanceExchange extends BaseExchange {
|
|
|
78431
78431
|
})));
|
|
78432
78432
|
}
|
|
78433
78433
|
if (preview.snapshot.margin_orders.length > 0) {
|
|
78434
|
-
await this.cancelMarginOrders(symbol,
|
|
78434
|
+
await this.cancelMarginOrders(symbol, is_isolated, preview.snapshot.margin_orders.map((order) => ({
|
|
78435
78435
|
orderId: order.orderId || order.order_id || order.id,
|
|
78436
78436
|
origClientOrderId: order.origClientOrderId
|
|
78437
78437
|
})));
|
|
@@ -78453,50 +78453,120 @@ class BinanceExchange extends BaseExchange {
|
|
|
78453
78453
|
});
|
|
78454
78454
|
let borrow_result = null;
|
|
78455
78455
|
if (execution_plan.borrow_delta > 0) {
|
|
78456
|
-
|
|
78456
|
+
const borrow_payload = {
|
|
78457
78457
|
asset: quote_asset,
|
|
78458
78458
|
amount: execution_plan.borrow_delta,
|
|
78459
|
-
isIsolated: "TRUE",
|
|
78460
|
-
symbol,
|
|
78461
78459
|
type: "BORROW"
|
|
78460
|
+
};
|
|
78461
|
+
if (is_isolated) {
|
|
78462
|
+
borrow_payload.isIsolated = "TRUE";
|
|
78463
|
+
borrow_payload.symbol = symbol;
|
|
78464
|
+
}
|
|
78465
|
+
borrow_result = await this.main_client.submitMarginAccountBorrowRepay(borrow_payload);
|
|
78466
|
+
}
|
|
78467
|
+
async function getCrossTransferAmount(asset, amount) {
|
|
78468
|
+
if (is_isolated) {
|
|
78469
|
+
return amount;
|
|
78470
|
+
}
|
|
78471
|
+
if (!this.main_client || typeof this.main_client.queryMaxTransferOutAmount !== "function") {
|
|
78472
|
+
return amount;
|
|
78473
|
+
}
|
|
78474
|
+
const response = await this.main_client.queryMaxTransferOutAmount({
|
|
78475
|
+
asset
|
|
78462
78476
|
});
|
|
78477
|
+
const allowed_amount = parseFloat(response?.amount?.toString?.() || "0");
|
|
78478
|
+
if (!Number.isFinite(allowed_amount)) {
|
|
78479
|
+
return amount;
|
|
78480
|
+
}
|
|
78481
|
+
return Math.max(0, Math.min(amount, allowed_amount));
|
|
78463
78482
|
}
|
|
78464
78483
|
let quote_transfer_result = null;
|
|
78484
|
+
let quote_transfer_amount = execution_plan.transfer_to_spot_amount;
|
|
78465
78485
|
if (execution_plan.transfer_to_spot_amount > 0) {
|
|
78466
|
-
|
|
78467
|
-
|
|
78468
|
-
|
|
78469
|
-
|
|
78470
|
-
|
|
78471
|
-
|
|
78472
|
-
|
|
78486
|
+
quote_transfer_amount = await getCrossTransferAmount.call(this, quote_asset, execution_plan.transfer_to_spot_amount);
|
|
78487
|
+
if (quote_transfer_amount > 0) {
|
|
78488
|
+
quote_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
78489
|
+
asset: quote_asset,
|
|
78490
|
+
amount: quote_transfer_amount,
|
|
78491
|
+
symbol,
|
|
78492
|
+
transFrom: "ISOLATED_MARGIN",
|
|
78493
|
+
transTo: "SPOT"
|
|
78494
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
78495
|
+
type: "MARGIN_MAIN",
|
|
78496
|
+
asset: quote_asset,
|
|
78497
|
+
amount: quote_transfer_amount
|
|
78498
|
+
});
|
|
78499
|
+
}
|
|
78473
78500
|
}
|
|
78474
78501
|
let base_transfer_result = null;
|
|
78502
|
+
let base_transfer_amount = execution_plan.transfer_base_to_spot_amount;
|
|
78475
78503
|
if (execution_plan.transfer_base_to_spot_amount > 0) {
|
|
78476
|
-
|
|
78477
|
-
|
|
78478
|
-
|
|
78479
|
-
|
|
78480
|
-
|
|
78481
|
-
|
|
78482
|
-
|
|
78504
|
+
base_transfer_amount = await getCrossTransferAmount.call(this, base_asset, execution_plan.transfer_base_to_spot_amount);
|
|
78505
|
+
if (base_transfer_amount > 0) {
|
|
78506
|
+
base_transfer_result = is_isolated ? await this.main_client.isolatedMarginAccountTransfer({
|
|
78507
|
+
asset: base_asset,
|
|
78508
|
+
amount: base_transfer_amount,
|
|
78509
|
+
symbol,
|
|
78510
|
+
transFrom: "ISOLATED_MARGIN",
|
|
78511
|
+
transTo: "SPOT"
|
|
78512
|
+
}) : await this.main_client.submitUniversalTransfer({
|
|
78513
|
+
type: "MARGIN_MAIN",
|
|
78514
|
+
asset: base_asset,
|
|
78515
|
+
amount: base_transfer_amount
|
|
78516
|
+
});
|
|
78517
|
+
}
|
|
78483
78518
|
}
|
|
78484
|
-
const
|
|
78519
|
+
const spot_quote_available = refreshed_snapshot.spot_quote_free + quote_transfer_amount;
|
|
78520
|
+
const requested_max_spot_buy_orders = requested_input.max_spot_buy_orders ?? 5;
|
|
78521
|
+
const sorted_long_orders = [...requested_input.long_orders].sort((a, b) => a.price - b.price);
|
|
78522
|
+
let affordable_spot_buy_orders = 0;
|
|
78523
|
+
let affordable_spot_buy_notional = 0;
|
|
78524
|
+
for (const order of sorted_long_orders.slice(0, requested_max_spot_buy_orders)) {
|
|
78525
|
+
const next_notional = affordable_spot_buy_notional + order.price * order.quantity;
|
|
78526
|
+
if (next_notional <= spot_quote_available + 0.00000001) {
|
|
78527
|
+
affordable_spot_buy_orders += 1;
|
|
78528
|
+
affordable_spot_buy_notional = next_notional;
|
|
78529
|
+
} else {
|
|
78530
|
+
break;
|
|
78531
|
+
}
|
|
78532
|
+
}
|
|
78533
|
+
const placement_plan = buildSpotMarginHedgePlan({
|
|
78534
|
+
borrow_amount: requested_input.borrow_amount,
|
|
78535
|
+
symbol: requested_input.symbol,
|
|
78536
|
+
margin_type: requested_input.margin_type,
|
|
78537
|
+
long_orders: requested_input.long_orders,
|
|
78538
|
+
short_orders: requested_input.short_orders,
|
|
78539
|
+
current_price: refreshed_snapshot.current_price,
|
|
78540
|
+
max_spot_buy_orders: affordable_spot_buy_orders,
|
|
78541
|
+
snapshot: {
|
|
78542
|
+
...refreshed_snapshot,
|
|
78543
|
+
borrowed_quote_amount: refreshed_snapshot.borrowed_quote_amount + execution_plan.borrow_delta,
|
|
78544
|
+
spot_quote_free: spot_quote_available,
|
|
78545
|
+
spot_base_free: refreshed_snapshot.spot_base_free + base_transfer_amount
|
|
78546
|
+
}
|
|
78547
|
+
});
|
|
78548
|
+
const spot_result = placement_plan.orders_to_create.spot.length > 0 ? await this.createSpotLimitOrders({
|
|
78485
78549
|
symbol,
|
|
78486
|
-
orders:
|
|
78550
|
+
orders: placement_plan.orders_to_create.spot,
|
|
78487
78551
|
price_places: symbol_formats.price_places,
|
|
78488
78552
|
decimal_places: symbol_formats.decimal_places
|
|
78489
78553
|
}) : [];
|
|
78490
|
-
const margin_result =
|
|
78554
|
+
const margin_result = placement_plan.orders_to_create.margin.length > 0 ? await this.createMarginLimitOrders({
|
|
78491
78555
|
symbol,
|
|
78492
|
-
orders:
|
|
78493
|
-
isIsolated:
|
|
78556
|
+
orders: placement_plan.orders_to_create.margin,
|
|
78557
|
+
isIsolated: is_isolated,
|
|
78494
78558
|
price_places: symbol_formats.price_places,
|
|
78495
78559
|
decimal_places: symbol_formats.decimal_places
|
|
78496
78560
|
}) : [];
|
|
78497
78561
|
return {
|
|
78498
78562
|
...preview,
|
|
78499
|
-
execution_plan
|
|
78563
|
+
execution_plan: {
|
|
78564
|
+
...placement_plan,
|
|
78565
|
+
borrow_amount: execution_plan.borrow_amount,
|
|
78566
|
+
borrow_delta: execution_plan.borrow_delta,
|
|
78567
|
+
transfer_to_spot_amount: quote_transfer_amount,
|
|
78568
|
+
transfer_base_to_spot_amount: base_transfer_amount
|
|
78569
|
+
},
|
|
78500
78570
|
refreshed_snapshot,
|
|
78501
78571
|
execution: {
|
|
78502
78572
|
borrow: borrow_result ? {
|
|
@@ -78506,12 +78576,12 @@ class BinanceExchange extends BaseExchange {
|
|
|
78506
78576
|
} : null,
|
|
78507
78577
|
quote_transfer: quote_transfer_result ? {
|
|
78508
78578
|
asset: quote_asset,
|
|
78509
|
-
amount:
|
|
78579
|
+
amount: quote_transfer_amount,
|
|
78510
78580
|
response: quote_transfer_result
|
|
78511
78581
|
} : null,
|
|
78512
78582
|
base_transfer: base_transfer_result ? {
|
|
78513
78583
|
asset: base_asset,
|
|
78514
|
-
amount:
|
|
78584
|
+
amount: base_transfer_amount,
|
|
78515
78585
|
response: base_transfer_result
|
|
78516
78586
|
} : null,
|
|
78517
78587
|
spot_orders: spot_result,
|
|
@@ -82006,6 +82076,76 @@ async function forceClosePosition2(client, symbol, options) {
|
|
|
82006
82076
|
}
|
|
82007
82077
|
|
|
82008
82078
|
// src/exchange-account.ts
|
|
82079
|
+
function getFormatPrecision(places) {
|
|
82080
|
+
if (!places) {
|
|
82081
|
+
return 0;
|
|
82082
|
+
}
|
|
82083
|
+
const match = places.match(/%\.(\d+)f/);
|
|
82084
|
+
return match ? Number(match[1]) : 0;
|
|
82085
|
+
}
|
|
82086
|
+
function floorToFormat(value2, places) {
|
|
82087
|
+
if (!Number.isFinite(value2) || value2 <= 0) {
|
|
82088
|
+
return 0;
|
|
82089
|
+
}
|
|
82090
|
+
const precision = getFormatPrecision(places);
|
|
82091
|
+
const factor = 10 ** precision;
|
|
82092
|
+
return Math.floor((value2 + Number.EPSILON) * factor) / factor;
|
|
82093
|
+
}
|
|
82094
|
+
|
|
82095
|
+
class TakeProfitReplaceRollbackSucceededError extends Error {
|
|
82096
|
+
status = "rollback_succeeded";
|
|
82097
|
+
restored = true;
|
|
82098
|
+
symbol;
|
|
82099
|
+
kind;
|
|
82100
|
+
takeProfitPrice;
|
|
82101
|
+
quantity;
|
|
82102
|
+
previousTakeProfitPrice;
|
|
82103
|
+
previousQuantity;
|
|
82104
|
+
restoreAttempted;
|
|
82105
|
+
cause;
|
|
82106
|
+
rollbackError;
|
|
82107
|
+
constructor(metadata3) {
|
|
82108
|
+
super("Take profit replacement failed and previous take profit was restored");
|
|
82109
|
+
this.name = "TakeProfitReplaceRollbackSucceededError";
|
|
82110
|
+
this.symbol = metadata3.symbol;
|
|
82111
|
+
this.kind = metadata3.kind;
|
|
82112
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
82113
|
+
this.quantity = metadata3.quantity;
|
|
82114
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
82115
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
82116
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
82117
|
+
this.cause = metadata3.cause;
|
|
82118
|
+
this.rollbackError = metadata3.rollbackError;
|
|
82119
|
+
}
|
|
82120
|
+
}
|
|
82121
|
+
|
|
82122
|
+
class TakeProfitReplaceRollbackFailedError extends Error {
|
|
82123
|
+
status = "rollback_failed";
|
|
82124
|
+
restored = false;
|
|
82125
|
+
symbol;
|
|
82126
|
+
kind;
|
|
82127
|
+
takeProfitPrice;
|
|
82128
|
+
quantity;
|
|
82129
|
+
previousTakeProfitPrice;
|
|
82130
|
+
previousQuantity;
|
|
82131
|
+
restoreAttempted;
|
|
82132
|
+
cause;
|
|
82133
|
+
rollbackError;
|
|
82134
|
+
constructor(metadata3) {
|
|
82135
|
+
super("Take profit replacement failed and previous take profit could not be restored");
|
|
82136
|
+
this.name = "TakeProfitReplaceRollbackFailedError";
|
|
82137
|
+
this.symbol = metadata3.symbol;
|
|
82138
|
+
this.kind = metadata3.kind;
|
|
82139
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
82140
|
+
this.quantity = metadata3.quantity;
|
|
82141
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
82142
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
82143
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
82144
|
+
this.cause = metadata3.cause;
|
|
82145
|
+
this.rollbackError = metadata3.rollbackError;
|
|
82146
|
+
}
|
|
82147
|
+
}
|
|
82148
|
+
|
|
82009
82149
|
class ExchangeAccount {
|
|
82010
82150
|
instance;
|
|
82011
82151
|
exchange;
|
|
@@ -82102,8 +82242,6 @@ class ExchangeAccount {
|
|
|
82102
82242
|
refresh
|
|
82103
82243
|
});
|
|
82104
82244
|
const raw_active_account = live_exchange_instance.data;
|
|
82105
|
-
console.log("raw_active", raw_active_account);
|
|
82106
|
-
console.log("symbol_config", symbol_config);
|
|
82107
82245
|
const _all = get_active_accounts({
|
|
82108
82246
|
active_account: raw_active_account,
|
|
82109
82247
|
symbol_config
|
|
@@ -82237,6 +82375,132 @@ class ExchangeAccount {
|
|
|
82237
82375
|
});
|
|
82238
82376
|
return await focus_position.cancelOrders(payload);
|
|
82239
82377
|
}
|
|
82378
|
+
async replaceTakeProfitForPosition(payload) {
|
|
82379
|
+
const { symbol, kind, takeProfitPrice, quantity } = payload;
|
|
82380
|
+
const focusPosition = await this.getFocusPosition({
|
|
82381
|
+
symbol,
|
|
82382
|
+
kind,
|
|
82383
|
+
update: true
|
|
82384
|
+
});
|
|
82385
|
+
const position2 = focusPosition.getInstance();
|
|
82386
|
+
const symbol_config = focusPosition.symbol_config;
|
|
82387
|
+
if (!symbol_config) {
|
|
82388
|
+
throw new Error(`Missing symbol config for ${symbol}`);
|
|
82389
|
+
}
|
|
82390
|
+
const normalizedQuantity = floorToFormat(quantity, symbol_config.decimal_places);
|
|
82391
|
+
const minSize = Number(symbol_config.min_size || 0);
|
|
82392
|
+
const previousTakeProfitPrice = Number(position2?.take_profit || 0);
|
|
82393
|
+
const previousQuantity = floorToFormat(Number(position2?.tp_quantity || 0), symbol_config.decimal_places);
|
|
82394
|
+
if (normalizedQuantity <= 0) {
|
|
82395
|
+
return {
|
|
82396
|
+
status: "below_min_lot",
|
|
82397
|
+
symbol,
|
|
82398
|
+
kind,
|
|
82399
|
+
takeProfitPrice,
|
|
82400
|
+
quantity: 0,
|
|
82401
|
+
minSize,
|
|
82402
|
+
reason: "quantity_floored_to_zero"
|
|
82403
|
+
};
|
|
82404
|
+
}
|
|
82405
|
+
if (minSize > 0 && normalizedQuantity < minSize) {
|
|
82406
|
+
return {
|
|
82407
|
+
status: "below_min_lot",
|
|
82408
|
+
symbol,
|
|
82409
|
+
kind,
|
|
82410
|
+
takeProfitPrice,
|
|
82411
|
+
quantity: normalizedQuantity,
|
|
82412
|
+
minSize,
|
|
82413
|
+
reason: "below_venue_minimum"
|
|
82414
|
+
};
|
|
82415
|
+
}
|
|
82416
|
+
const exchangeSeams = this.exchange;
|
|
82417
|
+
if (exchangeSeams.replaceTakeProfitForPosition) {
|
|
82418
|
+
await exchangeSeams.replaceTakeProfitForPosition({
|
|
82419
|
+
symbol,
|
|
82420
|
+
kind,
|
|
82421
|
+
takeProfitPrice,
|
|
82422
|
+
quantity: normalizedQuantity,
|
|
82423
|
+
price_places: symbol_config.price_places,
|
|
82424
|
+
decimal_places: symbol_config.decimal_places,
|
|
82425
|
+
previousTakeProfitPrice,
|
|
82426
|
+
previousQuantity
|
|
82427
|
+
});
|
|
82428
|
+
return {
|
|
82429
|
+
status: "replaced",
|
|
82430
|
+
symbol,
|
|
82431
|
+
kind,
|
|
82432
|
+
takeProfitPrice,
|
|
82433
|
+
quantity: normalizedQuantity,
|
|
82434
|
+
previousTakeProfitPrice,
|
|
82435
|
+
previousQuantity,
|
|
82436
|
+
restoreAttempted: false,
|
|
82437
|
+
via: "native_replace"
|
|
82438
|
+
};
|
|
82439
|
+
}
|
|
82440
|
+
const placeTpWithoutCancelling = exchangeSeams._placeTpOrder;
|
|
82441
|
+
if (!placeTpWithoutCancelling) {
|
|
82442
|
+
throw new Error(`Exchange ${this.instance.exchange} does not expose a TP replace or raw TP placement path`);
|
|
82443
|
+
}
|
|
82444
|
+
if (previousTakeProfitPrice > 0) {
|
|
82445
|
+
await this.cancelOrders({
|
|
82446
|
+
symbol,
|
|
82447
|
+
kind,
|
|
82448
|
+
price: previousTakeProfitPrice
|
|
82449
|
+
});
|
|
82450
|
+
}
|
|
82451
|
+
try {
|
|
82452
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
82453
|
+
symbol,
|
|
82454
|
+
tp: takeProfitPrice,
|
|
82455
|
+
kind,
|
|
82456
|
+
quantity: normalizedQuantity,
|
|
82457
|
+
cancel: false,
|
|
82458
|
+
price_places: symbol_config.price_places,
|
|
82459
|
+
decimal_places: symbol_config.decimal_places
|
|
82460
|
+
});
|
|
82461
|
+
return {
|
|
82462
|
+
status: "replaced",
|
|
82463
|
+
symbol,
|
|
82464
|
+
kind,
|
|
82465
|
+
takeProfitPrice,
|
|
82466
|
+
quantity: normalizedQuantity,
|
|
82467
|
+
previousTakeProfitPrice,
|
|
82468
|
+
previousQuantity,
|
|
82469
|
+
restoreAttempted: false
|
|
82470
|
+
};
|
|
82471
|
+
} catch (cause) {
|
|
82472
|
+
const metadata3 = {
|
|
82473
|
+
symbol,
|
|
82474
|
+
kind,
|
|
82475
|
+
takeProfitPrice,
|
|
82476
|
+
quantity: normalizedQuantity,
|
|
82477
|
+
previousTakeProfitPrice,
|
|
82478
|
+
previousQuantity,
|
|
82479
|
+
restoreAttempted: previousTakeProfitPrice > 0 && previousQuantity > 0,
|
|
82480
|
+
cause
|
|
82481
|
+
};
|
|
82482
|
+
if (!metadata3.restoreAttempted) {
|
|
82483
|
+
throw new TakeProfitReplaceRollbackFailedError(metadata3);
|
|
82484
|
+
}
|
|
82485
|
+
try {
|
|
82486
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
82487
|
+
symbol,
|
|
82488
|
+
tp: previousTakeProfitPrice,
|
|
82489
|
+
kind,
|
|
82490
|
+
quantity: previousQuantity,
|
|
82491
|
+
cancel: false,
|
|
82492
|
+
price_places: symbol_config.price_places,
|
|
82493
|
+
decimal_places: symbol_config.decimal_places
|
|
82494
|
+
});
|
|
82495
|
+
} catch (rollbackError) {
|
|
82496
|
+
throw new TakeProfitReplaceRollbackFailedError({
|
|
82497
|
+
...metadata3,
|
|
82498
|
+
rollbackError
|
|
82499
|
+
});
|
|
82500
|
+
}
|
|
82501
|
+
throw new TakeProfitReplaceRollbackSucceededError(metadata3);
|
|
82502
|
+
}
|
|
82503
|
+
}
|
|
82240
82504
|
async cancelExchangeOrders(payload) {
|
|
82241
82505
|
return this.exchange.cancelOrders(payload);
|
|
82242
82506
|
}
|
|
@@ -82521,8 +82785,8 @@ class ExchangeAccount {
|
|
|
82521
82785
|
if (payload.trigger && !long_pause_tp && !short_pause_tp && config2) {
|
|
82522
82786
|
return await this.reduceMajorPositionEntry({
|
|
82523
82787
|
symbol,
|
|
82524
|
-
long: config2.long,
|
|
82525
|
-
short: config2.short,
|
|
82788
|
+
long: kind === "long" ? config2.long : undefined,
|
|
82789
|
+
short: kind === "short" ? config2.short : undefined,
|
|
82526
82790
|
trigger: config2.trigger
|
|
82527
82791
|
});
|
|
82528
82792
|
}
|