dexbot 1.5.1 → 1.5.3
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/CHANGELOG.md +35 -0
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/resolve_source.ts +3 -0
- package/analysis/results/ama_sweep_results_lp_pool_133_1h.json +2455 -0
- package/analysis/results/bot_fitting_results_lp_pool_133_1h.json +218 -0
- package/analysis/tradingview/README.md +5 -1
- package/analysis/tradingview/analyze_tradingview.ts +27 -1
- package/analysis/tradingview/h-bts_tradingview.html +1570 -0
- package/analysis/tradingview/t-bts_tradingview.html +1570 -0
- package/analysis/tradingview/tradingview_uplot_chart_generator.ts +351 -22
- package/analysis/trend_detection/package.json +1 -1
- package/claw/package.json +1 -1
- package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
- package/claw/runtimes/openclaw-plugin/package.json +1 -1
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/dist/analysis/resolve_source.d.ts.map +1 -1
- package/dist/analysis/resolve_source.js +3 -0
- package/dist/analysis/resolve_source.js.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.d.ts +3 -0
- package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.js +27 -2
- package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +348 -22
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
- package/dist/dexbot.js +5 -5
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/core/asymmetric_bounds.d.ts +33 -1
- package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -1
- package/dist/market_adapter/core/asymmetric_bounds.js +38 -1
- package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +2 -18
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +38 -37
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +722 -557
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +7 -1
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +31 -51
- 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 +42 -3
- package/dist/modules/dexbot_startup_runtime.js.map +1 -1
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +19 -2
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/export.d.ts +122 -15
- package/dist/modules/order/export.d.ts.map +1 -1
- package/dist/modules/order/export.js +310 -46
- package/dist/modules/order/export.js.map +1 -1
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +29 -38
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile.js +7 -3
- 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 +31 -33
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/manager.d.ts +13 -6
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +22 -7
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +231 -11
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +38 -15
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +55 -16
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +116 -20
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +340 -35
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +1 -30
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +31 -98
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/validate.d.ts +3 -2
- package/dist/modules/order/utils/validate.d.ts.map +1 -1
- package/dist/modules/order/utils/validate.js +49 -28
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/dist/scripts/analyze-credit.d.ts.map +1 -1
- package/dist/scripts/analyze-credit.js +53 -33
- package/dist/scripts/analyze-credit.js.map +1 -1
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +7 -5
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
- package/docs/README.md +2 -2
- package/docs/WORKFLOW.md +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Includes grid indexing, order comparison, delta building, and strategy calculations.
|
|
6
6
|
*
|
|
7
7
|
* ===============================================================================
|
|
8
|
-
* TABLE OF CONTENTS (
|
|
8
|
+
* TABLE OF CONTENTS (35 exported functions)
|
|
9
9
|
* ===============================================================================
|
|
10
10
|
*
|
|
11
11
|
* SECTION 1: CHAIN ORDER MATCHING & RECONCILIATION (5 functions)
|
|
@@ -46,9 +46,8 @@
|
|
|
46
46
|
* - checkSizeThreshold(size, threshold) - Check if size exceeds threshold
|
|
47
47
|
* - checkSizesBeforeMinimum(sizes, minSize) - Check sizes against minimum
|
|
48
48
|
*
|
|
49
|
-
* SECTION 7: GRID BOUNDARY & ROLES (
|
|
49
|
+
* SECTION 7: GRID BOUNDARY & ROLES (3 functions)
|
|
50
50
|
* - calculateIdealBoundary(allSlots, startPrice, gapSlots) - Calculate ideal boundary
|
|
51
|
-
* - calculateFundDrivenBoundary(allSlots, availA, availB, startPrice, gapSlots) - Fund-driven boundary
|
|
52
51
|
* - assignGridRoles(allSlots, boundaryIdx, gapSlots, ...) - Assign BUY/SELL roles
|
|
53
52
|
* - shouldFlagOutOfSpread(order, startPrice, configSpread) - Check if order is out of spread
|
|
54
53
|
*
|
|
@@ -68,7 +67,7 @@
|
|
|
68
67
|
*
|
|
69
68
|
* ===============================================================================
|
|
70
69
|
*/
|
|
71
|
-
import { ORDER_TYPES, ORDER_STATES, TIMING, FEE_PARAMETERS, GRID_LIMITS, NATIVE_CLIENT, COW_PERFORMANCE } from '../../constants.js';
|
|
70
|
+
import { ORDER_TYPES, ORDER_STATES, TIMING, FEE_PARAMETERS, GRID_LIMITS, NATIVE_CLIENT, COW_PERFORMANCE, COW_ACTIONS } from '../../constants.js';
|
|
72
71
|
import * as Format from '../format.js';
|
|
73
72
|
import * as MathUtils from './math.js';
|
|
74
73
|
import Logger from '../../order/logger.js';
|
|
@@ -461,11 +460,13 @@ async function correctOrderPriceOnChain(manager, correctionInfo, accountName, pr
|
|
|
461
460
|
let shouldRemove = false;
|
|
462
461
|
// CROSSING-PLACEMENT GUARD: re-pricing the chain order to its slot's
|
|
463
462
|
// committed price must not cross an opposite-side live order (only
|
|
464
|
-
// reachable when the grid geometry itself is broken).
|
|
463
|
+
// reachable when the grid geometry itself is broken). Candidates are the
|
|
464
|
+
// shared master + pending-broadcast + orphan set so a re-price cannot
|
|
465
|
+
// cross a pending CREATE from an earlier uncertain batch. Drop the entry —
|
|
465
466
|
// the next sync's price-mismatch detection re-queues the correction
|
|
466
467
|
// once the crossed order is resolved (same lifecycle as a 'skipped'
|
|
467
468
|
// update below).
|
|
468
|
-
const crossed = MathUtils.findCrossedOrder(manager
|
|
469
|
+
const crossed = MathUtils.findCrossedOrder(buildCrossingCheckCandidates(manager), expectedPrice, type, manager.assets, (o) => isCrossingCheckCandidate(o, chainOrderId));
|
|
469
470
|
if (crossed) {
|
|
470
471
|
manager.logger?.log?.(`[CROSS-GUARD] Skipping price correction for ${chainOrderId} -> ${type} @${expectedPrice}: ` +
|
|
471
472
|
`crosses live ${crossed.type} ${crossed.id} (${crossed.orderId}) @${crossed.price}; ` +
|
|
@@ -1001,10 +1002,14 @@ function parseSlotIndex(id) {
|
|
|
1001
1002
|
return parseSlotIndexShared(id);
|
|
1002
1003
|
}
|
|
1003
1004
|
/**
|
|
1004
|
-
* Whether a parsed chain order matches a grid slot
|
|
1005
|
-
* type-compatible (slot may be SPREAD), price
|
|
1006
|
-
* 1% quantum tolerance (floor 2
|
|
1007
|
-
*
|
|
1005
|
+
* Whether a parsed chain order matches a grid slot exactly:
|
|
1006
|
+
* type-compatible (slot may be SPREAD), price strictly equal via integer
|
|
1007
|
+
* round-trip (single epsilon), size within 1% quantum tolerance (floor 2
|
|
1008
|
+
* units). STRICT genesis-slot-mapping matcher — use only where the slot is
|
|
1009
|
+
* derived from the price (nearest-slot authority). Uncertain-landed
|
|
1010
|
+
* adoption (a broadcast whose on-chain price may have drifted by dust)
|
|
1011
|
+
* must use chainOrderMatchesSlotWithTolerance instead, or the drifted
|
|
1012
|
+
* order is never adopted and gets re-broadcast as a duplicate.
|
|
1008
1013
|
* @param {Object} parsed - parseChainOrder output ({type, price, size, ...})
|
|
1009
1014
|
* @param {Object} slot - Grid slot order object
|
|
1010
1015
|
* @param {Object} assets - Manager assets ({assetA, assetB} with precision)
|
|
@@ -1027,6 +1032,131 @@ function chainOrderMatchesSlot(parsed, slot, assets) {
|
|
|
1027
1032
|
return false;
|
|
1028
1033
|
return true;
|
|
1029
1034
|
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Whether a parsed chain order matches a grid slot within price tolerance:
|
|
1037
|
+
* type-compatible (slot may be SPREAD), price within calculatePriceTolerance
|
|
1038
|
+
* (clamped to ~2 price quanta so dust-inflated tolerances cannot adopt a
|
|
1039
|
+
* wrong order), size within the same 1% quantum tolerance as the strict
|
|
1040
|
+
* matcher. For the UNCERTAIN-ADOPT paths only (a just-broadcast create whose
|
|
1041
|
+
* on-chain price may have drifted by rounding dust): the strict
|
|
1042
|
+
* chainOrderMatchesSlot would miss the drifted order, the slot would stay
|
|
1043
|
+
* VIRTUAL, and the next cycle would re-broadcast it as a duplicate.
|
|
1044
|
+
* Genesis slot mapping keeps the strict matcher (nearest-slot authority).
|
|
1045
|
+
* @param {Object} parsed - parseChainOrder output ({type, price, size, ...})
|
|
1046
|
+
* @param {Object} slot - Grid slot order object
|
|
1047
|
+
* @param {Object} assets - Manager assets ({assetA, assetB} with precision)
|
|
1048
|
+
* @returns {boolean}
|
|
1049
|
+
*/
|
|
1050
|
+
function chainOrderMatchesSlotWithTolerance(parsed, slot, assets) {
|
|
1051
|
+
if (!parsed || !slot || !assets)
|
|
1052
|
+
return false;
|
|
1053
|
+
if (parsed.type !== slot.type && slot.type !== ORDER_TYPES.SPREAD)
|
|
1054
|
+
return false;
|
|
1055
|
+
const precision = parsed.type === ORDER_TYPES.SELL ? assets.assetA.precision : assets.assetB.precision;
|
|
1056
|
+
let tolerance = null;
|
|
1057
|
+
try {
|
|
1058
|
+
tolerance = MathUtils.calculatePriceTolerance(Math.min(parsed.price, slot.price), Math.max(parsed.size, slot.size), parsed.type, assets);
|
|
1059
|
+
}
|
|
1060
|
+
catch {
|
|
1061
|
+
tolerance = null;
|
|
1062
|
+
}
|
|
1063
|
+
// Clamp to ~2 price quanta (relative): dust-sized orders inflate the
|
|
1064
|
+
// tolerance past the grid increment, which would adopt a wrong order.
|
|
1065
|
+
// A null tolerance (invalid inputs) falls back to the clamp itself.
|
|
1066
|
+
const quantumCap = 2 * MathUtils.quantumForPrecision(precision) * Math.max(1, Math.max(parsed.price, slot.price));
|
|
1067
|
+
if (tolerance == null || !Number.isFinite(tolerance))
|
|
1068
|
+
tolerance = quantumCap;
|
|
1069
|
+
else
|
|
1070
|
+
tolerance = Math.min(tolerance, quantumCap);
|
|
1071
|
+
if (Math.abs(parsed.price - slot.price) > tolerance)
|
|
1072
|
+
return false;
|
|
1073
|
+
const sizeTolerance = Math.max(2, Math.floor(floatToBlockchainInt(slot.size, precision) * 0.01));
|
|
1074
|
+
if (Math.abs(floatToBlockchainInt(parsed.size, precision) - floatToBlockchainInt(slot.size, precision)) > sizeTolerance)
|
|
1075
|
+
return false;
|
|
1076
|
+
return true;
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Identity of a crossing-check candidate. Master orders carry orderId,
|
|
1080
|
+
* unmatched chain orders carry chainOrderId, pending-broadcast wrappers
|
|
1081
|
+
* carry slotId + order (their inner order has no chain id yet — it may not
|
|
1082
|
+
* even be on chain). All three classes must be visible to crossing guards:
|
|
1083
|
+
* an UPDATE-only rotation batch can otherwise re-price across a pending
|
|
1084
|
+
* CREATE from an earlier uncertain batch and self-trade (BitShares has no
|
|
1085
|
+
* self-trade prevention).
|
|
1086
|
+
* @param {Object} o - Candidate order or pending-broadcast wrapper entry
|
|
1087
|
+
* @returns {string|null} Chain/slot identity, or null when not placeable
|
|
1088
|
+
*/
|
|
1089
|
+
function crossingCandidateChainId(o) {
|
|
1090
|
+
if (!o)
|
|
1091
|
+
return null;
|
|
1092
|
+
if (o.orderId)
|
|
1093
|
+
return o.orderId;
|
|
1094
|
+
if (o.chainOrderId)
|
|
1095
|
+
return o.chainOrderId;
|
|
1096
|
+
if (o.slotId && o.order)
|
|
1097
|
+
return o.slotId;
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Shared predicate for every crossing-placement guard (COW create/rotation/
|
|
1102
|
+
* fallback, startup reconcile placement, price-correction re-queue). Accepts
|
|
1103
|
+
* master orders (orderId), unmatched chain orders (chainOrderId), AND
|
|
1104
|
+
* pending-broadcast wrappers (slotId + order) — pending entries can never be
|
|
1105
|
+
* in cancelOpIndexByOrderId (keyed by chain ids; wrapper orderIds are slot
|
|
1106
|
+
* ids in a disjoint namespace), so the exclusion check is harmless for them.
|
|
1107
|
+
* VIRTUAL slots (no orderId) are rejected: they have no chain presence and
|
|
1108
|
+
* must never block placements.
|
|
1109
|
+
* @param {Object} o - Candidate order or pending-broadcast wrapper entry
|
|
1110
|
+
* @param {string|null} [excludeChainOrderId=null] - Chain id to exempt (the order being relocated itself)
|
|
1111
|
+
* @param {Map|null} [cancelOpIndexByOrderId=null] - orderId -> op index of its already-queued cancel
|
|
1112
|
+
* @returns {boolean} True when the candidate participates in crossing checks
|
|
1113
|
+
*/
|
|
1114
|
+
function isCrossingCheckCandidate(o, excludeChainOrderId = null, cancelOpIndexByOrderId = null) {
|
|
1115
|
+
if (!o)
|
|
1116
|
+
return false;
|
|
1117
|
+
const oid = crossingCandidateChainId(o);
|
|
1118
|
+
if (!oid)
|
|
1119
|
+
return false;
|
|
1120
|
+
if (excludeChainOrderId && oid === excludeChainOrderId)
|
|
1121
|
+
return false;
|
|
1122
|
+
if (cancelOpIndexByOrderId instanceof Map && cancelOpIndexByOrderId.has(oid))
|
|
1123
|
+
return false;
|
|
1124
|
+
const price = o.price ?? o.order?.price;
|
|
1125
|
+
if (price == null || !Number.isFinite(Number(price)))
|
|
1126
|
+
return false;
|
|
1127
|
+
return true;
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Shared candidate set for crossing-placement checks: master orders plus
|
|
1131
|
+
* chain-side orders that may exist on chain but are not (yet) adopted into
|
|
1132
|
+
* the master grid — pending-broadcast wrappers from earlier uncertain
|
|
1133
|
+
* batches (pushed as wrappers so the predicate can see their slotId; their
|
|
1134
|
+
* inner order carries type/price via findCrossedOrder's item.order fallback)
|
|
1135
|
+
* and unmatched chain orders (orphans). Without these, an UPDATE-only
|
|
1136
|
+
* rotation batch can re-price across an un-adopted chain order that
|
|
1137
|
+
* master-grid-only checks cannot see (the pending/unmatched batch guards
|
|
1138
|
+
* fire only for CREATE batches).
|
|
1139
|
+
* @param {Object} manager - OrderManager instance (orders Map, _pendingBroadcasts, _lastUnmatchedChainOrders)
|
|
1140
|
+
* @returns {any[]} Candidate orders/wrappers for findCrossedOrder
|
|
1141
|
+
*/
|
|
1142
|
+
function buildCrossingCheckCandidates(manager) {
|
|
1143
|
+
if (!manager)
|
|
1144
|
+
return [];
|
|
1145
|
+
const candidates = manager.orders instanceof Map ? [...manager.orders.values()] : [];
|
|
1146
|
+
if (manager._pendingBroadcasts instanceof Map) {
|
|
1147
|
+
for (const entry of manager._pendingBroadcasts.values()) {
|
|
1148
|
+
if (entry && entry.slotId && entry.order)
|
|
1149
|
+
candidates.push(entry);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
if (Array.isArray(manager._lastUnmatchedChainOrders)) {
|
|
1153
|
+
for (const o of manager._lastUnmatchedChainOrders) {
|
|
1154
|
+
if (o && o.type != null && o.price != null)
|
|
1155
|
+
candidates.push(o);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return candidates;
|
|
1159
|
+
}
|
|
1030
1160
|
// ================================================================================
|
|
1031
1161
|
// SECTION 4-6: FILTERING, PREDICATES & SIZE VALIDATION
|
|
1032
1162
|
// ================================================================================
|
|
@@ -1300,26 +1430,6 @@ function calculateIdealBoundary(allSlots, referencePrice, gapSlots) {
|
|
|
1300
1430
|
const buySpread = Math.floor(gapSlots / 2);
|
|
1301
1431
|
return Math.max(0, Math.min(allSlots.length - 1, splitIdx - buySpread - 1));
|
|
1302
1432
|
}
|
|
1303
|
-
/**
|
|
1304
|
-
* Calculate grid boundary based on available funds ratio.
|
|
1305
|
-
* Distributes buy/sell slots proportional to fund values.
|
|
1306
|
-
*
|
|
1307
|
-
* @param {Array<Object>} allSlots - All grid slots sorted by price
|
|
1308
|
-
* @param {number} availA - Available assetA (sell-side capital)
|
|
1309
|
-
* @param {number} availB - Available assetB (buy-side capital)
|
|
1310
|
-
* @param {number} price - Current reference price for valuation
|
|
1311
|
-
* @param {number} gapSlots - Number of gap slots between buy and sell
|
|
1312
|
-
* @returns {number} Fund-driven boundary index
|
|
1313
|
-
*/
|
|
1314
|
-
function calculateFundDrivenBoundary(allSlots, availA, availB, price, gapSlots) {
|
|
1315
|
-
const valA = toFiniteNumber(availA) * toFiniteNumber(price);
|
|
1316
|
-
const valB = toFiniteNumber(availB);
|
|
1317
|
-
const totalVal = valA + valB;
|
|
1318
|
-
if (totalVal <= 0)
|
|
1319
|
-
return Math.floor((allSlots.length - gapSlots) / 2);
|
|
1320
|
-
const targetBuySlots = Math.round((allSlots.length - gapSlots) * (valB / totalVal));
|
|
1321
|
-
return Math.max(0, Math.min(allSlots.length - gapSlots - 1, targetBuySlots - 1));
|
|
1322
|
-
}
|
|
1323
1433
|
/**
|
|
1324
1434
|
* Assign BUY/SELL/SPREAD roles to grid slots based on boundary.
|
|
1325
1435
|
* Slots below boundary are BUY, above boundary are SELL, between are SPREAD.
|
|
@@ -1675,10 +1785,10 @@ function deriveTargetBoundary(fills, currentBoundaryIdx, allSlots, config, gapSl
|
|
|
1675
1785
|
}
|
|
1676
1786
|
const remainingBudget = effectiveBudget - Math.abs(netShift);
|
|
1677
1787
|
newBoundaryIdx += netShift;
|
|
1678
|
-
// Clamp boundary — cap at one slot before the gap band's SELL rail
|
|
1679
|
-
//
|
|
1680
|
-
//
|
|
1681
|
-
//
|
|
1788
|
+
// Clamp boundary — cap at one slot before the gap band's SELL rail.
|
|
1789
|
+
// Degenerate geometries (fewer slots than the gap needs) fall back to the
|
|
1790
|
+
// legacy length-1 ceiling instead of collapsing the boundary below its
|
|
1791
|
+
// current position.
|
|
1682
1792
|
const gapAwareCeiling = allSlots.length - gapSlots - 1;
|
|
1683
1793
|
const legacyCeiling = allSlots.length - 1;
|
|
1684
1794
|
const ceiling = gapAwareCeiling >= 0
|
|
@@ -1754,5 +1864,200 @@ function calculateBudgetedSizes(slots, side, budget, weightDist, incrementPercen
|
|
|
1754
1864
|
0, precision);
|
|
1755
1865
|
}
|
|
1756
1866
|
// ================================================================================
|
|
1757
|
-
|
|
1867
|
+
// SECTION: COW batch-shared pure helpers (moved from dexbot_cow_runtime.ts —
|
|
1868
|
+
// no bot dependency; shared by the COW runtime and any future consumer).
|
|
1869
|
+
// ================================================================================
|
|
1870
|
+
/**
|
|
1871
|
+
* Whether a chain order still matches the cached pre-update state the
|
|
1872
|
+
* limit_order_update delta was built from. Only a provably-unchanged order
|
|
1873
|
+
* makes a re-broadcast of the identical delta safe (it applies to the same
|
|
1874
|
+
* base). Any other state (target applied, filled, resized) must defer.
|
|
1875
|
+
* @param {Object} chainOrder - Raw chain order object (get_full_accounts)
|
|
1876
|
+
* @param {Object|null} cachedRaw - The rawOnChain cache captured at build time
|
|
1877
|
+
* @returns {boolean}
|
|
1878
|
+
*/
|
|
1879
|
+
function chainOrderUnchangedFromCache(chainOrder, cachedRaw) {
|
|
1880
|
+
if (!chainOrder || !cachedRaw)
|
|
1881
|
+
return false;
|
|
1882
|
+
const base = chainOrder.sell_price?.base;
|
|
1883
|
+
const quote = chainOrder.sell_price?.quote;
|
|
1884
|
+
const cachedBase = cachedRaw.sell_price?.base?.amount;
|
|
1885
|
+
const cachedQuote = cachedRaw.sell_price?.quote?.amount;
|
|
1886
|
+
const cachedForSale = cachedRaw.for_sale;
|
|
1887
|
+
if (base === undefined || quote === undefined)
|
|
1888
|
+
return false;
|
|
1889
|
+
if (cachedForSale === undefined || cachedBase === undefined || cachedQuote === undefined)
|
|
1890
|
+
return false;
|
|
1891
|
+
return String(base.amount ?? '') === String(cachedBase)
|
|
1892
|
+
&& String(quote.amount ?? '') === String(cachedQuote)
|
|
1893
|
+
&& String(chainOrder.for_sale ?? '') === String(cachedForSale);
|
|
1894
|
+
}
|
|
1895
|
+
/**
|
|
1896
|
+
* PRE-BROADCAST CROSSED-BOOK ASSERT (defense-in-depth, any-writer detection).
|
|
1897
|
+
*
|
|
1898
|
+
* Simulates the post-batch book: currently placed master orders plus this
|
|
1899
|
+
* batch's action overlay (CREATEs add, CANCELs remove, UPDATEs reprice/move).
|
|
1900
|
+
* Returns a detail string when a planned BUY would price at-or-above a planned
|
|
1901
|
+
* SELL — a state no honest planner produces — so the caller can refuse the
|
|
1902
|
+
* broadcast instead of paying for adverse fills. Placed order prices are
|
|
1903
|
+
* independent of grid geometry, so this catches boundary overruns regardless
|
|
1904
|
+
* of which writer produced them.
|
|
1905
|
+
*
|
|
1906
|
+
* Detector only: any internal failure returns null (never blocks a broadcast).
|
|
1907
|
+
*/
|
|
1908
|
+
function detectCrossedBookPlan(manager, actions) {
|
|
1909
|
+
try {
|
|
1910
|
+
const startPrice = Number(manager?.config?.startPrice);
|
|
1911
|
+
const book = new Map();
|
|
1912
|
+
for (const o of Array.from(manager?.orders?.values?.() ?? [])) {
|
|
1913
|
+
if (!o || !o.orderId || o.price == null)
|
|
1914
|
+
continue;
|
|
1915
|
+
const price = Number(o.price);
|
|
1916
|
+
if (!Number.isFinite(price))
|
|
1917
|
+
continue;
|
|
1918
|
+
let type = o.type;
|
|
1919
|
+
if (type !== ORDER_TYPES.BUY && type !== ORDER_TYPES.SELL) {
|
|
1920
|
+
// Legacy SPREAD-typed placed order: derive side from the same
|
|
1921
|
+
// price-vs-startPrice convention used across the codebase.
|
|
1922
|
+
if (!Number.isFinite(startPrice))
|
|
1923
|
+
continue;
|
|
1924
|
+
type = price < startPrice ? ORDER_TYPES.BUY : ORDER_TYPES.SELL;
|
|
1925
|
+
}
|
|
1926
|
+
book.set(String(o.id), { type, price });
|
|
1927
|
+
}
|
|
1928
|
+
for (const a of actions ?? []) {
|
|
1929
|
+
const id = String(a.id ?? a.orderId ?? '');
|
|
1930
|
+
if (a.type === COW_ACTIONS.CANCEL) {
|
|
1931
|
+
if (id)
|
|
1932
|
+
book.delete(id);
|
|
1933
|
+
}
|
|
1934
|
+
else if (a.type === COW_ACTIONS.UPDATE) {
|
|
1935
|
+
const newPrice = Number(a.newPrice ?? a.order?.price);
|
|
1936
|
+
const newType = a.order?.type;
|
|
1937
|
+
if (id && Number.isFinite(newPrice)) {
|
|
1938
|
+
const entry = book.get(id);
|
|
1939
|
+
const type = (newType === ORDER_TYPES.BUY || newType === ORDER_TYPES.SELL)
|
|
1940
|
+
? newType
|
|
1941
|
+
: entry?.type;
|
|
1942
|
+
if (entry)
|
|
1943
|
+
book.delete(id);
|
|
1944
|
+
const key = String(a.newGridId ?? id);
|
|
1945
|
+
if (type === ORDER_TYPES.BUY || type === ORDER_TYPES.SELL) {
|
|
1946
|
+
book.set(key, { type, price: newPrice });
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
else if (a.type === COW_ACTIONS.CREATE) {
|
|
1951
|
+
const price = Number(a.order?.price);
|
|
1952
|
+
const type = a.order?.type;
|
|
1953
|
+
if (!Number.isFinite(price) || (type !== ORDER_TYPES.BUY && type !== ORDER_TYPES.SELL))
|
|
1954
|
+
continue;
|
|
1955
|
+
if (id)
|
|
1956
|
+
book.set(id, { type, price });
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
let maxBuy = -Infinity;
|
|
1960
|
+
let minSell = Infinity;
|
|
1961
|
+
for (const { type, price } of book.values()) {
|
|
1962
|
+
if (type === ORDER_TYPES.BUY && price > maxBuy)
|
|
1963
|
+
maxBuy = price;
|
|
1964
|
+
else if (type === ORDER_TYPES.SELL && price < minSell)
|
|
1965
|
+
minSell = price;
|
|
1966
|
+
}
|
|
1967
|
+
if (Number.isFinite(maxBuy) && Number.isFinite(minSell) && minSell <= maxBuy) {
|
|
1968
|
+
return `bestPlacedBuy=${maxBuy} >= bestPlacedSell=${minSell}`;
|
|
1969
|
+
}
|
|
1970
|
+
return null;
|
|
1971
|
+
}
|
|
1972
|
+
catch {
|
|
1973
|
+
return null;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Collect every on-chain order id master currently needs to converge against,
|
|
1978
|
+
* so adoption can re-read them by id (immune to the get_full_accounts window
|
|
1979
|
+
* truncation) instead of relying on a partial window read.
|
|
1980
|
+
*
|
|
1981
|
+
* Sources:
|
|
1982
|
+
* - master's own tracked order ids (existing on-chain orders);
|
|
1983
|
+
* - the batch's fresh CREATE ids extracted from the broadcast result
|
|
1984
|
+
* (operation_results[i][1] aligns positionally with placedContexts[i]).
|
|
1985
|
+
*
|
|
1986
|
+
* @param {any} mgr - bot.manager
|
|
1987
|
+
* @param {any} placedResults - broadcast result (has operation_results); null when unavailable
|
|
1988
|
+
* @param {any[]} placedContexts - opContexts (aligned with operation_results); null when unavailable
|
|
1989
|
+
* @param {string[]|null} [extraCreateIds=null] - fresh CREATE chain ids from another
|
|
1990
|
+
* authoritative source (e.g. the uncertain-broadcast poll confirmation) when
|
|
1991
|
+
* no broadcast result exists; merged into createIds so the lagging-create
|
|
1992
|
+
* retry guards them
|
|
1993
|
+
* @returns {string[]} Unique, well-formed 1.7.x order ids
|
|
1994
|
+
*/
|
|
1995
|
+
function collectKnownOnChainOrderIds(mgr, placedResults, placedContexts, extraCreateIds = null) {
|
|
1996
|
+
const masterIds = new Set();
|
|
1997
|
+
const grid = mgr && mgr.grid;
|
|
1998
|
+
if (Array.isArray(grid)) {
|
|
1999
|
+
for (const slot of grid) {
|
|
2000
|
+
if (slot && slot.orderId && /^1\.7\.\d+$/.test(String(slot.orderId))) {
|
|
2001
|
+
masterIds.add(String(slot.orderId));
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
// Master tracked ids live in the orders Map (mgr.grid is legacy and
|
|
2006
|
+
// unset on OrderManager — without this the by-id set omits every
|
|
2007
|
+
// pre-existing ACTIVE order and pass-1 phantom cleanup would virtualize
|
|
2008
|
+
// them as fills on a partial snapshot).
|
|
2009
|
+
if (mgr && mgr.orders instanceof Map) {
|
|
2010
|
+
for (const slot of mgr.orders.values()) {
|
|
2011
|
+
if (slot && slot.orderId && /^1\.7\.\d+$/.test(String(slot.orderId))) {
|
|
2012
|
+
masterIds.add(String(slot.orderId));
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
const createIds = new Set();
|
|
2017
|
+
if (placedResults && Array.isArray(placedContexts)) {
|
|
2018
|
+
const opResults = extractBatchOperationResults(placedResults);
|
|
2019
|
+
if (Array.isArray(opResults)) {
|
|
2020
|
+
for (let i = 0; i < placedContexts.length; i++) {
|
|
2021
|
+
const ctx = placedContexts[i];
|
|
2022
|
+
if (!ctx || ctx.kind !== 'create')
|
|
2023
|
+
continue;
|
|
2024
|
+
const opResult = opResults[i] && opResults[i][1];
|
|
2025
|
+
if (opResult && /^1\.7\.\d+$/.test(String(opResult))) {
|
|
2026
|
+
createIds.add(String(opResult));
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
if (Array.isArray(extraCreateIds)) {
|
|
2032
|
+
for (const id of extraCreateIds) {
|
|
2033
|
+
if (id && /^1\.7\.\d+$/.test(String(id)))
|
|
2034
|
+
createIds.add(String(id));
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
// Existing chain ids referenced by non-create op contexts (cancel /
|
|
2038
|
+
// rotation / size-update). Pre-existing orders whose absence is expected
|
|
2039
|
+
// (cancels/fills in this batch), so they join the by-id set but never
|
|
2040
|
+
// the lagging-create guard.
|
|
2041
|
+
if (Array.isArray(placedContexts)) {
|
|
2042
|
+
for (const ctx of placedContexts) {
|
|
2043
|
+
if (!ctx || ctx.kind === 'create')
|
|
2044
|
+
continue;
|
|
2045
|
+
const refs = [];
|
|
2046
|
+
if (ctx.kind === 'cancel' && ctx.order)
|
|
2047
|
+
refs.push(ctx.order.orderId);
|
|
2048
|
+
else if (ctx.kind === 'rotation' && ctx.rotation?.oldOrder)
|
|
2049
|
+
refs.push(ctx.rotation.oldOrder.orderId);
|
|
2050
|
+
else if (ctx.kind === 'size-update' && ctx.updateInfo?.partialOrder)
|
|
2051
|
+
refs.push(ctx.updateInfo.partialOrder.orderId);
|
|
2052
|
+
for (const id of refs) {
|
|
2053
|
+
if (id && /^1\.7\.\d+$/.test(String(id)))
|
|
2054
|
+
masterIds.add(String(id));
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
const all = new Set([...masterIds, ...createIds]);
|
|
2059
|
+
return { masterIds: [...masterIds], createIds: [...createIds], all: [...all] };
|
|
2060
|
+
}
|
|
2061
|
+
// ================================================================================
|
|
2062
|
+
export { parseChainOrder, findMatchingGridOrderByOpenOrder, applyChainSizeToGridOrder, buildFillKey, correctOrderPriceOnChain, correctAllPriceMismatches, buildCreateOrderArgs, getOrderTypeFromUpdatedFlags, resolveConfiguredPriceBound, virtualizeOrder, convertToSpreadPlaceholder, toRailHolePlaceholder, geometryTypeForSlotIndex, detectGapEvacuationCandidates, updateGapEvacuationStreaks, resolveSpreadOrderSide, chainOrderMatchesSlot, chainOrderMatchesSlotWithTolerance, crossingCandidateChainId, isCrossingCheckCandidate, buildCrossingCheckCandidates, parseSlotIndex, filterOrdersByType, buildOutsideInPairGroups, extractBatchOperationResults, formatUnmatchedChainOrder, isOrderOnChain, isOrderVirtual, hasOnChainId, isOrderPlaced, isPhantomOrder, isSlotAvailable, isEmptyGridSlot, isOrderHealthy, checkSizeThreshold, checkSizesBeforeMinimum, calculateIdealBoundary, assignGridRoles, resolveOnChainRetypeType, shouldFlagOutOfSpread, buildIndexes, validateIndexes, ordersEqual, buildDelta, getOrderSize, deriveTargetBoundary, isShiftEligibleFill, getActiveOrdersTotal, getSideBudget, calculateBudgetedSizes, buildCreateOpFingerprint, isOrderGoneErrorMessage, recordDuplicateOrphanDetection, clearDuplicateOrphanDetection, duplicateOrphanLogInfo, chainOrderUnchangedFromCache, detectCrossedBookPlan, collectKnownOnChainOrderIds };
|
|
1758
2063
|
//# sourceMappingURL=order.js.map
|