@gbozee/ultimate 0.0.2-next.75 → 0.0.2-next.78
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 +207 -6
- package/dist/index.d.ts +96 -1
- package/dist/index.js +207 -6
- package/dist/mcp-server.cjs +205 -6
- package/dist/mcp-server.js +205 -6
- 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
|
|
|
@@ -73726,7 +73728,13 @@ function buildPosition(position2, orders, options) {
|
|
|
73726
73728
|
{ price: entry, quantity },
|
|
73727
73729
|
...limitOrders.map((o) => ({ price: o.price, quantity: o.qty }))
|
|
73728
73730
|
], decimal_places, price_places);
|
|
73729
|
-
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];
|
|
73730
73738
|
const tpOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((x) => x.kind === "long" ? x.side === "sell" : x.side === "buy")[0];
|
|
73731
73739
|
const avg_entry = avg.entry;
|
|
73732
73740
|
const stop_loss = stopOrders?.triggerPrice || 0;
|
|
@@ -78395,6 +78403,76 @@ async function forceClosePosition2(client, symbol, options) {
|
|
|
78395
78403
|
}
|
|
78396
78404
|
|
|
78397
78405
|
// src/exchange-account.ts
|
|
78406
|
+
function getFormatPrecision(places) {
|
|
78407
|
+
if (!places) {
|
|
78408
|
+
return 0;
|
|
78409
|
+
}
|
|
78410
|
+
const match = places.match(/%\.(\d+)f/);
|
|
78411
|
+
return match ? Number(match[1]) : 0;
|
|
78412
|
+
}
|
|
78413
|
+
function floorToFormat(value2, places) {
|
|
78414
|
+
if (!Number.isFinite(value2) || value2 <= 0) {
|
|
78415
|
+
return 0;
|
|
78416
|
+
}
|
|
78417
|
+
const precision = getFormatPrecision(places);
|
|
78418
|
+
const factor = 10 ** precision;
|
|
78419
|
+
return Math.floor((value2 + Number.EPSILON) * factor) / factor;
|
|
78420
|
+
}
|
|
78421
|
+
|
|
78422
|
+
class TakeProfitReplaceRollbackSucceededError extends Error {
|
|
78423
|
+
status = "rollback_succeeded";
|
|
78424
|
+
restored = true;
|
|
78425
|
+
symbol;
|
|
78426
|
+
kind;
|
|
78427
|
+
takeProfitPrice;
|
|
78428
|
+
quantity;
|
|
78429
|
+
previousTakeProfitPrice;
|
|
78430
|
+
previousQuantity;
|
|
78431
|
+
restoreAttempted;
|
|
78432
|
+
cause;
|
|
78433
|
+
rollbackError;
|
|
78434
|
+
constructor(metadata3) {
|
|
78435
|
+
super("Take profit replacement failed and previous take profit was restored");
|
|
78436
|
+
this.name = "TakeProfitReplaceRollbackSucceededError";
|
|
78437
|
+
this.symbol = metadata3.symbol;
|
|
78438
|
+
this.kind = metadata3.kind;
|
|
78439
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78440
|
+
this.quantity = metadata3.quantity;
|
|
78441
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78442
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78443
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78444
|
+
this.cause = metadata3.cause;
|
|
78445
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78446
|
+
}
|
|
78447
|
+
}
|
|
78448
|
+
|
|
78449
|
+
class TakeProfitReplaceRollbackFailedError extends Error {
|
|
78450
|
+
status = "rollback_failed";
|
|
78451
|
+
restored = false;
|
|
78452
|
+
symbol;
|
|
78453
|
+
kind;
|
|
78454
|
+
takeProfitPrice;
|
|
78455
|
+
quantity;
|
|
78456
|
+
previousTakeProfitPrice;
|
|
78457
|
+
previousQuantity;
|
|
78458
|
+
restoreAttempted;
|
|
78459
|
+
cause;
|
|
78460
|
+
rollbackError;
|
|
78461
|
+
constructor(metadata3) {
|
|
78462
|
+
super("Take profit replacement failed and previous take profit could not be restored");
|
|
78463
|
+
this.name = "TakeProfitReplaceRollbackFailedError";
|
|
78464
|
+
this.symbol = metadata3.symbol;
|
|
78465
|
+
this.kind = metadata3.kind;
|
|
78466
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78467
|
+
this.quantity = metadata3.quantity;
|
|
78468
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78469
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78470
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78471
|
+
this.cause = metadata3.cause;
|
|
78472
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78473
|
+
}
|
|
78474
|
+
}
|
|
78475
|
+
|
|
78398
78476
|
class ExchangeAccount {
|
|
78399
78477
|
instance;
|
|
78400
78478
|
exchange;
|
|
@@ -78491,8 +78569,6 @@ class ExchangeAccount {
|
|
|
78491
78569
|
refresh
|
|
78492
78570
|
});
|
|
78493
78571
|
const raw_active_account = live_exchange_instance.data;
|
|
78494
|
-
console.log("raw_active", raw_active_account);
|
|
78495
|
-
console.log("symbol_config", symbol_config);
|
|
78496
78572
|
const _all = get_active_accounts({
|
|
78497
78573
|
active_account: raw_active_account,
|
|
78498
78574
|
symbol_config
|
|
@@ -78508,7 +78584,6 @@ class ExchangeAccount {
|
|
|
78508
78584
|
refresh: live_refresh,
|
|
78509
78585
|
symbol
|
|
78510
78586
|
});
|
|
78511
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
78512
78587
|
if (leverage) {
|
|
78513
78588
|
this.exchange.setLeverage({ symbol, leverage });
|
|
78514
78589
|
}
|
|
@@ -78626,6 +78701,132 @@ class ExchangeAccount {
|
|
|
78626
78701
|
});
|
|
78627
78702
|
return await focus_position.cancelOrders(payload);
|
|
78628
78703
|
}
|
|
78704
|
+
async replaceTakeProfitForPosition(payload) {
|
|
78705
|
+
const { symbol, kind, takeProfitPrice, quantity } = payload;
|
|
78706
|
+
const focusPosition = await this.getFocusPosition({
|
|
78707
|
+
symbol,
|
|
78708
|
+
kind,
|
|
78709
|
+
update: true
|
|
78710
|
+
});
|
|
78711
|
+
const position2 = focusPosition.getInstance();
|
|
78712
|
+
const symbol_config = focusPosition.symbol_config;
|
|
78713
|
+
if (!symbol_config) {
|
|
78714
|
+
throw new Error(`Missing symbol config for ${symbol}`);
|
|
78715
|
+
}
|
|
78716
|
+
const normalizedQuantity = floorToFormat(quantity, symbol_config.decimal_places);
|
|
78717
|
+
const minSize = Number(symbol_config.min_size || 0);
|
|
78718
|
+
const previousTakeProfitPrice = Number(position2?.take_profit || 0);
|
|
78719
|
+
const previousQuantity = floorToFormat(Number(position2?.tp_quantity || 0), symbol_config.decimal_places);
|
|
78720
|
+
if (normalizedQuantity <= 0) {
|
|
78721
|
+
return {
|
|
78722
|
+
status: "below_min_lot",
|
|
78723
|
+
symbol,
|
|
78724
|
+
kind,
|
|
78725
|
+
takeProfitPrice,
|
|
78726
|
+
quantity: 0,
|
|
78727
|
+
minSize,
|
|
78728
|
+
reason: "quantity_floored_to_zero"
|
|
78729
|
+
};
|
|
78730
|
+
}
|
|
78731
|
+
if (minSize > 0 && normalizedQuantity < minSize) {
|
|
78732
|
+
return {
|
|
78733
|
+
status: "below_min_lot",
|
|
78734
|
+
symbol,
|
|
78735
|
+
kind,
|
|
78736
|
+
takeProfitPrice,
|
|
78737
|
+
quantity: normalizedQuantity,
|
|
78738
|
+
minSize,
|
|
78739
|
+
reason: "below_venue_minimum"
|
|
78740
|
+
};
|
|
78741
|
+
}
|
|
78742
|
+
const exchangeSeams = this.exchange;
|
|
78743
|
+
if (exchangeSeams.replaceTakeProfitForPosition) {
|
|
78744
|
+
await exchangeSeams.replaceTakeProfitForPosition({
|
|
78745
|
+
symbol,
|
|
78746
|
+
kind,
|
|
78747
|
+
takeProfitPrice,
|
|
78748
|
+
quantity: normalizedQuantity,
|
|
78749
|
+
price_places: symbol_config.price_places,
|
|
78750
|
+
decimal_places: symbol_config.decimal_places,
|
|
78751
|
+
previousTakeProfitPrice,
|
|
78752
|
+
previousQuantity
|
|
78753
|
+
});
|
|
78754
|
+
return {
|
|
78755
|
+
status: "replaced",
|
|
78756
|
+
symbol,
|
|
78757
|
+
kind,
|
|
78758
|
+
takeProfitPrice,
|
|
78759
|
+
quantity: normalizedQuantity,
|
|
78760
|
+
previousTakeProfitPrice,
|
|
78761
|
+
previousQuantity,
|
|
78762
|
+
restoreAttempted: false,
|
|
78763
|
+
via: "native_replace"
|
|
78764
|
+
};
|
|
78765
|
+
}
|
|
78766
|
+
const placeTpWithoutCancelling = exchangeSeams._placeTpOrder;
|
|
78767
|
+
if (!placeTpWithoutCancelling) {
|
|
78768
|
+
throw new Error(`Exchange ${this.instance.exchange} does not expose a TP replace or raw TP placement path`);
|
|
78769
|
+
}
|
|
78770
|
+
if (previousTakeProfitPrice > 0) {
|
|
78771
|
+
await this.cancelOrders({
|
|
78772
|
+
symbol,
|
|
78773
|
+
kind,
|
|
78774
|
+
price: previousTakeProfitPrice
|
|
78775
|
+
});
|
|
78776
|
+
}
|
|
78777
|
+
try {
|
|
78778
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78779
|
+
symbol,
|
|
78780
|
+
tp: takeProfitPrice,
|
|
78781
|
+
kind,
|
|
78782
|
+
quantity: normalizedQuantity,
|
|
78783
|
+
cancel: false,
|
|
78784
|
+
price_places: symbol_config.price_places,
|
|
78785
|
+
decimal_places: symbol_config.decimal_places
|
|
78786
|
+
});
|
|
78787
|
+
return {
|
|
78788
|
+
status: "replaced",
|
|
78789
|
+
symbol,
|
|
78790
|
+
kind,
|
|
78791
|
+
takeProfitPrice,
|
|
78792
|
+
quantity: normalizedQuantity,
|
|
78793
|
+
previousTakeProfitPrice,
|
|
78794
|
+
previousQuantity,
|
|
78795
|
+
restoreAttempted: false
|
|
78796
|
+
};
|
|
78797
|
+
} catch (cause) {
|
|
78798
|
+
const metadata3 = {
|
|
78799
|
+
symbol,
|
|
78800
|
+
kind,
|
|
78801
|
+
takeProfitPrice,
|
|
78802
|
+
quantity: normalizedQuantity,
|
|
78803
|
+
previousTakeProfitPrice,
|
|
78804
|
+
previousQuantity,
|
|
78805
|
+
restoreAttempted: previousTakeProfitPrice > 0 && previousQuantity > 0,
|
|
78806
|
+
cause
|
|
78807
|
+
};
|
|
78808
|
+
if (!metadata3.restoreAttempted) {
|
|
78809
|
+
throw new TakeProfitReplaceRollbackFailedError(metadata3);
|
|
78810
|
+
}
|
|
78811
|
+
try {
|
|
78812
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78813
|
+
symbol,
|
|
78814
|
+
tp: previousTakeProfitPrice,
|
|
78815
|
+
kind,
|
|
78816
|
+
quantity: previousQuantity,
|
|
78817
|
+
cancel: false,
|
|
78818
|
+
price_places: symbol_config.price_places,
|
|
78819
|
+
decimal_places: symbol_config.decimal_places
|
|
78820
|
+
});
|
|
78821
|
+
} catch (rollbackError) {
|
|
78822
|
+
throw new TakeProfitReplaceRollbackFailedError({
|
|
78823
|
+
...metadata3,
|
|
78824
|
+
rollbackError
|
|
78825
|
+
});
|
|
78826
|
+
}
|
|
78827
|
+
throw new TakeProfitReplaceRollbackSucceededError(metadata3);
|
|
78828
|
+
}
|
|
78829
|
+
}
|
|
78629
78830
|
async cancelExchangeOrders(payload) {
|
|
78630
78831
|
return this.exchange.cancelOrders(payload);
|
|
78631
78832
|
}
|
|
@@ -78910,8 +79111,8 @@ class ExchangeAccount {
|
|
|
78910
79111
|
if (payload.trigger && !long_pause_tp && !short_pause_tp && config2) {
|
|
78911
79112
|
return await this.reduceMajorPositionEntry({
|
|
78912
79113
|
symbol,
|
|
78913
|
-
long: config2.long,
|
|
78914
|
-
short: config2.short,
|
|
79114
|
+
long: kind === "long" ? config2.long : undefined,
|
|
79115
|
+
short: kind === "short" ? config2.short : undefined,
|
|
78915
79116
|
trigger: config2.trigger
|
|
78916
79117
|
});
|
|
78917
79118
|
}
|
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 };
|
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
|
|
|
@@ -73624,7 +73626,13 @@ function buildPosition(position2, orders, options) {
|
|
|
73624
73626
|
{ price: entry, quantity },
|
|
73625
73627
|
...limitOrders.map((o) => ({ price: o.price, quantity: o.qty }))
|
|
73626
73628
|
], decimal_places, price_places);
|
|
73627
|
-
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];
|
|
73628
73636
|
const tpOrders = orders.filter((x) => x.kind === kind && !x.isStop).filter((x) => x.kind === "long" ? x.side === "sell" : x.side === "buy")[0];
|
|
73629
73637
|
const avg_entry = avg.entry;
|
|
73630
73638
|
const stop_loss = stopOrders?.triggerPrice || 0;
|
|
@@ -78293,6 +78301,76 @@ async function forceClosePosition2(client, symbol, options) {
|
|
|
78293
78301
|
}
|
|
78294
78302
|
|
|
78295
78303
|
// src/exchange-account.ts
|
|
78304
|
+
function getFormatPrecision(places) {
|
|
78305
|
+
if (!places) {
|
|
78306
|
+
return 0;
|
|
78307
|
+
}
|
|
78308
|
+
const match = places.match(/%\.(\d+)f/);
|
|
78309
|
+
return match ? Number(match[1]) : 0;
|
|
78310
|
+
}
|
|
78311
|
+
function floorToFormat(value2, places) {
|
|
78312
|
+
if (!Number.isFinite(value2) || value2 <= 0) {
|
|
78313
|
+
return 0;
|
|
78314
|
+
}
|
|
78315
|
+
const precision = getFormatPrecision(places);
|
|
78316
|
+
const factor = 10 ** precision;
|
|
78317
|
+
return Math.floor((value2 + Number.EPSILON) * factor) / factor;
|
|
78318
|
+
}
|
|
78319
|
+
|
|
78320
|
+
class TakeProfitReplaceRollbackSucceededError extends Error {
|
|
78321
|
+
status = "rollback_succeeded";
|
|
78322
|
+
restored = true;
|
|
78323
|
+
symbol;
|
|
78324
|
+
kind;
|
|
78325
|
+
takeProfitPrice;
|
|
78326
|
+
quantity;
|
|
78327
|
+
previousTakeProfitPrice;
|
|
78328
|
+
previousQuantity;
|
|
78329
|
+
restoreAttempted;
|
|
78330
|
+
cause;
|
|
78331
|
+
rollbackError;
|
|
78332
|
+
constructor(metadata3) {
|
|
78333
|
+
super("Take profit replacement failed and previous take profit was restored");
|
|
78334
|
+
this.name = "TakeProfitReplaceRollbackSucceededError";
|
|
78335
|
+
this.symbol = metadata3.symbol;
|
|
78336
|
+
this.kind = metadata3.kind;
|
|
78337
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78338
|
+
this.quantity = metadata3.quantity;
|
|
78339
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78340
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78341
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78342
|
+
this.cause = metadata3.cause;
|
|
78343
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78344
|
+
}
|
|
78345
|
+
}
|
|
78346
|
+
|
|
78347
|
+
class TakeProfitReplaceRollbackFailedError extends Error {
|
|
78348
|
+
status = "rollback_failed";
|
|
78349
|
+
restored = false;
|
|
78350
|
+
symbol;
|
|
78351
|
+
kind;
|
|
78352
|
+
takeProfitPrice;
|
|
78353
|
+
quantity;
|
|
78354
|
+
previousTakeProfitPrice;
|
|
78355
|
+
previousQuantity;
|
|
78356
|
+
restoreAttempted;
|
|
78357
|
+
cause;
|
|
78358
|
+
rollbackError;
|
|
78359
|
+
constructor(metadata3) {
|
|
78360
|
+
super("Take profit replacement failed and previous take profit could not be restored");
|
|
78361
|
+
this.name = "TakeProfitReplaceRollbackFailedError";
|
|
78362
|
+
this.symbol = metadata3.symbol;
|
|
78363
|
+
this.kind = metadata3.kind;
|
|
78364
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
78365
|
+
this.quantity = metadata3.quantity;
|
|
78366
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
78367
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
78368
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
78369
|
+
this.cause = metadata3.cause;
|
|
78370
|
+
this.rollbackError = metadata3.rollbackError;
|
|
78371
|
+
}
|
|
78372
|
+
}
|
|
78373
|
+
|
|
78296
78374
|
class ExchangeAccount {
|
|
78297
78375
|
instance;
|
|
78298
78376
|
exchange;
|
|
@@ -78389,8 +78467,6 @@ class ExchangeAccount {
|
|
|
78389
78467
|
refresh
|
|
78390
78468
|
});
|
|
78391
78469
|
const raw_active_account = live_exchange_instance.data;
|
|
78392
|
-
console.log("raw_active", raw_active_account);
|
|
78393
|
-
console.log("symbol_config", symbol_config);
|
|
78394
78470
|
const _all = get_active_accounts({
|
|
78395
78471
|
active_account: raw_active_account,
|
|
78396
78472
|
symbol_config
|
|
@@ -78406,7 +78482,6 @@ class ExchangeAccount {
|
|
|
78406
78482
|
refresh: live_refresh,
|
|
78407
78483
|
symbol
|
|
78408
78484
|
});
|
|
78409
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
78410
78485
|
if (leverage) {
|
|
78411
78486
|
this.exchange.setLeverage({ symbol, leverage });
|
|
78412
78487
|
}
|
|
@@ -78524,6 +78599,132 @@ class ExchangeAccount {
|
|
|
78524
78599
|
});
|
|
78525
78600
|
return await focus_position.cancelOrders(payload);
|
|
78526
78601
|
}
|
|
78602
|
+
async replaceTakeProfitForPosition(payload) {
|
|
78603
|
+
const { symbol, kind, takeProfitPrice, quantity } = payload;
|
|
78604
|
+
const focusPosition = await this.getFocusPosition({
|
|
78605
|
+
symbol,
|
|
78606
|
+
kind,
|
|
78607
|
+
update: true
|
|
78608
|
+
});
|
|
78609
|
+
const position2 = focusPosition.getInstance();
|
|
78610
|
+
const symbol_config = focusPosition.symbol_config;
|
|
78611
|
+
if (!symbol_config) {
|
|
78612
|
+
throw new Error(`Missing symbol config for ${symbol}`);
|
|
78613
|
+
}
|
|
78614
|
+
const normalizedQuantity = floorToFormat(quantity, symbol_config.decimal_places);
|
|
78615
|
+
const minSize = Number(symbol_config.min_size || 0);
|
|
78616
|
+
const previousTakeProfitPrice = Number(position2?.take_profit || 0);
|
|
78617
|
+
const previousQuantity = floorToFormat(Number(position2?.tp_quantity || 0), symbol_config.decimal_places);
|
|
78618
|
+
if (normalizedQuantity <= 0) {
|
|
78619
|
+
return {
|
|
78620
|
+
status: "below_min_lot",
|
|
78621
|
+
symbol,
|
|
78622
|
+
kind,
|
|
78623
|
+
takeProfitPrice,
|
|
78624
|
+
quantity: 0,
|
|
78625
|
+
minSize,
|
|
78626
|
+
reason: "quantity_floored_to_zero"
|
|
78627
|
+
};
|
|
78628
|
+
}
|
|
78629
|
+
if (minSize > 0 && normalizedQuantity < minSize) {
|
|
78630
|
+
return {
|
|
78631
|
+
status: "below_min_lot",
|
|
78632
|
+
symbol,
|
|
78633
|
+
kind,
|
|
78634
|
+
takeProfitPrice,
|
|
78635
|
+
quantity: normalizedQuantity,
|
|
78636
|
+
minSize,
|
|
78637
|
+
reason: "below_venue_minimum"
|
|
78638
|
+
};
|
|
78639
|
+
}
|
|
78640
|
+
const exchangeSeams = this.exchange;
|
|
78641
|
+
if (exchangeSeams.replaceTakeProfitForPosition) {
|
|
78642
|
+
await exchangeSeams.replaceTakeProfitForPosition({
|
|
78643
|
+
symbol,
|
|
78644
|
+
kind,
|
|
78645
|
+
takeProfitPrice,
|
|
78646
|
+
quantity: normalizedQuantity,
|
|
78647
|
+
price_places: symbol_config.price_places,
|
|
78648
|
+
decimal_places: symbol_config.decimal_places,
|
|
78649
|
+
previousTakeProfitPrice,
|
|
78650
|
+
previousQuantity
|
|
78651
|
+
});
|
|
78652
|
+
return {
|
|
78653
|
+
status: "replaced",
|
|
78654
|
+
symbol,
|
|
78655
|
+
kind,
|
|
78656
|
+
takeProfitPrice,
|
|
78657
|
+
quantity: normalizedQuantity,
|
|
78658
|
+
previousTakeProfitPrice,
|
|
78659
|
+
previousQuantity,
|
|
78660
|
+
restoreAttempted: false,
|
|
78661
|
+
via: "native_replace"
|
|
78662
|
+
};
|
|
78663
|
+
}
|
|
78664
|
+
const placeTpWithoutCancelling = exchangeSeams._placeTpOrder;
|
|
78665
|
+
if (!placeTpWithoutCancelling) {
|
|
78666
|
+
throw new Error(`Exchange ${this.instance.exchange} does not expose a TP replace or raw TP placement path`);
|
|
78667
|
+
}
|
|
78668
|
+
if (previousTakeProfitPrice > 0) {
|
|
78669
|
+
await this.cancelOrders({
|
|
78670
|
+
symbol,
|
|
78671
|
+
kind,
|
|
78672
|
+
price: previousTakeProfitPrice
|
|
78673
|
+
});
|
|
78674
|
+
}
|
|
78675
|
+
try {
|
|
78676
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78677
|
+
symbol,
|
|
78678
|
+
tp: takeProfitPrice,
|
|
78679
|
+
kind,
|
|
78680
|
+
quantity: normalizedQuantity,
|
|
78681
|
+
cancel: false,
|
|
78682
|
+
price_places: symbol_config.price_places,
|
|
78683
|
+
decimal_places: symbol_config.decimal_places
|
|
78684
|
+
});
|
|
78685
|
+
return {
|
|
78686
|
+
status: "replaced",
|
|
78687
|
+
symbol,
|
|
78688
|
+
kind,
|
|
78689
|
+
takeProfitPrice,
|
|
78690
|
+
quantity: normalizedQuantity,
|
|
78691
|
+
previousTakeProfitPrice,
|
|
78692
|
+
previousQuantity,
|
|
78693
|
+
restoreAttempted: false
|
|
78694
|
+
};
|
|
78695
|
+
} catch (cause) {
|
|
78696
|
+
const metadata3 = {
|
|
78697
|
+
symbol,
|
|
78698
|
+
kind,
|
|
78699
|
+
takeProfitPrice,
|
|
78700
|
+
quantity: normalizedQuantity,
|
|
78701
|
+
previousTakeProfitPrice,
|
|
78702
|
+
previousQuantity,
|
|
78703
|
+
restoreAttempted: previousTakeProfitPrice > 0 && previousQuantity > 0,
|
|
78704
|
+
cause
|
|
78705
|
+
};
|
|
78706
|
+
if (!metadata3.restoreAttempted) {
|
|
78707
|
+
throw new TakeProfitReplaceRollbackFailedError(metadata3);
|
|
78708
|
+
}
|
|
78709
|
+
try {
|
|
78710
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
78711
|
+
symbol,
|
|
78712
|
+
tp: previousTakeProfitPrice,
|
|
78713
|
+
kind,
|
|
78714
|
+
quantity: previousQuantity,
|
|
78715
|
+
cancel: false,
|
|
78716
|
+
price_places: symbol_config.price_places,
|
|
78717
|
+
decimal_places: symbol_config.decimal_places
|
|
78718
|
+
});
|
|
78719
|
+
} catch (rollbackError) {
|
|
78720
|
+
throw new TakeProfitReplaceRollbackFailedError({
|
|
78721
|
+
...metadata3,
|
|
78722
|
+
rollbackError
|
|
78723
|
+
});
|
|
78724
|
+
}
|
|
78725
|
+
throw new TakeProfitReplaceRollbackSucceededError(metadata3);
|
|
78726
|
+
}
|
|
78727
|
+
}
|
|
78527
78728
|
async cancelExchangeOrders(payload) {
|
|
78528
78729
|
return this.exchange.cancelOrders(payload);
|
|
78529
78730
|
}
|
|
@@ -78808,8 +79009,8 @@ class ExchangeAccount {
|
|
|
78808
79009
|
if (payload.trigger && !long_pause_tp && !short_pause_tp && config2) {
|
|
78809
79010
|
return await this.reduceMajorPositionEntry({
|
|
78810
79011
|
symbol,
|
|
78811
|
-
long: config2.long,
|
|
78812
|
-
short: config2.short,
|
|
79012
|
+
long: kind === "long" ? config2.long : undefined,
|
|
79013
|
+
short: kind === "short" ? config2.short : undefined,
|
|
78813
79014
|
trigger: config2.trigger
|
|
78814
79015
|
});
|
|
78815
79016
|
}
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -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;
|
|
@@ -82117,6 +82123,76 @@ async function forceClosePosition2(client, symbol, options) {
|
|
|
82117
82123
|
}
|
|
82118
82124
|
|
|
82119
82125
|
// src/exchange-account.ts
|
|
82126
|
+
function getFormatPrecision(places) {
|
|
82127
|
+
if (!places) {
|
|
82128
|
+
return 0;
|
|
82129
|
+
}
|
|
82130
|
+
const match = places.match(/%\.(\d+)f/);
|
|
82131
|
+
return match ? Number(match[1]) : 0;
|
|
82132
|
+
}
|
|
82133
|
+
function floorToFormat(value2, places) {
|
|
82134
|
+
if (!Number.isFinite(value2) || value2 <= 0) {
|
|
82135
|
+
return 0;
|
|
82136
|
+
}
|
|
82137
|
+
const precision = getFormatPrecision(places);
|
|
82138
|
+
const factor = 10 ** precision;
|
|
82139
|
+
return Math.floor((value2 + Number.EPSILON) * factor) / factor;
|
|
82140
|
+
}
|
|
82141
|
+
|
|
82142
|
+
class TakeProfitReplaceRollbackSucceededError extends Error {
|
|
82143
|
+
status = "rollback_succeeded";
|
|
82144
|
+
restored = true;
|
|
82145
|
+
symbol;
|
|
82146
|
+
kind;
|
|
82147
|
+
takeProfitPrice;
|
|
82148
|
+
quantity;
|
|
82149
|
+
previousTakeProfitPrice;
|
|
82150
|
+
previousQuantity;
|
|
82151
|
+
restoreAttempted;
|
|
82152
|
+
cause;
|
|
82153
|
+
rollbackError;
|
|
82154
|
+
constructor(metadata3) {
|
|
82155
|
+
super("Take profit replacement failed and previous take profit was restored");
|
|
82156
|
+
this.name = "TakeProfitReplaceRollbackSucceededError";
|
|
82157
|
+
this.symbol = metadata3.symbol;
|
|
82158
|
+
this.kind = metadata3.kind;
|
|
82159
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
82160
|
+
this.quantity = metadata3.quantity;
|
|
82161
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
82162
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
82163
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
82164
|
+
this.cause = metadata3.cause;
|
|
82165
|
+
this.rollbackError = metadata3.rollbackError;
|
|
82166
|
+
}
|
|
82167
|
+
}
|
|
82168
|
+
|
|
82169
|
+
class TakeProfitReplaceRollbackFailedError extends Error {
|
|
82170
|
+
status = "rollback_failed";
|
|
82171
|
+
restored = false;
|
|
82172
|
+
symbol;
|
|
82173
|
+
kind;
|
|
82174
|
+
takeProfitPrice;
|
|
82175
|
+
quantity;
|
|
82176
|
+
previousTakeProfitPrice;
|
|
82177
|
+
previousQuantity;
|
|
82178
|
+
restoreAttempted;
|
|
82179
|
+
cause;
|
|
82180
|
+
rollbackError;
|
|
82181
|
+
constructor(metadata3) {
|
|
82182
|
+
super("Take profit replacement failed and previous take profit could not be restored");
|
|
82183
|
+
this.name = "TakeProfitReplaceRollbackFailedError";
|
|
82184
|
+
this.symbol = metadata3.symbol;
|
|
82185
|
+
this.kind = metadata3.kind;
|
|
82186
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
82187
|
+
this.quantity = metadata3.quantity;
|
|
82188
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
82189
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
82190
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
82191
|
+
this.cause = metadata3.cause;
|
|
82192
|
+
this.rollbackError = metadata3.rollbackError;
|
|
82193
|
+
}
|
|
82194
|
+
}
|
|
82195
|
+
|
|
82120
82196
|
class ExchangeAccount {
|
|
82121
82197
|
instance;
|
|
82122
82198
|
exchange;
|
|
@@ -82213,8 +82289,6 @@ class ExchangeAccount {
|
|
|
82213
82289
|
refresh
|
|
82214
82290
|
});
|
|
82215
82291
|
const raw_active_account = live_exchange_instance.data;
|
|
82216
|
-
console.log("raw_active", raw_active_account);
|
|
82217
|
-
console.log("symbol_config", symbol_config);
|
|
82218
82292
|
const _all = get_active_accounts({
|
|
82219
82293
|
active_account: raw_active_account,
|
|
82220
82294
|
symbol_config
|
|
@@ -82230,7 +82304,6 @@ class ExchangeAccount {
|
|
|
82230
82304
|
refresh: live_refresh,
|
|
82231
82305
|
symbol
|
|
82232
82306
|
});
|
|
82233
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
82234
82307
|
if (leverage) {
|
|
82235
82308
|
this.exchange.setLeverage({ symbol, leverage });
|
|
82236
82309
|
}
|
|
@@ -82348,6 +82421,132 @@ class ExchangeAccount {
|
|
|
82348
82421
|
});
|
|
82349
82422
|
return await focus_position.cancelOrders(payload);
|
|
82350
82423
|
}
|
|
82424
|
+
async replaceTakeProfitForPosition(payload) {
|
|
82425
|
+
const { symbol, kind, takeProfitPrice, quantity } = payload;
|
|
82426
|
+
const focusPosition = await this.getFocusPosition({
|
|
82427
|
+
symbol,
|
|
82428
|
+
kind,
|
|
82429
|
+
update: true
|
|
82430
|
+
});
|
|
82431
|
+
const position2 = focusPosition.getInstance();
|
|
82432
|
+
const symbol_config = focusPosition.symbol_config;
|
|
82433
|
+
if (!symbol_config) {
|
|
82434
|
+
throw new Error(`Missing symbol config for ${symbol}`);
|
|
82435
|
+
}
|
|
82436
|
+
const normalizedQuantity = floorToFormat(quantity, symbol_config.decimal_places);
|
|
82437
|
+
const minSize = Number(symbol_config.min_size || 0);
|
|
82438
|
+
const previousTakeProfitPrice = Number(position2?.take_profit || 0);
|
|
82439
|
+
const previousQuantity = floorToFormat(Number(position2?.tp_quantity || 0), symbol_config.decimal_places);
|
|
82440
|
+
if (normalizedQuantity <= 0) {
|
|
82441
|
+
return {
|
|
82442
|
+
status: "below_min_lot",
|
|
82443
|
+
symbol,
|
|
82444
|
+
kind,
|
|
82445
|
+
takeProfitPrice,
|
|
82446
|
+
quantity: 0,
|
|
82447
|
+
minSize,
|
|
82448
|
+
reason: "quantity_floored_to_zero"
|
|
82449
|
+
};
|
|
82450
|
+
}
|
|
82451
|
+
if (minSize > 0 && normalizedQuantity < minSize) {
|
|
82452
|
+
return {
|
|
82453
|
+
status: "below_min_lot",
|
|
82454
|
+
symbol,
|
|
82455
|
+
kind,
|
|
82456
|
+
takeProfitPrice,
|
|
82457
|
+
quantity: normalizedQuantity,
|
|
82458
|
+
minSize,
|
|
82459
|
+
reason: "below_venue_minimum"
|
|
82460
|
+
};
|
|
82461
|
+
}
|
|
82462
|
+
const exchangeSeams = this.exchange;
|
|
82463
|
+
if (exchangeSeams.replaceTakeProfitForPosition) {
|
|
82464
|
+
await exchangeSeams.replaceTakeProfitForPosition({
|
|
82465
|
+
symbol,
|
|
82466
|
+
kind,
|
|
82467
|
+
takeProfitPrice,
|
|
82468
|
+
quantity: normalizedQuantity,
|
|
82469
|
+
price_places: symbol_config.price_places,
|
|
82470
|
+
decimal_places: symbol_config.decimal_places,
|
|
82471
|
+
previousTakeProfitPrice,
|
|
82472
|
+
previousQuantity
|
|
82473
|
+
});
|
|
82474
|
+
return {
|
|
82475
|
+
status: "replaced",
|
|
82476
|
+
symbol,
|
|
82477
|
+
kind,
|
|
82478
|
+
takeProfitPrice,
|
|
82479
|
+
quantity: normalizedQuantity,
|
|
82480
|
+
previousTakeProfitPrice,
|
|
82481
|
+
previousQuantity,
|
|
82482
|
+
restoreAttempted: false,
|
|
82483
|
+
via: "native_replace"
|
|
82484
|
+
};
|
|
82485
|
+
}
|
|
82486
|
+
const placeTpWithoutCancelling = exchangeSeams._placeTpOrder;
|
|
82487
|
+
if (!placeTpWithoutCancelling) {
|
|
82488
|
+
throw new Error(`Exchange ${this.instance.exchange} does not expose a TP replace or raw TP placement path`);
|
|
82489
|
+
}
|
|
82490
|
+
if (previousTakeProfitPrice > 0) {
|
|
82491
|
+
await this.cancelOrders({
|
|
82492
|
+
symbol,
|
|
82493
|
+
kind,
|
|
82494
|
+
price: previousTakeProfitPrice
|
|
82495
|
+
});
|
|
82496
|
+
}
|
|
82497
|
+
try {
|
|
82498
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
82499
|
+
symbol,
|
|
82500
|
+
tp: takeProfitPrice,
|
|
82501
|
+
kind,
|
|
82502
|
+
quantity: normalizedQuantity,
|
|
82503
|
+
cancel: false,
|
|
82504
|
+
price_places: symbol_config.price_places,
|
|
82505
|
+
decimal_places: symbol_config.decimal_places
|
|
82506
|
+
});
|
|
82507
|
+
return {
|
|
82508
|
+
status: "replaced",
|
|
82509
|
+
symbol,
|
|
82510
|
+
kind,
|
|
82511
|
+
takeProfitPrice,
|
|
82512
|
+
quantity: normalizedQuantity,
|
|
82513
|
+
previousTakeProfitPrice,
|
|
82514
|
+
previousQuantity,
|
|
82515
|
+
restoreAttempted: false
|
|
82516
|
+
};
|
|
82517
|
+
} catch (cause) {
|
|
82518
|
+
const metadata3 = {
|
|
82519
|
+
symbol,
|
|
82520
|
+
kind,
|
|
82521
|
+
takeProfitPrice,
|
|
82522
|
+
quantity: normalizedQuantity,
|
|
82523
|
+
previousTakeProfitPrice,
|
|
82524
|
+
previousQuantity,
|
|
82525
|
+
restoreAttempted: previousTakeProfitPrice > 0 && previousQuantity > 0,
|
|
82526
|
+
cause
|
|
82527
|
+
};
|
|
82528
|
+
if (!metadata3.restoreAttempted) {
|
|
82529
|
+
throw new TakeProfitReplaceRollbackFailedError(metadata3);
|
|
82530
|
+
}
|
|
82531
|
+
try {
|
|
82532
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
82533
|
+
symbol,
|
|
82534
|
+
tp: previousTakeProfitPrice,
|
|
82535
|
+
kind,
|
|
82536
|
+
quantity: previousQuantity,
|
|
82537
|
+
cancel: false,
|
|
82538
|
+
price_places: symbol_config.price_places,
|
|
82539
|
+
decimal_places: symbol_config.decimal_places
|
|
82540
|
+
});
|
|
82541
|
+
} catch (rollbackError) {
|
|
82542
|
+
throw new TakeProfitReplaceRollbackFailedError({
|
|
82543
|
+
...metadata3,
|
|
82544
|
+
rollbackError
|
|
82545
|
+
});
|
|
82546
|
+
}
|
|
82547
|
+
throw new TakeProfitReplaceRollbackSucceededError(metadata3);
|
|
82548
|
+
}
|
|
82549
|
+
}
|
|
82351
82550
|
async cancelExchangeOrders(payload) {
|
|
82352
82551
|
return this.exchange.cancelOrders(payload);
|
|
82353
82552
|
}
|
|
@@ -82632,8 +82831,8 @@ class ExchangeAccount {
|
|
|
82632
82831
|
if (payload.trigger && !long_pause_tp && !short_pause_tp && config2) {
|
|
82633
82832
|
return await this.reduceMajorPositionEntry({
|
|
82634
82833
|
symbol,
|
|
82635
|
-
long: config2.long,
|
|
82636
|
-
short: config2.short,
|
|
82834
|
+
long: kind === "long" ? config2.long : undefined,
|
|
82835
|
+
short: kind === "short" ? config2.short : undefined,
|
|
82637
82836
|
trigger: config2.trigger
|
|
82638
82837
|
});
|
|
82639
82838
|
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -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;
|
|
@@ -82076,6 +82082,76 @@ async function forceClosePosition2(client, symbol, options) {
|
|
|
82076
82082
|
}
|
|
82077
82083
|
|
|
82078
82084
|
// src/exchange-account.ts
|
|
82085
|
+
function getFormatPrecision(places) {
|
|
82086
|
+
if (!places) {
|
|
82087
|
+
return 0;
|
|
82088
|
+
}
|
|
82089
|
+
const match = places.match(/%\.(\d+)f/);
|
|
82090
|
+
return match ? Number(match[1]) : 0;
|
|
82091
|
+
}
|
|
82092
|
+
function floorToFormat(value2, places) {
|
|
82093
|
+
if (!Number.isFinite(value2) || value2 <= 0) {
|
|
82094
|
+
return 0;
|
|
82095
|
+
}
|
|
82096
|
+
const precision = getFormatPrecision(places);
|
|
82097
|
+
const factor = 10 ** precision;
|
|
82098
|
+
return Math.floor((value2 + Number.EPSILON) * factor) / factor;
|
|
82099
|
+
}
|
|
82100
|
+
|
|
82101
|
+
class TakeProfitReplaceRollbackSucceededError extends Error {
|
|
82102
|
+
status = "rollback_succeeded";
|
|
82103
|
+
restored = true;
|
|
82104
|
+
symbol;
|
|
82105
|
+
kind;
|
|
82106
|
+
takeProfitPrice;
|
|
82107
|
+
quantity;
|
|
82108
|
+
previousTakeProfitPrice;
|
|
82109
|
+
previousQuantity;
|
|
82110
|
+
restoreAttempted;
|
|
82111
|
+
cause;
|
|
82112
|
+
rollbackError;
|
|
82113
|
+
constructor(metadata3) {
|
|
82114
|
+
super("Take profit replacement failed and previous take profit was restored");
|
|
82115
|
+
this.name = "TakeProfitReplaceRollbackSucceededError";
|
|
82116
|
+
this.symbol = metadata3.symbol;
|
|
82117
|
+
this.kind = metadata3.kind;
|
|
82118
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
82119
|
+
this.quantity = metadata3.quantity;
|
|
82120
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
82121
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
82122
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
82123
|
+
this.cause = metadata3.cause;
|
|
82124
|
+
this.rollbackError = metadata3.rollbackError;
|
|
82125
|
+
}
|
|
82126
|
+
}
|
|
82127
|
+
|
|
82128
|
+
class TakeProfitReplaceRollbackFailedError extends Error {
|
|
82129
|
+
status = "rollback_failed";
|
|
82130
|
+
restored = false;
|
|
82131
|
+
symbol;
|
|
82132
|
+
kind;
|
|
82133
|
+
takeProfitPrice;
|
|
82134
|
+
quantity;
|
|
82135
|
+
previousTakeProfitPrice;
|
|
82136
|
+
previousQuantity;
|
|
82137
|
+
restoreAttempted;
|
|
82138
|
+
cause;
|
|
82139
|
+
rollbackError;
|
|
82140
|
+
constructor(metadata3) {
|
|
82141
|
+
super("Take profit replacement failed and previous take profit could not be restored");
|
|
82142
|
+
this.name = "TakeProfitReplaceRollbackFailedError";
|
|
82143
|
+
this.symbol = metadata3.symbol;
|
|
82144
|
+
this.kind = metadata3.kind;
|
|
82145
|
+
this.takeProfitPrice = metadata3.takeProfitPrice;
|
|
82146
|
+
this.quantity = metadata3.quantity;
|
|
82147
|
+
this.previousTakeProfitPrice = metadata3.previousTakeProfitPrice;
|
|
82148
|
+
this.previousQuantity = metadata3.previousQuantity;
|
|
82149
|
+
this.restoreAttempted = metadata3.restoreAttempted;
|
|
82150
|
+
this.cause = metadata3.cause;
|
|
82151
|
+
this.rollbackError = metadata3.rollbackError;
|
|
82152
|
+
}
|
|
82153
|
+
}
|
|
82154
|
+
|
|
82079
82155
|
class ExchangeAccount {
|
|
82080
82156
|
instance;
|
|
82081
82157
|
exchange;
|
|
@@ -82172,8 +82248,6 @@ class ExchangeAccount {
|
|
|
82172
82248
|
refresh
|
|
82173
82249
|
});
|
|
82174
82250
|
const raw_active_account = live_exchange_instance.data;
|
|
82175
|
-
console.log("raw_active", raw_active_account);
|
|
82176
|
-
console.log("symbol_config", symbol_config);
|
|
82177
82251
|
const _all = get_active_accounts({
|
|
82178
82252
|
active_account: raw_active_account,
|
|
82179
82253
|
symbol_config
|
|
@@ -82189,7 +82263,6 @@ class ExchangeAccount {
|
|
|
82189
82263
|
refresh: live_refresh,
|
|
82190
82264
|
symbol
|
|
82191
82265
|
});
|
|
82192
|
-
console.log("active_accounts", active_account, live_refresh);
|
|
82193
82266
|
if (leverage) {
|
|
82194
82267
|
this.exchange.setLeverage({ symbol, leverage });
|
|
82195
82268
|
}
|
|
@@ -82307,6 +82380,132 @@ class ExchangeAccount {
|
|
|
82307
82380
|
});
|
|
82308
82381
|
return await focus_position.cancelOrders(payload);
|
|
82309
82382
|
}
|
|
82383
|
+
async replaceTakeProfitForPosition(payload) {
|
|
82384
|
+
const { symbol, kind, takeProfitPrice, quantity } = payload;
|
|
82385
|
+
const focusPosition = await this.getFocusPosition({
|
|
82386
|
+
symbol,
|
|
82387
|
+
kind,
|
|
82388
|
+
update: true
|
|
82389
|
+
});
|
|
82390
|
+
const position2 = focusPosition.getInstance();
|
|
82391
|
+
const symbol_config = focusPosition.symbol_config;
|
|
82392
|
+
if (!symbol_config) {
|
|
82393
|
+
throw new Error(`Missing symbol config for ${symbol}`);
|
|
82394
|
+
}
|
|
82395
|
+
const normalizedQuantity = floorToFormat(quantity, symbol_config.decimal_places);
|
|
82396
|
+
const minSize = Number(symbol_config.min_size || 0);
|
|
82397
|
+
const previousTakeProfitPrice = Number(position2?.take_profit || 0);
|
|
82398
|
+
const previousQuantity = floorToFormat(Number(position2?.tp_quantity || 0), symbol_config.decimal_places);
|
|
82399
|
+
if (normalizedQuantity <= 0) {
|
|
82400
|
+
return {
|
|
82401
|
+
status: "below_min_lot",
|
|
82402
|
+
symbol,
|
|
82403
|
+
kind,
|
|
82404
|
+
takeProfitPrice,
|
|
82405
|
+
quantity: 0,
|
|
82406
|
+
minSize,
|
|
82407
|
+
reason: "quantity_floored_to_zero"
|
|
82408
|
+
};
|
|
82409
|
+
}
|
|
82410
|
+
if (minSize > 0 && normalizedQuantity < minSize) {
|
|
82411
|
+
return {
|
|
82412
|
+
status: "below_min_lot",
|
|
82413
|
+
symbol,
|
|
82414
|
+
kind,
|
|
82415
|
+
takeProfitPrice,
|
|
82416
|
+
quantity: normalizedQuantity,
|
|
82417
|
+
minSize,
|
|
82418
|
+
reason: "below_venue_minimum"
|
|
82419
|
+
};
|
|
82420
|
+
}
|
|
82421
|
+
const exchangeSeams = this.exchange;
|
|
82422
|
+
if (exchangeSeams.replaceTakeProfitForPosition) {
|
|
82423
|
+
await exchangeSeams.replaceTakeProfitForPosition({
|
|
82424
|
+
symbol,
|
|
82425
|
+
kind,
|
|
82426
|
+
takeProfitPrice,
|
|
82427
|
+
quantity: normalizedQuantity,
|
|
82428
|
+
price_places: symbol_config.price_places,
|
|
82429
|
+
decimal_places: symbol_config.decimal_places,
|
|
82430
|
+
previousTakeProfitPrice,
|
|
82431
|
+
previousQuantity
|
|
82432
|
+
});
|
|
82433
|
+
return {
|
|
82434
|
+
status: "replaced",
|
|
82435
|
+
symbol,
|
|
82436
|
+
kind,
|
|
82437
|
+
takeProfitPrice,
|
|
82438
|
+
quantity: normalizedQuantity,
|
|
82439
|
+
previousTakeProfitPrice,
|
|
82440
|
+
previousQuantity,
|
|
82441
|
+
restoreAttempted: false,
|
|
82442
|
+
via: "native_replace"
|
|
82443
|
+
};
|
|
82444
|
+
}
|
|
82445
|
+
const placeTpWithoutCancelling = exchangeSeams._placeTpOrder;
|
|
82446
|
+
if (!placeTpWithoutCancelling) {
|
|
82447
|
+
throw new Error(`Exchange ${this.instance.exchange} does not expose a TP replace or raw TP placement path`);
|
|
82448
|
+
}
|
|
82449
|
+
if (previousTakeProfitPrice > 0) {
|
|
82450
|
+
await this.cancelOrders({
|
|
82451
|
+
symbol,
|
|
82452
|
+
kind,
|
|
82453
|
+
price: previousTakeProfitPrice
|
|
82454
|
+
});
|
|
82455
|
+
}
|
|
82456
|
+
try {
|
|
82457
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
82458
|
+
symbol,
|
|
82459
|
+
tp: takeProfitPrice,
|
|
82460
|
+
kind,
|
|
82461
|
+
quantity: normalizedQuantity,
|
|
82462
|
+
cancel: false,
|
|
82463
|
+
price_places: symbol_config.price_places,
|
|
82464
|
+
decimal_places: symbol_config.decimal_places
|
|
82465
|
+
});
|
|
82466
|
+
return {
|
|
82467
|
+
status: "replaced",
|
|
82468
|
+
symbol,
|
|
82469
|
+
kind,
|
|
82470
|
+
takeProfitPrice,
|
|
82471
|
+
quantity: normalizedQuantity,
|
|
82472
|
+
previousTakeProfitPrice,
|
|
82473
|
+
previousQuantity,
|
|
82474
|
+
restoreAttempted: false
|
|
82475
|
+
};
|
|
82476
|
+
} catch (cause) {
|
|
82477
|
+
const metadata3 = {
|
|
82478
|
+
symbol,
|
|
82479
|
+
kind,
|
|
82480
|
+
takeProfitPrice,
|
|
82481
|
+
quantity: normalizedQuantity,
|
|
82482
|
+
previousTakeProfitPrice,
|
|
82483
|
+
previousQuantity,
|
|
82484
|
+
restoreAttempted: previousTakeProfitPrice > 0 && previousQuantity > 0,
|
|
82485
|
+
cause
|
|
82486
|
+
};
|
|
82487
|
+
if (!metadata3.restoreAttempted) {
|
|
82488
|
+
throw new TakeProfitReplaceRollbackFailedError(metadata3);
|
|
82489
|
+
}
|
|
82490
|
+
try {
|
|
82491
|
+
await placeTpWithoutCancelling.call(this.exchange, {
|
|
82492
|
+
symbol,
|
|
82493
|
+
tp: previousTakeProfitPrice,
|
|
82494
|
+
kind,
|
|
82495
|
+
quantity: previousQuantity,
|
|
82496
|
+
cancel: false,
|
|
82497
|
+
price_places: symbol_config.price_places,
|
|
82498
|
+
decimal_places: symbol_config.decimal_places
|
|
82499
|
+
});
|
|
82500
|
+
} catch (rollbackError) {
|
|
82501
|
+
throw new TakeProfitReplaceRollbackFailedError({
|
|
82502
|
+
...metadata3,
|
|
82503
|
+
rollbackError
|
|
82504
|
+
});
|
|
82505
|
+
}
|
|
82506
|
+
throw new TakeProfitReplaceRollbackSucceededError(metadata3);
|
|
82507
|
+
}
|
|
82508
|
+
}
|
|
82310
82509
|
async cancelExchangeOrders(payload) {
|
|
82311
82510
|
return this.exchange.cancelOrders(payload);
|
|
82312
82511
|
}
|
|
@@ -82591,8 +82790,8 @@ class ExchangeAccount {
|
|
|
82591
82790
|
if (payload.trigger && !long_pause_tp && !short_pause_tp && config2) {
|
|
82592
82791
|
return await this.reduceMajorPositionEntry({
|
|
82593
82792
|
symbol,
|
|
82594
|
-
long: config2.long,
|
|
82595
|
-
short: config2.short,
|
|
82793
|
+
long: kind === "long" ? config2.long : undefined,
|
|
82794
|
+
short: kind === "short" ? config2.short : undefined,
|
|
82596
82795
|
trigger: config2.trigger
|
|
82597
82796
|
});
|
|
82598
82797
|
}
|