dexbot 1.4.3 → 1.4.5
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/modules/dexbot_cow_runtime.d.ts +30 -0
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +192 -0
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +32 -25
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/order/accounting.d.ts +2 -2
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +3 -6
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/async_lock.d.ts +7 -0
- package/dist/modules/order/async_lock.d.ts.map +1 -1
- package/dist/modules/order/async_lock.js +31 -1
- package/dist/modules/order/async_lock.js.map +1 -1
- package/dist/modules/order/grid.d.ts +3 -11
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +70 -98
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile.d.ts +3 -9
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile.js +80 -48
- package/dist/modules/order/grid_reconcile.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.js +29 -1
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/manager.d.ts +38 -7
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +99 -123
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/strategy.js +0 -1
- package/dist/modules/order/strategy.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +1 -2
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +26 -1
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +91 -53
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +10 -20
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +9 -3
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +31 -67
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/validate.js +1 -1
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/package.json +1 -1
|
@@ -102,6 +102,7 @@ declare class AsyncLock {
|
|
|
102
102
|
private _defaultTimeout;
|
|
103
103
|
private _onContention;
|
|
104
104
|
private readonly _lockId;
|
|
105
|
+
private _orphaned;
|
|
105
106
|
constructor(options?: AsyncLockOptions);
|
|
106
107
|
/**
|
|
107
108
|
* Acquire the lock and execute callback exclusively
|
|
@@ -156,6 +157,12 @@ declare class AsyncLock {
|
|
|
156
157
|
* stale callback from interfering with a new acquirer that claimed
|
|
157
158
|
* the lock after forceRelease.
|
|
158
159
|
*
|
|
160
|
+
* CONCURRENT EXECUTION GUARD: If a callback is currently executing
|
|
161
|
+
* (_holding is true), forceRelease defers _locked = false to the
|
|
162
|
+
* stale callback's finally block via the _orphaned flag. This
|
|
163
|
+
* prevents a new acquirer from entering while the stale callback
|
|
164
|
+
* is still running, preserving mutual exclusion.
|
|
165
|
+
*
|
|
159
166
|
* @returns {number} Count of cleared items
|
|
160
167
|
*/
|
|
161
168
|
forceRelease(): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async_lock.d.ts","sourceRoot":"","sources":["../../../modules/order/async_lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFG;AAaH,UAAU,gBAAgB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,UAAU,cAAc;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAeD,cAAM,SAAS;IACX,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;
|
|
1
|
+
{"version":3,"file":"async_lock.d.ts","sourceRoot":"","sources":["../../../modules/order/async_lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFG;AAaH,UAAU,gBAAgB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,UAAU,cAAc;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAeD,cAAM,SAAS;IACX,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,SAAS,CAAU;gBAEf,OAAO,GAAE,gBAAqB;IAW1C;;;;;;;OAOG;IACG,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAmEtF;;;OAGG;YACW,aAAa;IA2D3B;;;OAGG;IACH,QAAQ,IAAI,OAAO;IAInB;;;;OAIG;IACH,WAAW,IAAI,OAAO;IAOtB;;;OAGG;IACH,cAAc,IAAI,MAAM;IAIxB;;;;OAIG;IACH,UAAU,IAAI,MAAM;IASpB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,YAAY,IAAI,MAAM;CAyBzB;AAED,eAAe,SAAS,CAAA"}
|
|
@@ -106,6 +106,7 @@ class AsyncLock {
|
|
|
106
106
|
_defaultTimeout;
|
|
107
107
|
_onContention;
|
|
108
108
|
_lockId;
|
|
109
|
+
_orphaned;
|
|
109
110
|
constructor(options = {}) {
|
|
110
111
|
this._queue = [];
|
|
111
112
|
this._locked = false;
|
|
@@ -114,6 +115,7 @@ class AsyncLock {
|
|
|
114
115
|
this._defaultTimeout = options.timeout || null;
|
|
115
116
|
this._onContention = options.onContention || null;
|
|
116
117
|
this._lockId = Symbol('AsyncLock');
|
|
118
|
+
this._orphaned = false;
|
|
117
119
|
}
|
|
118
120
|
/**
|
|
119
121
|
* Acquire the lock and execute callback exclusively
|
|
@@ -223,6 +225,17 @@ class AsyncLock {
|
|
|
223
225
|
this._locked = false;
|
|
224
226
|
this._processQueue();
|
|
225
227
|
}
|
|
228
|
+
else if (this._orphaned) {
|
|
229
|
+
// Stale callback finishing after forceRelease. The
|
|
230
|
+
// forceRelease deferred _locked = false to us via the
|
|
231
|
+
// _orphaned flag. Release the lock so queued callbacks
|
|
232
|
+
// can proceed. This prevents concurrent execution: the
|
|
233
|
+
// stale callback continues to hold _locked until it
|
|
234
|
+
// finishes, blocking any new acquirer.
|
|
235
|
+
this._orphaned = false;
|
|
236
|
+
this._locked = false;
|
|
237
|
+
this._processQueue();
|
|
238
|
+
}
|
|
226
239
|
// After a callback completes, if there are still queued items
|
|
227
240
|
// (but the previous _locked=false path already called
|
|
228
241
|
// _processQueue which started the next one), check whether
|
|
@@ -288,6 +301,12 @@ class AsyncLock {
|
|
|
288
301
|
* stale callback from interfering with a new acquirer that claimed
|
|
289
302
|
* the lock after forceRelease.
|
|
290
303
|
*
|
|
304
|
+
* CONCURRENT EXECUTION GUARD: If a callback is currently executing
|
|
305
|
+
* (_holding is true), forceRelease defers _locked = false to the
|
|
306
|
+
* stale callback's finally block via the _orphaned flag. This
|
|
307
|
+
* prevents a new acquirer from entering while the stale callback
|
|
308
|
+
* is still running, preserving mutual exclusion.
|
|
309
|
+
*
|
|
291
310
|
* @returns {number} Count of cleared items
|
|
292
311
|
*/
|
|
293
312
|
forceRelease() {
|
|
@@ -296,6 +315,7 @@ class AsyncLock {
|
|
|
296
315
|
// finally block — it will see generation mismatch and skip
|
|
297
316
|
// touching _locked / _processQueue.
|
|
298
317
|
this._generation++;
|
|
318
|
+
const wasHolding = this._holding;
|
|
299
319
|
this._holding = false;
|
|
300
320
|
while (this._queue.length > 0) {
|
|
301
321
|
const { reject, timer } = this._queue.shift();
|
|
@@ -303,7 +323,17 @@ class AsyncLock {
|
|
|
303
323
|
clearTimeout(timer);
|
|
304
324
|
reject(new Error('Lock force-released'));
|
|
305
325
|
}
|
|
306
|
-
|
|
326
|
+
if (wasHolding) {
|
|
327
|
+
// A callback is currently executing — defer lock release to
|
|
328
|
+
// the stale callback's finally block, which detects the
|
|
329
|
+
// orphan state via generation mismatch + _orphaned flag and
|
|
330
|
+
// releases _locked when done. This prevents concurrent
|
|
331
|
+
// execution between the stale callback and a new acquirer.
|
|
332
|
+
this._orphaned = true;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
this._locked = false;
|
|
336
|
+
}
|
|
307
337
|
return count;
|
|
308
338
|
}
|
|
309
339
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async_lock.js","sourceRoot":"","sources":["../../../modules/order/async_lock.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFG;;AAIH,gCAAwC;AAsBxC,IAAI,kBAAuB,CAAC;AAC5B,MAAM,YAAY,GAAG,IAAA,oBAAc,GAAE,CAAC;AACtC,IAAI,YAAY,EAAE,CAAC;IACf,IAAI,CAAC;QACD,kBAAkB,GAAG,YAAY,CAAC,kBAAkB,CAAC,EAAE,iBAAiB,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACL,kBAAkB,GAAG,IAAI,CAAC;IAC9B,CAAC;AACL,CAAC;KAAM,CAAC;IACJ,kBAAkB,GAAG,IAAI,CAAC;AAC9B,CAAC;AACD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,kBAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAEtE,MAAM,SAAS;IACH,MAAM,CAAmB;IACzB,OAAO,CAAU;IACjB,QAAQ,CAAU;IAClB,WAAW,CAAS;IACpB,eAAe,CAAgB;IAC/B,aAAa,CAAsB;IAC1B,OAAO,CAAS;
|
|
1
|
+
{"version":3,"file":"async_lock.js","sourceRoot":"","sources":["../../../modules/order/async_lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,9 +65,8 @@
|
|
|
65
65
|
* 21. getDustOrders(manager, partials, side) - Get all dust order IDs (public)
|
|
66
66
|
* 22. determineOrderSideByFunds(manager, currentMarketPrice) - Determine priority side
|
|
67
67
|
*
|
|
68
|
-
* SPREAD CORRECTION (
|
|
69
|
-
* 23.
|
|
70
|
-
* 24. prepareSpreadCorrectionOrders(manager, preferredSide) - Prepare correction orders
|
|
68
|
+
* SPREAD CORRECTION (1 method)
|
|
69
|
+
* 23. prepareSpreadCorrectionOrders(manager, preferredSide) - Prepare correction orders
|
|
71
70
|
*
|
|
72
71
|
* DUST DETECTION (1 method - internal)
|
|
73
72
|
* 25. _getDustOrders(manager, partials, type) - Internal dust detection helper
|
|
@@ -431,13 +430,6 @@ export declare function getDustOrders(manager: any, partials: any, side: any): P
|
|
|
431
430
|
* @returns {{ side: import('./types').OrderType|null, reason: string }} The side to correct on, or null if insufficient funds.
|
|
432
431
|
*/
|
|
433
432
|
export declare function determineOrderSideByFunds(manager: any, currentMarketPrice: any): any;
|
|
434
|
-
/**
|
|
435
|
-
* Calculate the geometric ideal size for a new order being placed during spread correction.
|
|
436
|
-
* @param {import('./types').OrderManager} manager - The manager instance.
|
|
437
|
-
* @param {import('./types').OrderType} targetType - The type of order being placed (ORDER_TYPES.BUY or ORDER_TYPES.SELL).
|
|
438
|
-
* @returns {Promise<number|null>} The calculated geometric size.
|
|
439
|
-
*/
|
|
440
|
-
export declare function calculateGeometricSizeForSpreadCorrection(manager: any, targetType: any): Promise<any>;
|
|
441
433
|
/**
|
|
442
434
|
* Prepares one or more orders to correct a wide spread.
|
|
443
435
|
* @param {import('./types').OrderManager} manager - The OrderManager instance.
|
|
@@ -445,5 +437,5 @@ export declare function calculateGeometricSizeForSpreadCorrection(manager: any,
|
|
|
445
437
|
* @returns {Promise<import('./types').SpreadCorrectionResult>}
|
|
446
438
|
* @throws {Error} If preferredSide is invalid.
|
|
447
439
|
*/
|
|
448
|
-
export declare function prepareSpreadCorrectionOrders(manager: any, preferredSide: any): Promise<any>;
|
|
440
|
+
export declare function prepareSpreadCorrectionOrders(manager: any, preferredSide: any, outOfSpread?: number): Promise<any>;
|
|
449
441
|
//# sourceMappingURL=grid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../modules/order/grid.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../modules/order/grid.ts"],"names":[],"mappings":"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"}
|
|
@@ -66,9 +66,8 @@
|
|
|
66
66
|
* 21. getDustOrders(manager, partials, side) - Get all dust order IDs (public)
|
|
67
67
|
* 22. determineOrderSideByFunds(manager, currentMarketPrice) - Determine priority side
|
|
68
68
|
*
|
|
69
|
-
* SPREAD CORRECTION (
|
|
70
|
-
* 23.
|
|
71
|
-
* 24. prepareSpreadCorrectionOrders(manager, preferredSide) - Prepare correction orders
|
|
69
|
+
* SPREAD CORRECTION (1 method)
|
|
70
|
+
* 23. prepareSpreadCorrectionOrders(manager, preferredSide) - Prepare correction orders
|
|
72
71
|
*
|
|
73
72
|
* DUST DETECTION (1 method - internal)
|
|
74
73
|
* 25. _getDustOrders(manager, partials, type) - Internal dust detection helper
|
|
@@ -151,7 +150,6 @@ exports.checkWindowDust = checkWindowDust;
|
|
|
151
150
|
exports.hasAnyDust = hasAnyDust;
|
|
152
151
|
exports.getDustOrders = getDustOrders;
|
|
153
152
|
exports.determineOrderSideByFunds = determineOrderSideByFunds;
|
|
154
|
-
exports.calculateGeometricSizeForSpreadCorrection = calculateGeometricSizeForSpreadCorrection;
|
|
155
153
|
exports.prepareSpreadCorrectionOrders = prepareSpreadCorrectionOrders;
|
|
156
154
|
const constants_1 = require("../constants");
|
|
157
155
|
const { GRID_COMPARISON } = constants_1.GRID_LIMITS;
|
|
@@ -299,9 +297,10 @@ async function _getSizingContext(manager, side, { skipRecalc = false } = {}) {
|
|
|
299
297
|
const targetBuy = Math.max(0, manager.config.activeOrders?.buy ?? 1);
|
|
300
298
|
const targetSell = Math.max(0, manager.config.activeOrders?.sell ?? 1);
|
|
301
299
|
const totalTarget = targetBuy + targetSell;
|
|
302
|
-
const
|
|
300
|
+
const btsOrderType = (0, math_1.getBtsSide)(manager.config?.assetA, manager.config?.assetB);
|
|
301
|
+
const isBtsSide = isBuy ? (btsOrderType === constants_1.ORDER_TYPES.BUY) : (btsOrderType === constants_1.ORDER_TYPES.SELL);
|
|
303
302
|
const formulaBudget = (0, math_1.calculateOrderCreationFees)(manager.config.assetA, manager.config.assetB, totalTarget, manager.config?.feeParams?.BTS_RESERVATION_MULTIPLIER);
|
|
304
|
-
budget = (0,
|
|
303
|
+
budget = (0, math_1.adjustBudgetForBtsFees)(budget, isBtsSide, formulaBudget, manager.config.min_BTS_value || 0, Format.toFiniteNumber(manager.funds?.btsBalance?.free, 0), Format.toFiniteNumber(isBuy ? (snap.allocatedBuy || 0) : (snap.allocatedSell || 0)), Format.toFiniteNumber(snap.allocatedBuy || 0)
|
|
305
304
|
+ Format.toFiniteNumber(snap.allocatedSell || 0));
|
|
306
305
|
}
|
|
307
306
|
return {
|
|
@@ -476,17 +475,6 @@ function createOrderGrid(config) {
|
|
|
476
475
|
function _clearOrderCachesLogic(manager) {
|
|
477
476
|
// Replace frozen master grid with fresh empty frozen Map (COW pattern)
|
|
478
477
|
manager.orders = Object.freeze(new Map());
|
|
479
|
-
// Clear index Sets with fresh empty Sets (mutable for _applyOrderUpdate)
|
|
480
|
-
if (manager._ordersByState) {
|
|
481
|
-
for (const key of Object.keys(manager._ordersByState)) {
|
|
482
|
-
manager._ordersByState[key] = new Set();
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (manager._ordersByType) {
|
|
486
|
-
for (const key of Object.keys(manager._ordersByType)) {
|
|
487
|
-
manager._ordersByType[key] = new Set();
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
478
|
}
|
|
491
479
|
/**
|
|
492
480
|
* Restore a persisted grid snapshot onto a manager instance.
|
|
@@ -512,8 +500,7 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
512
500
|
manager.funds.btsFeesOwed = savedBtsFeesOwed;
|
|
513
501
|
// Restore boundary index for StrategyEngine
|
|
514
502
|
if (typeof boundaryIdx === 'number') {
|
|
515
|
-
manager.boundaryIdx
|
|
516
|
-
// FIX: Use consistent optional chaining pattern for logger calls
|
|
503
|
+
manager._restoreBoundary(boundaryIdx);
|
|
517
504
|
manager.logger?.log?.(`Restored boundary index: ${boundaryIdx}`, 'info');
|
|
518
505
|
}
|
|
519
506
|
// Reassign slot types for virtual slots based on current boundary.
|
|
@@ -523,7 +510,7 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
523
510
|
if (typeof boundaryIdx === 'number') {
|
|
524
511
|
const gapSlots = calculateGapSlots(manager.config?.incrementPercent, manager.config?.targetSpreadPercent, manager.config?.gridLimits);
|
|
525
512
|
const buyEndIdx = boundaryIdx;
|
|
526
|
-
const sellStartIdx = boundaryIdx
|
|
513
|
+
const sellStartIdx = (0, math_1.getSellStartIdx)(boundaryIdx, gapSlots);
|
|
527
514
|
let reassignCount = 0;
|
|
528
515
|
grid = grid.map((slot, i) => {
|
|
529
516
|
if (slot.state === constants_1.ORDER_STATES.VIRTUAL && !slot.orderId) {
|
|
@@ -799,7 +786,7 @@ async function initializeGrid(manager) {
|
|
|
799
786
|
// RC-8: Update boundary with notification to dependent systems
|
|
800
787
|
// Persist master boundary for StrategyEngine
|
|
801
788
|
if (manager.boundaryIdx !== boundaryIdx) {
|
|
802
|
-
manager.boundaryIdx
|
|
789
|
+
manager._restoreBoundary(boundaryIdx);
|
|
803
790
|
// RC-8: Notify StrategyEngine of boundary change (if method exists)
|
|
804
791
|
if (typeof manager.notifyBoundaryUpdate === 'function') {
|
|
805
792
|
try {
|
|
@@ -1184,6 +1171,12 @@ async function updateGridFromBlockchainSnapshot(manager, orderType = 'both', fro
|
|
|
1184
1171
|
// included in the correct side's budget allocation. Running assignGridRoles
|
|
1185
1172
|
// after the size calc means crossers keep their pre-shift sizes, producing
|
|
1186
1173
|
// an allocation that doesn't match the post-shift grid structure.
|
|
1174
|
+
//
|
|
1175
|
+
// NOTE: syncBoundaryToFunds is a pure computation (no eager write), so
|
|
1176
|
+
// manager.boundaryIdx still carries the pre-shift value here. The
|
|
1177
|
+
// overrideBoundaryIdx !== manager.boundaryIdx check correctly detects
|
|
1178
|
+
// that a shift is needed. The boundary is only written atomically
|
|
1179
|
+
// inside _commitWorkingGrid via _setBoundary.
|
|
1187
1180
|
if (overrideBoundaryIdx !== null && overrideBoundaryIdx !== manager.boundaryIdx) {
|
|
1188
1181
|
const gapSlots = calculateGapSlots(manager.config.incrementPercent, manager.config.targetSpreadPercent, manager.config.gridLimits);
|
|
1189
1182
|
const allSlots = Array.from(workingGrid.values())
|
|
@@ -1289,9 +1282,14 @@ async function compareGrids(calculatedGrid, persistedGrid, manager = null) {
|
|
|
1289
1282
|
const computeSideIdeals = (activeOrders, type, ctx) => {
|
|
1290
1283
|
if (!manager || !ctx || ctx.budget <= 0 || activeOrders.length === 0)
|
|
1291
1284
|
return activeOrders;
|
|
1292
|
-
// Identify ALL slots currently assigned to this side
|
|
1293
|
-
//
|
|
1294
|
-
|
|
1285
|
+
// Identify ALL slots currently assigned to this side from the calculated
|
|
1286
|
+
// grid snapshot (not a fresh read from manager.orders). Using the snapshot
|
|
1287
|
+
// avoids TOCTOU races and ensures the slot classification is consistent with
|
|
1288
|
+
// the grid state used for the RMS computation. If a boundary shift is pending
|
|
1289
|
+
// (set via _gridSidesUpdated but not yet committed through the COW pipeline),
|
|
1290
|
+
// the types in manager.orders are stale — reading from the snapshot matches
|
|
1291
|
+
// what the rest of the comparison sees.
|
|
1292
|
+
const sideSlots = calculatedSnap
|
|
1295
1293
|
.filter((o) => o.type === type)
|
|
1296
1294
|
.sort((a, b) => (a.price ?? 0) - (b.price ?? 0));
|
|
1297
1295
|
if (sideSlots.length === 0)
|
|
@@ -1438,6 +1436,10 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1438
1436
|
// This prevents concurrent fill processing from modifying funds while we're making decisions
|
|
1439
1437
|
let correction = null;
|
|
1440
1438
|
let shouldApplyCorrection = false;
|
|
1439
|
+
if (!manager._gridLock) {
|
|
1440
|
+
manager.logger?.log?.('Spread check skipped: no grid lock available', 'warn');
|
|
1441
|
+
return { ordersPlaced: 0, partialsMoved: 0 };
|
|
1442
|
+
}
|
|
1441
1443
|
// Derive current market price from the bot's own grid (no blockchain call needed).
|
|
1442
1444
|
// Grid prices are in B/A format (e.g. BTS/XRP) so no inversion is required.
|
|
1443
1445
|
// Mid between best bid and best ask is the most current price the bot has.
|
|
@@ -1447,7 +1449,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1447
1449
|
const lastPrice = (bestBuy !== null && bestSell !== null)
|
|
1448
1450
|
? (bestBuy + bestSell) / 2
|
|
1449
1451
|
: Number(manager.config.startPrice) || 0;
|
|
1450
|
-
//
|
|
1452
|
+
// Lock guard is handled at function entry — if _gridLock is absent we return early.
|
|
1451
1453
|
let fundSnapshot = null;
|
|
1452
1454
|
// Detect empty-side condition: when one side has zero on-chain orders,
|
|
1453
1455
|
// the spread is technically infinite and shouldFlagOutOfSpread's old
|
|
@@ -1488,7 +1490,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1488
1490
|
const gapSlots = calculateGapSlots(manager.config.incrementPercent, manager.config.targetSpreadPercent, manager.config.gridLimits);
|
|
1489
1491
|
const railLen = allSlots.length;
|
|
1490
1492
|
const buyEndIdx = manager.boundaryIdx;
|
|
1491
|
-
const sellStartIdx = manager.boundaryIdx
|
|
1493
|
+
const sellStartIdx = (0, math_1.getSellStartIdx)(manager.boundaryIdx, gapSlots);
|
|
1492
1494
|
const buySideCount = Math.max(0, Math.min(railLen, buyEndIdx + 1));
|
|
1493
1495
|
const sellSideCount = Math.max(0, railLen - sellStartIdx);
|
|
1494
1496
|
// Structural resync: both sides have room but one is empty.
|
|
@@ -1514,7 +1516,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1514
1516
|
if (!decision.side)
|
|
1515
1517
|
return false;
|
|
1516
1518
|
// Perform spread correction by placing orders on the chosen side.
|
|
1517
|
-
correction = await prepareSpreadCorrectionOrders(manager, decision.side);
|
|
1519
|
+
correction = await prepareSpreadCorrectionOrders(manager, decision.side, manager.outOfSpread);
|
|
1518
1520
|
if (!correction)
|
|
1519
1521
|
return false;
|
|
1520
1522
|
let placeCount = correction.ordersToPlace?.length || 0;
|
|
@@ -1525,7 +1527,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1525
1527
|
const oppositeSide = decision.side === constants_1.ORDER_TYPES.BUY ? constants_1.ORDER_TYPES.SELL : constants_1.ORDER_TYPES.BUY;
|
|
1526
1528
|
manager.logger?.log?.(`[SPREAD] Side ${decision.side} produced zero candidates; ` +
|
|
1527
1529
|
`trying opposite side ${oppositeSide}.`, 'debug');
|
|
1528
|
-
const oppositeCorrection = await prepareSpreadCorrectionOrders(manager, oppositeSide);
|
|
1530
|
+
const oppositeCorrection = await prepareSpreadCorrectionOrders(manager, oppositeSide, manager.outOfSpread);
|
|
1529
1531
|
if (oppositeCorrection) {
|
|
1530
1532
|
correction = oppositeCorrection;
|
|
1531
1533
|
placeCount = correction.ordersToPlace?.length || 0;
|
|
@@ -1540,12 +1542,20 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1540
1542
|
shouldApplyCorrection = await manager._gridLock.acquire(executeSpreadCheck);
|
|
1541
1543
|
}
|
|
1542
1544
|
catch (err) {
|
|
1543
|
-
manager.logger?.log?.(`Error checking spread condition: ${(0, errors_1.getErrorMessage)(err)}`, '
|
|
1545
|
+
manager.logger?.log?.(`Error checking spread condition: ${(0, errors_1.getErrorMessage)(err)}`, 'error');
|
|
1546
|
+
// Track failure in recovery state for external monitoring.
|
|
1547
|
+
// Do NOT throw — the startup runtime path lacks a try/catch and
|
|
1548
|
+
// a throw would crash startup. The error is symptom of a deeper
|
|
1549
|
+
// issue (lock contention, grid inconsistency) that should be
|
|
1550
|
+
// diagnosed separately.
|
|
1551
|
+
if (manager._recoveryState) {
|
|
1552
|
+
manager._recoveryState = { ...manager._recoveryState, lastFailureAt: Date.now() };
|
|
1553
|
+
}
|
|
1544
1554
|
return { ordersPlaced: 0, partialsMoved: 0 };
|
|
1545
1555
|
}
|
|
1546
|
-
//
|
|
1547
|
-
// The lock is only needed for fund verification; order placement doesn't need it
|
|
1548
|
-
// Pre-flight fund verification
|
|
1556
|
+
// Blockchain operations are intentionally OUTSIDE the lock to reduce lock contention.
|
|
1557
|
+
// The lock is only needed for fund verification; order placement doesn't need it.
|
|
1558
|
+
// Pre-flight fund verification mitigates TOCTOU between lock release and broadcast.
|
|
1549
1559
|
if (shouldApplyCorrection && updateOrdersOnChainBatch && correction && fundSnapshot) {
|
|
1550
1560
|
const currentFunds = _snapshotFundState(manager);
|
|
1551
1561
|
const fundChanged = fundSnapshot.buyFree !== currentFunds.buyFree
|
|
@@ -1554,28 +1564,27 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1554
1564
|
|| fundSnapshot.sellLocked !== currentFunds.sellLocked;
|
|
1555
1565
|
if (fundChanged) {
|
|
1556
1566
|
// TOCTOU: fund state changed between lock release and broadcast.
|
|
1557
|
-
//
|
|
1558
|
-
//
|
|
1559
|
-
//
|
|
1560
|
-
//
|
|
1561
|
-
//
|
|
1562
|
-
//
|
|
1563
|
-
//
|
|
1564
|
-
//
|
|
1565
|
-
|
|
1566
|
-
const
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
});
|
|
1573
|
-
if (!rePlanResult.side) {
|
|
1567
|
+
// Lock-free re-plan — no _gridLock needed because the fund read
|
|
1568
|
+
// (recalculateFunds) is serialized by _fundLock internally, and
|
|
1569
|
+
// the remaining logic is read-only against COW-frozen state.
|
|
1570
|
+
// All grid-lock-needing work (fund verification, grid reads) is
|
|
1571
|
+
// done under the single outer acquire above; this re-plan runs
|
|
1572
|
+
// after that lock was released.
|
|
1573
|
+
// Refresh lastPrice from current grid state — the grid may
|
|
1574
|
+
// have changed since function entry (TOCTOU).
|
|
1575
|
+
const freshOnChain = _getOnChainOrders(manager);
|
|
1576
|
+
const freshBest = (0, math_1.getGridBestPrices)(freshOnChain.onChainBuys, freshOnChain.onChainSells);
|
|
1577
|
+
const freshPrice = (freshBest.bestBuy !== null && freshBest.bestSell !== null)
|
|
1578
|
+
? (freshBest.bestBuy + freshBest.bestSell) / 2
|
|
1579
|
+
: Number(manager.config.startPrice) || 0;
|
|
1580
|
+
const rePlanDecision = determineOrderSideByFunds(manager, freshPrice);
|
|
1581
|
+
if (!rePlanDecision.side) {
|
|
1574
1582
|
manager.logger?.log?.(`[SPREAD] Fund state changed; no side has sufficient funds for re-plan. Skipping cycle.`, 'warn');
|
|
1575
1583
|
return { ordersPlaced: 0, partialsMoved: 0 };
|
|
1576
1584
|
}
|
|
1577
|
-
|
|
1578
|
-
if (
|
|
1585
|
+
const rePlanCorrection = await prepareSpreadCorrectionOrders(manager, rePlanDecision.side, manager.outOfSpread);
|
|
1586
|
+
if (rePlanCorrection && ((rePlanCorrection.ordersToPlace?.length || 0) + (rePlanCorrection.ordersToUpdate?.length || 0) > 0)) {
|
|
1587
|
+
correction = rePlanCorrection;
|
|
1579
1588
|
fundSnapshot = currentFunds;
|
|
1580
1589
|
manager.logger?.log?.(`[SPREAD] Fund state changed between lock release and broadcast — ` +
|
|
1581
1590
|
`re-planned with updated funds: ${correction.ordersToPlace?.length || 0} creates, ` +
|
|
@@ -1832,40 +1841,6 @@ function determineOrderSideByFunds(manager, currentMarketPrice) {
|
|
|
1832
1841
|
}
|
|
1833
1842
|
return { side, reason: side ? `Choosing ${side}` : 'Insufficient funds or committed inventory' };
|
|
1834
1843
|
}
|
|
1835
|
-
/**
|
|
1836
|
-
* Calculate the geometric ideal size for a new order being placed during spread correction.
|
|
1837
|
-
* @param {import('./types').OrderManager} manager - The manager instance.
|
|
1838
|
-
* @param {import('./types').OrderType} targetType - The type of order being placed (ORDER_TYPES.BUY or ORDER_TYPES.SELL).
|
|
1839
|
-
* @returns {Promise<number|null>} The calculated geometric size.
|
|
1840
|
-
*/
|
|
1841
|
-
async function calculateGeometricSizeForSpreadCorrection(manager, targetType) {
|
|
1842
|
-
const side = targetType === constants_1.ORDER_TYPES.BUY ? 'buy' : 'sell';
|
|
1843
|
-
// Count only on-chain orders (ACTIVE+PARTIAL) to avoid diluting the
|
|
1844
|
-
// spread-correction order size across hundreds of virtual slots on a
|
|
1845
|
-
// full-rail grid. Virtual slots have their capital tracked separately
|
|
1846
|
-
// in funds.virtual and should not compete for the free budget.
|
|
1847
|
-
const slotsCount = Array.from(manager.orders.values())
|
|
1848
|
-
.filter((o) => o.type === targetType && (o.state === constants_1.ORDER_STATES.ACTIVE || o.state === constants_1.ORDER_STATES.PARTIAL))
|
|
1849
|
-
.length + 1;
|
|
1850
|
-
// Use centralized sizing context (respects botFunds % allocation)
|
|
1851
|
-
const ctx = await _getSizingContext(manager, side);
|
|
1852
|
-
if (!ctx || ctx.budget <= 0 || slotsCount < 1)
|
|
1853
|
-
return null;
|
|
1854
|
-
// ALLOW slotsCount === 1 to enable spread correction even if a side is completely missing
|
|
1855
|
-
const dummy = Array.from({ length: slotsCount }, () => ({ type: targetType }));
|
|
1856
|
-
try {
|
|
1857
|
-
const sized = (0, math_1.calculateOrderSizes)(dummy, manager.config, side === 'sell' ? ctx.budget : 0, side === 'buy' ? ctx.budget : 0, 0, 0, ctx.precision, ctx.precision);
|
|
1858
|
-
if (!Array.isArray(sized) || sized.length === 0) {
|
|
1859
|
-
manager.logger?.log?.(`calculateOrderSizes returned invalid result for spread correction`, 'warn');
|
|
1860
|
-
return null;
|
|
1861
|
-
}
|
|
1862
|
-
return side === 'sell' ? sized[0].size : sized[sized.length - 1].size;
|
|
1863
|
-
}
|
|
1864
|
-
catch (e) {
|
|
1865
|
-
manager.logger?.log?.(`Error calculating geometric size for spread correction: ${(0, errors_1.getErrorMessage)(e)}`, 'warn');
|
|
1866
|
-
return null;
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
1844
|
/**
|
|
1870
1845
|
* Prepares one or more orders to correct a wide spread.
|
|
1871
1846
|
* @param {import('./types').OrderManager} manager - The OrderManager instance.
|
|
@@ -1873,7 +1848,7 @@ async function calculateGeometricSizeForSpreadCorrection(manager, targetType) {
|
|
|
1873
1848
|
* @returns {Promise<import('./types').SpreadCorrectionResult>}
|
|
1874
1849
|
* @throws {Error} If preferredSide is invalid.
|
|
1875
1850
|
*/
|
|
1876
|
-
async function prepareSpreadCorrectionOrders(manager, preferredSide) {
|
|
1851
|
+
async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread = 0) {
|
|
1877
1852
|
// FIX: Validate preferredSide parameter to prevent silent logic errors
|
|
1878
1853
|
if (preferredSide !== constants_1.ORDER_TYPES.BUY && preferredSide !== constants_1.ORDER_TYPES.SELL) {
|
|
1879
1854
|
throw new Error(`Invalid preferredSide: ${preferredSide}. Must be '${constants_1.ORDER_TYPES.BUY}' or '${constants_1.ORDER_TYPES.SELL}'.`);
|
|
@@ -1882,7 +1857,7 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide) {
|
|
|
1882
1857
|
const ordersToUpdate = [];
|
|
1883
1858
|
const railType = preferredSide;
|
|
1884
1859
|
const sideName = railType === constants_1.ORDER_TYPES.BUY ? 'buy' : 'sell';
|
|
1885
|
-
const configuredMissingSlots = Number(
|
|
1860
|
+
const configuredMissingSlots = Number(outOfSpread || 0);
|
|
1886
1861
|
const missingSlots = configuredMissingSlots > 0
|
|
1887
1862
|
? Math.floor(configuredMissingSlots)
|
|
1888
1863
|
: 1;
|
|
@@ -1905,17 +1880,14 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide) {
|
|
|
1905
1880
|
.sort((a, b) => a.price - b.price);
|
|
1906
1881
|
const slotIndexMap = new Map(allSlotsByPrice.map((o, i) => [o.id, i]));
|
|
1907
1882
|
const gapSlots = calculateGapSlots(manager.config?.incrementPercent, manager.config?.targetSpreadPercent, manager.config?.gridLimits);
|
|
1908
|
-
//
|
|
1909
|
-
//
|
|
1910
|
-
//
|
|
1911
|
-
//
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
const bIdx = manager.boundaryIdx ?? 0;
|
|
1917
|
-
const buyEndIdx = bIdx;
|
|
1918
|
-
const sellStartIdx = bIdx + Number(gapSlots) + 1;
|
|
1883
|
+
// Use the committed boundary for slot classification — never a speculative
|
|
1884
|
+
// value from syncBoundaryToFunds that hasn't been persisted through the COW
|
|
1885
|
+
// pipeline. If the boundary shifts later via _commitWorkingGrid, the next
|
|
1886
|
+
// spread correction cycle will re-classify with the updated committed value.
|
|
1887
|
+
// This prevents TOCTOU-style inconsistency where slot types are chosen
|
|
1888
|
+
// against a boundary that was never atomically committed to manager.orders.
|
|
1889
|
+
const buyEndIdx = manager.boundaryIdx ?? 0;
|
|
1890
|
+
const sellStartIdx = (0, math_1.getSellStartIdx)(manager.boundaryIdx, gapSlots);
|
|
1919
1891
|
const getSlotCorrectType = (slot) => {
|
|
1920
1892
|
const idx = slotIndexMap.get(slot.id);
|
|
1921
1893
|
if (idx === undefined)
|