dexbot 1.4.4 → 1.4.6
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/bots_file_lock.d.ts +4 -4
- package/dist/modules/bots_file_lock.d.ts.map +1 -1
- package/dist/modules/bots_file_lock.js +16 -53
- package/dist/modules/bots_file_lock.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +10 -10
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +18 -8
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +4 -5
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +2 -2
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +8 -7
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/dexbot_startup_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_startup_runtime.js +1 -0
- package/dist/modules/dexbot_startup_runtime.js.map +1 -1
- package/dist/modules/dexbot_state_recovery.js +2 -2
- package/dist/modules/dexbot_state_recovery.js.map +1 -1
- package/dist/modules/order/async_lock.d.ts +12 -3
- package/dist/modules/order/async_lock.d.ts.map +1 -1
- package/dist/modules/order/async_lock.js +46 -8
- package/dist/modules/order/async_lock.js.map +1 -1
- package/dist/modules/order/grid.d.ts +10 -10
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +75 -92
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile.d.ts +4 -2
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile.js +70 -55
- package/dist/modules/order/grid_reconcile.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts +6 -2
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.js +101 -44
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/manager.d.ts +21 -5
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +123 -44
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/strategy.d.ts.map +1 -1
- package/dist/modules/order/strategy.js +9 -2
- package/dist/modules/order/strategy.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts +7 -3
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +10 -23
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +5 -2
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +23 -6
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +2 -1
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +8 -5
- package/dist/modules/order/utils/system.js.map +1 -1
- package/package.json +1 -1
|
@@ -68,9 +68,11 @@
|
|
|
68
68
|
*
|
|
69
69
|
* Re-entrancy detection uses AsyncLocalStorage (Node.js) to distinguish
|
|
70
70
|
* truly nested calls (same async context) from separate concurrent
|
|
71
|
-
* callers.
|
|
72
|
-
*
|
|
73
|
-
*
|
|
71
|
+
* callers. The store tracks a Set of all lock IDs held in the current
|
|
72
|
+
* async context chain, preserving outer lock identity across nested
|
|
73
|
+
* acquisitions. If AsyncLocalStorage is unavailable in the current
|
|
74
|
+
* runtime (e.g. a browser bundle shim), the lock falls back to a
|
|
75
|
+
* simpler _holding flag that treats any concurrent call as re-entrant.
|
|
74
76
|
*
|
|
75
77
|
* CRITICAL INVARIANTS:
|
|
76
78
|
* - _locked = true ONLY if callback is currently executing
|
|
@@ -106,6 +108,7 @@ class AsyncLock {
|
|
|
106
108
|
_defaultTimeout;
|
|
107
109
|
_onContention;
|
|
108
110
|
_lockId;
|
|
111
|
+
_orphaned;
|
|
109
112
|
constructor(options = {}) {
|
|
110
113
|
this._queue = [];
|
|
111
114
|
this._locked = false;
|
|
@@ -114,6 +117,7 @@ class AsyncLock {
|
|
|
114
117
|
this._defaultTimeout = options.timeout || null;
|
|
115
118
|
this._onContention = options.onContention || null;
|
|
116
119
|
this._lockId = Symbol('AsyncLock');
|
|
120
|
+
this._orphaned = false;
|
|
117
121
|
}
|
|
118
122
|
/**
|
|
119
123
|
* Acquire the lock and execute callback exclusively
|
|
@@ -128,14 +132,19 @@ class AsyncLock {
|
|
|
128
132
|
// context, run the callback directly. Uses AsyncLocalStorage (Node.js)
|
|
129
133
|
// to distinguish truly nested calls from separate concurrent callers.
|
|
130
134
|
// Falls back to _holding flag in environments without AsyncLocalStorage.
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
135
|
+
const contextSet = _lockCtx ? _lockCtx.getStore() : undefined;
|
|
136
|
+
if ((contextSet && contextSet.has(this._lockId)) || (!_lockCtx && this._holding)) {
|
|
133
137
|
return callback();
|
|
134
138
|
}
|
|
135
139
|
const timeout = options.timeout || this._defaultTimeout;
|
|
136
140
|
const cancelToken = options.cancelToken;
|
|
137
141
|
const wrappedCallback = _lockCtx
|
|
138
|
-
? () =>
|
|
142
|
+
? () => {
|
|
143
|
+
const prevSet = _lockCtx.getStore() || new Set();
|
|
144
|
+
const newSet = new Set(prevSet);
|
|
145
|
+
newSet.add(this._lockId);
|
|
146
|
+
return _lockCtx.run(newSet, callback);
|
|
147
|
+
}
|
|
139
148
|
: callback;
|
|
140
149
|
return new Promise((resolve, reject) => {
|
|
141
150
|
let timer;
|
|
@@ -223,6 +232,17 @@ class AsyncLock {
|
|
|
223
232
|
this._locked = false;
|
|
224
233
|
this._processQueue();
|
|
225
234
|
}
|
|
235
|
+
else if (this._orphaned) {
|
|
236
|
+
// Stale callback finishing after forceRelease. The
|
|
237
|
+
// forceRelease deferred _locked = false to us via the
|
|
238
|
+
// _orphaned flag. Release the lock so queued callbacks
|
|
239
|
+
// can proceed. This prevents concurrent execution: the
|
|
240
|
+
// stale callback continues to hold _locked until it
|
|
241
|
+
// finishes, blocking any new acquirer.
|
|
242
|
+
this._orphaned = false;
|
|
243
|
+
this._locked = false;
|
|
244
|
+
this._processQueue();
|
|
245
|
+
}
|
|
226
246
|
// After a callback completes, if there are still queued items
|
|
227
247
|
// (but the previous _locked=false path already called
|
|
228
248
|
// _processQueue which started the next one), check whether
|
|
@@ -247,7 +267,8 @@ class AsyncLock {
|
|
|
247
267
|
*/
|
|
248
268
|
isReentrant() {
|
|
249
269
|
if (_lockCtx) {
|
|
250
|
-
|
|
270
|
+
const store = _lockCtx.getStore();
|
|
271
|
+
return store ? store.has(this._lockId) : false;
|
|
251
272
|
}
|
|
252
273
|
return this._holding;
|
|
253
274
|
}
|
|
@@ -288,6 +309,12 @@ class AsyncLock {
|
|
|
288
309
|
* stale callback from interfering with a new acquirer that claimed
|
|
289
310
|
* the lock after forceRelease.
|
|
290
311
|
*
|
|
312
|
+
* CONCURRENT EXECUTION GUARD: If a callback is currently executing
|
|
313
|
+
* (_holding is true), forceRelease defers _locked = false to the
|
|
314
|
+
* stale callback's finally block via the _orphaned flag. This
|
|
315
|
+
* prevents a new acquirer from entering while the stale callback
|
|
316
|
+
* is still running, preserving mutual exclusion.
|
|
317
|
+
*
|
|
291
318
|
* @returns {number} Count of cleared items
|
|
292
319
|
*/
|
|
293
320
|
forceRelease() {
|
|
@@ -296,6 +323,7 @@ class AsyncLock {
|
|
|
296
323
|
// finally block — it will see generation mismatch and skip
|
|
297
324
|
// touching _locked / _processQueue.
|
|
298
325
|
this._generation++;
|
|
326
|
+
const wasHolding = this._holding;
|
|
299
327
|
this._holding = false;
|
|
300
328
|
while (this._queue.length > 0) {
|
|
301
329
|
const { reject, timer } = this._queue.shift();
|
|
@@ -303,7 +331,17 @@ class AsyncLock {
|
|
|
303
331
|
clearTimeout(timer);
|
|
304
332
|
reject(new Error('Lock force-released'));
|
|
305
333
|
}
|
|
306
|
-
|
|
334
|
+
if (wasHolding) {
|
|
335
|
+
// A callback is currently executing — defer lock release to
|
|
336
|
+
// the stale callback's finally block, which detects the
|
|
337
|
+
// orphan state via generation mismatch + _orphaned flag and
|
|
338
|
+
// releases _locked when done. This prevents concurrent
|
|
339
|
+
// execution between the stale callback and a new acquirer.
|
|
340
|
+
this._orphaned = true;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
this._locked = false;
|
|
344
|
+
}
|
|
307
345
|
return count;
|
|
308
346
|
}
|
|
309
347
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async_lock.js","sourceRoot":"","sources":["../../../modules/order/async_lock.ts"],"names":[],"mappings":";AAAA
|
|
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
|
|
@@ -193,6 +192,14 @@ export declare function _getSizingContext(manager: any, side: any, { skipRecalc
|
|
|
193
192
|
export declare function createOrderGrid(config: any): any;
|
|
194
193
|
/**
|
|
195
194
|
* Restore a persisted grid snapshot onto a manager instance.
|
|
195
|
+
*
|
|
196
|
+
* Side effects on manager:
|
|
197
|
+
* - Reassigns all slot types to match current boundary + gapSlots
|
|
198
|
+
* (fixes stale persisted types that cause ILLEGAL_SPREAD_STATE)
|
|
199
|
+
* - Sets `manager._gapSlots` to the computed gapSlots so strategy reads
|
|
200
|
+
* the creation-time value instead of recomputing from live config
|
|
201
|
+
* - Sanitizes phantom orders (ACTIVE/PARTIAL without orderId → VIRTUAL)
|
|
202
|
+
*
|
|
196
203
|
* @param {import('./types').OrderManager} manager - The manager instance.
|
|
197
204
|
* @param {Array<import('./types').GridOrderSlot>} grid - The persisted grid array.
|
|
198
205
|
* @param {number|null} [boundaryIdx=null] - The master boundary index.
|
|
@@ -431,13 +438,6 @@ export declare function getDustOrders(manager: any, partials: any, side: any): P
|
|
|
431
438
|
* @returns {{ side: import('./types').OrderType|null, reason: string }} The side to correct on, or null if insufficient funds.
|
|
432
439
|
*/
|
|
433
440
|
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
441
|
/**
|
|
442
442
|
* Prepares one or more orders to correct a wide spread.
|
|
443
443
|
* @param {import('./types').OrderManager} manager - The OrderManager instance.
|
|
@@ -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;
|
|
@@ -461,7 +459,7 @@ function createOrderGrid(config) {
|
|
|
461
459
|
buy: Math.floor(gapSlots / 2),
|
|
462
460
|
sell: gapSlots - Math.floor(gapSlots / 2)
|
|
463
461
|
};
|
|
464
|
-
return { orders: updatedOrders, boundaryIdx, initialSpreadCount };
|
|
462
|
+
return { orders: updatedOrders, boundaryIdx, initialSpreadCount, gapSlots };
|
|
465
463
|
}
|
|
466
464
|
/**
|
|
467
465
|
* Internal utility to clear all order-related manager caches.
|
|
@@ -480,6 +478,14 @@ function _clearOrderCachesLogic(manager) {
|
|
|
480
478
|
}
|
|
481
479
|
/**
|
|
482
480
|
* Restore a persisted grid snapshot onto a manager instance.
|
|
481
|
+
*
|
|
482
|
+
* Side effects on manager:
|
|
483
|
+
* - Reassigns all slot types to match current boundary + gapSlots
|
|
484
|
+
* (fixes stale persisted types that cause ILLEGAL_SPREAD_STATE)
|
|
485
|
+
* - Sets `manager._gapSlots` to the computed gapSlots so strategy reads
|
|
486
|
+
* the creation-time value instead of recomputing from live config
|
|
487
|
+
* - Sanitizes phantom orders (ACTIVE/PARTIAL without orderId → VIRTUAL)
|
|
488
|
+
*
|
|
483
489
|
* @param {import('./types').OrderManager} manager - The manager instance.
|
|
484
490
|
* @param {Array<import('./types').GridOrderSlot>} grid - The persisted grid array.
|
|
485
491
|
* @param {number|null} [boundaryIdx=null] - The master boundary index.
|
|
@@ -505,31 +511,32 @@ async function loadGrid(manager, grid, boundaryIdx = null) {
|
|
|
505
511
|
manager._restoreBoundary(boundaryIdx);
|
|
506
512
|
manager.logger?.log?.(`Restored boundary index: ${boundaryIdx}`, 'info');
|
|
507
513
|
}
|
|
508
|
-
// Reassign slot types
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
//
|
|
514
|
+
// Reassign slot types based on current boundary.
|
|
515
|
+
// Every slot's type must match its position in the price-sorted rail
|
|
516
|
+
// relative to the boundary + gapSlots. Stale persisted types cause
|
|
517
|
+
// split spread zones and ILLEGAL_SPREAD_STATE errors. The subsequent
|
|
518
|
+
// sync will detect type mismatches with chain orders (e.g. a BUY-zone
|
|
519
|
+
// slot holding a SELL chain order) and auto-cancel + recreate them.
|
|
512
520
|
if (typeof boundaryIdx === 'number') {
|
|
513
521
|
const gapSlots = calculateGapSlots(manager.config?.incrementPercent, manager.config?.targetSpreadPercent, manager.config?.gridLimits);
|
|
522
|
+
manager._gapSlots = gapSlots;
|
|
514
523
|
const buyEndIdx = boundaryIdx;
|
|
515
524
|
const sellStartIdx = (0, math_1.getSellStartIdx)(boundaryIdx, gapSlots);
|
|
516
525
|
let reassignCount = 0;
|
|
517
526
|
grid = grid.map((slot, i) => {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return { ...slot, type: correctType };
|
|
527
|
-
}
|
|
527
|
+
const correctType = (i <= buyEndIdx)
|
|
528
|
+
? constants_1.ORDER_TYPES.BUY
|
|
529
|
+
: (i >= sellStartIdx)
|
|
530
|
+
? constants_1.ORDER_TYPES.SELL
|
|
531
|
+
: constants_1.ORDER_TYPES.SPREAD;
|
|
532
|
+
if (slot.type !== correctType) {
|
|
533
|
+
reassignCount++;
|
|
534
|
+
return { ...slot, type: correctType };
|
|
528
535
|
}
|
|
529
536
|
return slot;
|
|
530
537
|
});
|
|
531
538
|
if (reassignCount > 0) {
|
|
532
|
-
manager.logger?.log?.(`[GRID-
|
|
539
|
+
manager.logger?.log?.(`[GRID-TYPE-CORRECT] Reassigned ${reassignCount} stale slot type(s) based on boundary ${boundaryIdx}`, 'warn');
|
|
533
540
|
}
|
|
534
541
|
}
|
|
535
542
|
// Gap 6: Grid size cap — validate grid slot count against expected maximum.
|
|
@@ -779,12 +786,13 @@ async function initializeGrid(manager) {
|
|
|
779
786
|
// Continuing would create grid with 0 fund allocation, rendering it non-functional
|
|
780
787
|
throw new Error(`Cannot initialize grid without account totals: ${(0, errors_1.getErrorMessage)(e)}`);
|
|
781
788
|
}
|
|
782
|
-
const { orders, boundaryIdx, initialSpreadCount } = createOrderGrid({
|
|
789
|
+
const { orders, boundaryIdx, initialSpreadCount, gapSlots } = createOrderGrid({
|
|
783
790
|
...manager.config,
|
|
784
791
|
startPrice: gridStartPrice,
|
|
785
792
|
minPrice: resolvedMinP,
|
|
786
793
|
maxPrice: resolvedMaxP,
|
|
787
794
|
});
|
|
795
|
+
manager._gapSlots = gapSlots;
|
|
788
796
|
// RC-8: Update boundary with notification to dependent systems
|
|
789
797
|
// Persist master boundary for StrategyEngine
|
|
790
798
|
if (manager.boundaryIdx !== boundaryIdx) {
|
|
@@ -1180,7 +1188,7 @@ async function updateGridFromBlockchainSnapshot(manager, orderType = 'both', fro
|
|
|
1180
1188
|
// that a shift is needed. The boundary is only written atomically
|
|
1181
1189
|
// inside _commitWorkingGrid via _setBoundary.
|
|
1182
1190
|
if (overrideBoundaryIdx !== null && overrideBoundaryIdx !== manager.boundaryIdx) {
|
|
1183
|
-
const gapSlots = calculateGapSlots(manager.config.incrementPercent, manager.config.targetSpreadPercent, manager.config.gridLimits);
|
|
1191
|
+
const gapSlots = manager._gapSlots ?? calculateGapSlots(manager.config.incrementPercent, manager.config.targetSpreadPercent, manager.config.gridLimits);
|
|
1184
1192
|
const allSlots = Array.from(workingGrid.values())
|
|
1185
1193
|
.filter((s) => s.price != null)
|
|
1186
1194
|
.sort((a, b) => a.price - b.price);
|
|
@@ -1438,6 +1446,10 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1438
1446
|
// This prevents concurrent fill processing from modifying funds while we're making decisions
|
|
1439
1447
|
let correction = null;
|
|
1440
1448
|
let shouldApplyCorrection = false;
|
|
1449
|
+
if (!manager._gridLock) {
|
|
1450
|
+
manager.logger?.log?.('Spread check skipped: no grid lock available', 'warn');
|
|
1451
|
+
return { ordersPlaced: 0, partialsMoved: 0 };
|
|
1452
|
+
}
|
|
1441
1453
|
// Derive current market price from the bot's own grid (no blockchain call needed).
|
|
1442
1454
|
// Grid prices are in B/A format (e.g. BTS/XRP) so no inversion is required.
|
|
1443
1455
|
// Mid between best bid and best ask is the most current price the bot has.
|
|
@@ -1447,7 +1459,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1447
1459
|
const lastPrice = (bestBuy !== null && bestSell !== null)
|
|
1448
1460
|
? (bestBuy + bestSell) / 2
|
|
1449
1461
|
: Number(manager.config.startPrice) || 0;
|
|
1450
|
-
//
|
|
1462
|
+
// Lock guard is handled at function entry — if _gridLock is absent we return early.
|
|
1451
1463
|
let fundSnapshot = null;
|
|
1452
1464
|
// Detect empty-side condition: when one side has zero on-chain orders,
|
|
1453
1465
|
// the spread is technically infinite and shouldFlagOutOfSpread's old
|
|
@@ -1485,7 +1497,7 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1485
1497
|
const allSlots = Array.from(manager.orders.values())
|
|
1486
1498
|
.filter((o) => o.price != null)
|
|
1487
1499
|
.sort((a, b) => a.price - b.price);
|
|
1488
|
-
const gapSlots = calculateGapSlots(manager.config.incrementPercent, manager.config.targetSpreadPercent, manager.config.gridLimits);
|
|
1500
|
+
const gapSlots = manager._gapSlots ?? calculateGapSlots(manager.config.incrementPercent, manager.config.targetSpreadPercent, manager.config.gridLimits);
|
|
1489
1501
|
const railLen = allSlots.length;
|
|
1490
1502
|
const buyEndIdx = manager.boundaryIdx;
|
|
1491
1503
|
const sellStartIdx = (0, math_1.getSellStartIdx)(manager.boundaryIdx, gapSlots);
|
|
@@ -1540,12 +1552,20 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1540
1552
|
shouldApplyCorrection = await manager._gridLock.acquire(executeSpreadCheck);
|
|
1541
1553
|
}
|
|
1542
1554
|
catch (err) {
|
|
1543
|
-
manager.logger?.log?.(`Error checking spread condition: ${(0, errors_1.getErrorMessage)(err)}`, '
|
|
1555
|
+
manager.logger?.log?.(`Error checking spread condition: ${(0, errors_1.getErrorMessage)(err)}`, 'error');
|
|
1556
|
+
// Track failure in recovery state for external monitoring.
|
|
1557
|
+
// Do NOT throw — the startup runtime path lacks a try/catch and
|
|
1558
|
+
// a throw would crash startup. The error is symptom of a deeper
|
|
1559
|
+
// issue (lock contention, grid inconsistency) that should be
|
|
1560
|
+
// diagnosed separately.
|
|
1561
|
+
if (manager._recoveryState) {
|
|
1562
|
+
manager._recoveryState = { ...manager._recoveryState, lastFailureAt: Date.now() };
|
|
1563
|
+
}
|
|
1544
1564
|
return { ordersPlaced: 0, partialsMoved: 0 };
|
|
1545
1565
|
}
|
|
1546
|
-
//
|
|
1547
|
-
// The lock is only needed for fund verification; order placement doesn't need it
|
|
1548
|
-
// Pre-flight fund verification
|
|
1566
|
+
// Blockchain operations are intentionally OUTSIDE the lock to reduce lock contention.
|
|
1567
|
+
// The lock is only needed for fund verification; order placement doesn't need it.
|
|
1568
|
+
// Pre-flight fund verification mitigates TOCTOU between lock release and broadcast.
|
|
1549
1569
|
if (shouldApplyCorrection && updateOrdersOnChainBatch && correction && fundSnapshot) {
|
|
1550
1570
|
const currentFunds = _snapshotFundState(manager);
|
|
1551
1571
|
const fundChanged = fundSnapshot.buyFree !== currentFunds.buyFree
|
|
@@ -1554,28 +1574,27 @@ async function checkSpreadCondition(manager, _BitShares, updateOrdersOnChainBatc
|
|
|
1554
1574
|
|| fundSnapshot.sellLocked !== currentFunds.sellLocked;
|
|
1555
1575
|
if (fundChanged) {
|
|
1556
1576
|
// 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) {
|
|
1577
|
+
// Lock-free re-plan — no _gridLock needed because the fund read
|
|
1578
|
+
// (recalculateFunds) is serialized by _fundLock internally, and
|
|
1579
|
+
// the remaining logic is read-only against COW-frozen state.
|
|
1580
|
+
// All grid-lock-needing work (fund verification, grid reads) is
|
|
1581
|
+
// done under the single outer acquire above; this re-plan runs
|
|
1582
|
+
// after that lock was released.
|
|
1583
|
+
// Refresh lastPrice from current grid state — the grid may
|
|
1584
|
+
// have changed since function entry (TOCTOU).
|
|
1585
|
+
const freshOnChain = _getOnChainOrders(manager);
|
|
1586
|
+
const freshBest = (0, math_1.getGridBestPrices)(freshOnChain.onChainBuys, freshOnChain.onChainSells);
|
|
1587
|
+
const freshPrice = (freshBest.bestBuy !== null && freshBest.bestSell !== null)
|
|
1588
|
+
? (freshBest.bestBuy + freshBest.bestSell) / 2
|
|
1589
|
+
: Number(manager.config.startPrice) || 0;
|
|
1590
|
+
const rePlanDecision = determineOrderSideByFunds(manager, freshPrice);
|
|
1591
|
+
if (!rePlanDecision.side) {
|
|
1574
1592
|
manager.logger?.log?.(`[SPREAD] Fund state changed; no side has sufficient funds for re-plan. Skipping cycle.`, 'warn');
|
|
1575
1593
|
return { ordersPlaced: 0, partialsMoved: 0 };
|
|
1576
1594
|
}
|
|
1577
|
-
|
|
1578
|
-
if (
|
|
1595
|
+
const rePlanCorrection = await prepareSpreadCorrectionOrders(manager, rePlanDecision.side, manager.outOfSpread);
|
|
1596
|
+
if (rePlanCorrection && ((rePlanCorrection.ordersToPlace?.length || 0) + (rePlanCorrection.ordersToUpdate?.length || 0) > 0)) {
|
|
1597
|
+
correction = rePlanCorrection;
|
|
1579
1598
|
fundSnapshot = currentFunds;
|
|
1580
1599
|
manager.logger?.log?.(`[SPREAD] Fund state changed between lock release and broadcast — ` +
|
|
1581
1600
|
`re-planned with updated funds: ${correction.ordersToPlace?.length || 0} creates, ` +
|
|
@@ -1832,40 +1851,6 @@ function determineOrderSideByFunds(manager, currentMarketPrice) {
|
|
|
1832
1851
|
}
|
|
1833
1852
|
return { side, reason: side ? `Choosing ${side}` : 'Insufficient funds or committed inventory' };
|
|
1834
1853
|
}
|
|
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
1854
|
/**
|
|
1870
1855
|
* Prepares one or more orders to correct a wide spread.
|
|
1871
1856
|
* @param {import('./types').OrderManager} manager - The OrderManager instance.
|
|
@@ -1904,17 +1889,15 @@ async function prepareSpreadCorrectionOrders(manager, preferredSide, outOfSpread
|
|
|
1904
1889
|
.filter((o) => o.price != null && Number.isFinite(o.price))
|
|
1905
1890
|
.sort((a, b) => a.price - b.price);
|
|
1906
1891
|
const slotIndexMap = new Map(allSlotsByPrice.map((o, i) => [o.id, i]));
|
|
1907
|
-
const gapSlots = calculateGapSlots(manager.config?.incrementPercent, manager.config?.targetSpreadPercent, manager.config?.gridLimits);
|
|
1908
|
-
//
|
|
1909
|
-
//
|
|
1910
|
-
//
|
|
1911
|
-
//
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
const buyEndIdx = bIdx;
|
|
1917
|
-
const sellStartIdx = (0, math_1.getSellStartIdx)(bIdx, gapSlots);
|
|
1892
|
+
const gapSlots = manager._gapSlots ?? calculateGapSlots(manager.config?.incrementPercent, manager.config?.targetSpreadPercent, manager.config?.gridLimits);
|
|
1893
|
+
// Use the committed boundary for slot classification — never a speculative
|
|
1894
|
+
// value from syncBoundaryToFunds that hasn't been persisted through the COW
|
|
1895
|
+
// pipeline. If the boundary shifts later via _commitWorkingGrid, the next
|
|
1896
|
+
// spread correction cycle will re-classify with the updated committed value.
|
|
1897
|
+
// This prevents TOCTOU-style inconsistency where slot types are chosen
|
|
1898
|
+
// against a boundary that was never atomically committed to manager.orders.
|
|
1899
|
+
const buyEndIdx = manager.boundaryIdx ?? 0;
|
|
1900
|
+
const sellStartIdx = (0, math_1.getSellStartIdx)(manager.boundaryIdx, gapSlots);
|
|
1918
1901
|
const getSlotCorrectType = (slot) => {
|
|
1919
1902
|
const idx = slotIndexMap.get(slot.id);
|
|
1920
1903
|
if (idx === undefined)
|