dexbot 1.4.25 → 1.5.0
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 +28 -0
- package/README.md +6 -2
- package/analysis/README.md +14 -7
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/analyze_derivatives.ts +1 -1
- package/analysis/bot_key_utils.ts +272 -3
- package/analysis/bot_usage/discover_bot_accounts.ts +64 -9
- package/analysis/bot_usage/kibana_bot_queries.ts +40 -1
- package/analysis/chart_utils.ts +6 -3
- package/analysis/grid_correction_check.ts +56 -20
- package/analysis/resolve_bot_accounts.ts +199 -0
- package/analysis/trade_profitability.ts +36 -8
- package/analysis/tradingview/README.md +25 -0
- package/analysis/tradingview/analyze_tradingview.ts +2 -2
- package/analysis/trend_detection/package.json +1 -1
- package/claw/modules/claw_launcher.ts +5 -4
- package/claw/modules/credit_runtime_adapter.ts +2 -1
- package/claw/modules/dexbot_profiles.ts +4 -3
- 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/analyze_derivatives.js +1 -1
- package/dist/analysis/bot_key_utils.d.ts +28 -1
- package/dist/analysis/bot_key_utils.d.ts.map +1 -1
- package/dist/analysis/bot_key_utils.js +385 -3
- package/dist/analysis/bot_key_utils.js.map +1 -1
- package/dist/analysis/bot_usage/discover_bot_accounts.js +60 -10
- package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +45 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.js +39 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
- package/dist/analysis/chart_utils.d.ts +2 -1
- package/dist/analysis/chart_utils.d.ts.map +1 -1
- package/dist/analysis/chart_utils.js +4 -1
- package/dist/analysis/chart_utils.js.map +1 -1
- package/dist/analysis/grid_correction_check.d.ts.map +1 -1
- package/dist/analysis/grid_correction_check.js +60 -20
- package/dist/analysis/grid_correction_check.js.map +1 -1
- package/dist/analysis/resolve_bot_accounts.d.ts +3 -0
- package/dist/analysis/resolve_bot_accounts.d.ts.map +1 -0
- package/dist/analysis/resolve_bot_accounts.js +216 -0
- package/dist/analysis/resolve_bot_accounts.js.map +1 -0
- package/dist/analysis/trade_profitability.d.ts.map +1 -1
- package/dist/analysis/trade_profitability.js +40 -8
- package/dist/analysis/trade_profitability.js.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.js +2 -2
- package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +2 -1
- package/dist/bot.js.map +1 -1
- package/dist/dexbot.d.ts.map +1 -1
- package/dist/dexbot.js +73 -17
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/ama_signal_runner.js +2 -2
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +2 -1
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts +4 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.js +3 -2
- package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
- package/dist/modules/account_bots.d.ts +42 -1
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +168 -17
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/bitshares_client.d.ts +4 -1
- package/dist/modules/bitshares_client.d.ts.map +1 -1
- package/dist/modules/bitshares_client.js +12 -4
- package/dist/modules/bitshares_client.js.map +1 -1
- package/dist/modules/bot_settings.d.ts.map +1 -1
- package/dist/modules/bot_settings.js +2 -1
- package/dist/modules/bot_settings.js.map +1 -1
- package/dist/modules/cli_colors.d.ts +39 -0
- package/dist/modules/cli_colors.d.ts.map +1 -0
- package/dist/modules/cli_colors.js +42 -0
- package/dist/modules/cli_colors.js.map +1 -0
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +15 -11
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/credit_pricing.d.ts +61 -0
- package/dist/modules/credit_pricing.d.ts.map +1 -0
- package/dist/modules/credit_pricing.js +237 -0
- package/dist/modules/credit_pricing.js.map +1 -0
- package/dist/modules/credit_runtime.d.ts +1 -0
- package/dist/modules/credit_runtime.d.ts.map +1 -1
- package/dist/modules/credit_runtime.js +49 -107
- package/dist/modules/credit_runtime.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +2 -1
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +22 -6
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +19 -2
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +249 -26
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +15 -16
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +69 -42
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/fund_registry.d.ts +1 -1
- package/dist/modules/fund_registry.js +1 -1
- package/dist/modules/launcher/adapter_requirement.d.ts +38 -0
- package/dist/modules/launcher/adapter_requirement.d.ts.map +1 -0
- package/dist/modules/launcher/adapter_requirement.js +141 -0
- package/dist/modules/launcher/adapter_requirement.js.map +1 -0
- package/dist/modules/launcher/bot_supervisor.d.ts +2 -2
- package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
- package/dist/modules/launcher/bot_supervisor.js +32 -3
- package/dist/modules/launcher/bot_supervisor.js.map +1 -1
- package/dist/modules/launcher/launch_modes.d.ts.map +1 -1
- package/dist/modules/launcher/launch_modes.js +5 -4
- package/dist/modules/launcher/launch_modes.js.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.d.ts +1 -1
- package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.js +12 -8
- package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
- package/dist/modules/launcher/status_reporting.d.ts +6 -6
- package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
- package/dist/modules/launcher/status_reporting.js +9 -6
- package/dist/modules/launcher/status_reporting.js.map +1 -1
- package/dist/modules/order/export.d.ts +1 -1
- package/dist/modules/order/export.js +1 -1
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +47 -12
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/logger.d.ts +23 -1
- package/dist/modules/order/logger.d.ts.map +1 -1
- package/dist/modules/order/logger.js +60 -17
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +43 -3
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/sync_engine.js +1 -1
- package/dist/modules/order/utils/system.d.ts +52 -4
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +106 -14
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/validate.d.ts +1 -1
- package/dist/modules/order/utils/validate.d.ts.map +1 -1
- package/dist/modules/order/utils/validate.js +43 -3
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/dist/modules/utils/chain_logs.d.ts +11 -0
- package/dist/modules/utils/chain_logs.d.ts.map +1 -0
- package/dist/modules/utils/chain_logs.js +23 -0
- package/dist/modules/utils/chain_logs.js.map +1 -0
- package/dist/modules/utils/sanitize_key.d.ts +7 -0
- package/dist/modules/utils/sanitize_key.d.ts.map +1 -1
- package/dist/modules/utils/sanitize_key.js +15 -0
- package/dist/modules/utils/sanitize_key.js.map +1 -1
- package/dist/pm2.d.ts +11 -1
- package/dist/pm2.d.ts.map +1 -1
- package/dist/pm2.js +66 -15
- package/dist/pm2.js.map +1 -1
- package/dist/scripts/analyze-credit.d.ts +4 -0
- package/dist/scripts/analyze-credit.d.ts.map +1 -0
- package/dist/scripts/analyze-credit.js +760 -0
- package/dist/scripts/analyze-credit.js.map +1 -0
- package/dist/scripts/analyze-orders.d.ts +1 -10
- package/dist/scripts/analyze-orders.d.ts.map +1 -1
- package/dist/scripts/analyze-orders.js +11 -34
- package/dist/scripts/analyze-orders.js.map +1 -1
- package/dist/scripts/test-credit-renewal.d.ts.map +1 -1
- package/dist/scripts/test-credit-renewal.js +37 -5
- package/dist/scripts/test-credit-renewal.js.map +1 -1
- package/dist/scripts/tv.d.ts +11 -0
- package/dist/scripts/tv.d.ts.map +1 -0
- package/dist/scripts/tv.js +357 -0
- package/dist/scripts/tv.js.map +1 -0
- package/dist/scripts/update.js +77 -10
- package/dist/scripts/update.js.map +1 -1
- package/dist/unlock.d.ts.map +1 -1
- package/dist/unlock.js +20 -8
- package/dist/unlock.js.map +1 -1
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +7 -6
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
- package/docs/MPA_CREDIT_USAGE.md +3 -1
- package/docs/README.md +2 -2
- package/docs/WORKFLOW.md +3 -1
- package/package.json +2 -1
- package/scripts/README.md +12 -0
|
@@ -1445,7 +1445,8 @@ function validateOrderSizeForExecution(bot, size, type, orderLike = null, fallba
|
|
|
1445
1445
|
* LAST-FILL-GUARD helper — pivot ± halfIncrement (replaces price-tolerance).
|
|
1446
1446
|
* last fill @x with increment i: BUY < x*(1 - i/2/100), SELL > x*(1 + i/2/100)
|
|
1447
1447
|
* e.g. x=1000, i=0.5% => BUY < 997.5, SELL > 1002.5
|
|
1448
|
-
* Cold (pivot null or lastType null) => disabled. Spread
|
|
1448
|
+
* Cold (pivot null or lastType null) => disabled. Spread-correction CREATES
|
|
1449
|
+
* bypass per-action (see broadcast sites); rotations never bypass.
|
|
1449
1450
|
* @param {number} price - Target order price
|
|
1450
1451
|
* @param {number} size - Order size (unused, kept for compat)
|
|
1451
1452
|
* @param {string} type - ORDER_TYPES.BUY/SELL
|
|
@@ -1478,6 +1479,123 @@ function isLastFillGuardBlocked(price, _size, type, lastPrice, lastType, increme
|
|
|
1478
1479
|
return { blocked: true, pivot, halfInc, threshold: sellThreshold };
|
|
1479
1480
|
return { blocked: false, pivot: null, halfInc, threshold: null };
|
|
1480
1481
|
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Refresh the LAST-FILL guard pivot from fills that arrived while the current
|
|
1484
|
+
* batch was being planned or broadcast but have not been ingested yet.
|
|
1485
|
+
*
|
|
1486
|
+
* The pivot (_lastFilledPrice) is recorded per fill-processing batch, but a
|
|
1487
|
+
* multi-chunk COW broadcast spans tens of seconds — fills detected mid-cycle
|
|
1488
|
+
* sit in _incomingFillQueue until the cycle ends, so ops built for later
|
|
1489
|
+
* chunks would be checked against a stale pivot (production incident: a chunk
|
|
1490
|
+
* planned with a pre-crash pivot placed asks 0.6% below the true latest fill
|
|
1491
|
+
* that was already queued). Peek-only: never drains the queue, the owning
|
|
1492
|
+
* fill cycle still processes every entry. Best-effort: returns false when no
|
|
1493
|
+
* queued fill yields a usable side + price.
|
|
1494
|
+
* @param {import('./dexbot_class.js').DEXBot} bot
|
|
1495
|
+
* @returns {boolean} True when the pivot was refreshed from queued fills
|
|
1496
|
+
*/
|
|
1497
|
+
function refreshLastFillPivotFromQueue(bot) {
|
|
1498
|
+
try {
|
|
1499
|
+
const queue = bot?._incomingFillQueue;
|
|
1500
|
+
if (!Array.isArray(queue) || queue.length === 0)
|
|
1501
|
+
return false;
|
|
1502
|
+
const mgr = bot?.manager;
|
|
1503
|
+
if (!mgr || !mgr.orders)
|
|
1504
|
+
return false;
|
|
1505
|
+
const assets = mgr.assets;
|
|
1506
|
+
const findSlotByOrderId = (orderId) => {
|
|
1507
|
+
if (!orderId)
|
|
1508
|
+
return null;
|
|
1509
|
+
try {
|
|
1510
|
+
for (const o of mgr.orders.values()) {
|
|
1511
|
+
if (o?.orderId === orderId)
|
|
1512
|
+
return o;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
catch { /* ignore iteration errors */ }
|
|
1516
|
+
return null;
|
|
1517
|
+
};
|
|
1518
|
+
let latest = null;
|
|
1519
|
+
for (const fill of queue) {
|
|
1520
|
+
const fillOp = fill?.op?.[1] || fill;
|
|
1521
|
+
const orderId = fillOp?.order_id || fill?.orderId;
|
|
1522
|
+
let type = null;
|
|
1523
|
+
let price = null;
|
|
1524
|
+
// Prefer the grid slot: a limit fill executes at (or better than)
|
|
1525
|
+
// its slot price, which is exactly the guard's price convention.
|
|
1526
|
+
const slot = findSlotByOrderId(orderId);
|
|
1527
|
+
if (slot && (slot.type === ORDER_TYPES.BUY || slot.type === ORDER_TYPES.SELL)) {
|
|
1528
|
+
type = slot.type;
|
|
1529
|
+
const slotPrice = Number(slot.price);
|
|
1530
|
+
if (Number.isFinite(slotPrice) && slotPrice > 0)
|
|
1531
|
+
price = slotPrice;
|
|
1532
|
+
}
|
|
1533
|
+
// Fall back to fill economics (B/A convention, mirroring
|
|
1534
|
+
// _computeFillContext's pays-asset side resolution, including
|
|
1535
|
+
// SPREAD slots carrying on-chain orders).
|
|
1536
|
+
if ((type == null || price == null) && fillOp?.pays && fillOp?.receives && assets?.assetA && assets?.assetB) {
|
|
1537
|
+
try {
|
|
1538
|
+
const paysId = fillOp.pays.asset_id;
|
|
1539
|
+
const paysAmt = Number(fillOp.pays.amount);
|
|
1540
|
+
const recvAmt = Number(fillOp.receives.amount);
|
|
1541
|
+
if (Number.isFinite(paysAmt) && paysAmt > 0 && Number.isFinite(recvAmt) && recvAmt > 0) {
|
|
1542
|
+
const paysFloat = (base, precision) => base / Math.pow(10, precision);
|
|
1543
|
+
if (paysId === assets.assetA.id) {
|
|
1544
|
+
type = ORDER_TYPES.SELL;
|
|
1545
|
+
price = paysFloat(recvAmt, assets.assetB.precision) / paysFloat(paysAmt, assets.assetA.precision);
|
|
1546
|
+
}
|
|
1547
|
+
else if (paysId === assets.assetB.id) {
|
|
1548
|
+
type = ORDER_TYPES.BUY;
|
|
1549
|
+
price = paysFloat(paysAmt, assets.assetB.precision) / paysFloat(recvAmt, assets.assetA.precision);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
catch { /* best-effort */ }
|
|
1554
|
+
}
|
|
1555
|
+
if ((type === ORDER_TYPES.BUY || type === ORDER_TYPES.SELL)
|
|
1556
|
+
&& Number.isFinite(price) && price > 0) {
|
|
1557
|
+
latest = { price: price, type };
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
if (!latest)
|
|
1561
|
+
return false;
|
|
1562
|
+
mgr._lastFilledPrice = latest.price;
|
|
1563
|
+
mgr._lastFilledType = latest.type;
|
|
1564
|
+
if (latest.type === ORDER_TYPES.BUY) {
|
|
1565
|
+
mgr._lastFilledBuyPrice = latest.price;
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
mgr._lastFilledSellPrice = latest.price;
|
|
1569
|
+
}
|
|
1570
|
+
try {
|
|
1571
|
+
mgr.logger?.log?.(`[LAST-FILL-GUARD] Pivot refreshed from ${queue.length} pending queued fill(s): ` +
|
|
1572
|
+
`${latest.type} @${Format.formatPrice6(latest.price)}`, 'debug');
|
|
1573
|
+
}
|
|
1574
|
+
catch { /* ignore logging errors */ }
|
|
1575
|
+
return true;
|
|
1576
|
+
}
|
|
1577
|
+
catch {
|
|
1578
|
+
return false;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Resolve the grid increment percent for the LAST-FILL guard in one place so
|
|
1583
|
+
* every check site and the batch summary use (and print) the same value.
|
|
1584
|
+
* Falls back to the default 0.5 when unset/invalid.
|
|
1585
|
+
* @param {import('./dexbot_class.js').DEXBot} bot
|
|
1586
|
+
* @returns {number} Positive increment percent
|
|
1587
|
+
*/
|
|
1588
|
+
function resolveLastFillGuardIncrement(bot) {
|
|
1589
|
+
const raw = Number(bot?.manager?.config?.incrementPercent
|
|
1590
|
+
?? bot?.config?.incrementPercent
|
|
1591
|
+
?? constantsModule?.DEFAULT_CONFIG?.incrementPercent
|
|
1592
|
+
?? 0.5);
|
|
1593
|
+
if (Number.isFinite(raw) && raw > 0)
|
|
1594
|
+
return raw;
|
|
1595
|
+
return Number(constantsModule?.DEFAULT_CONFIG?.incrementPercent) > 0
|
|
1596
|
+
? Number(constantsModule?.DEFAULT_CONFIG?.incrementPercent)
|
|
1597
|
+
: 0.5;
|
|
1598
|
+
}
|
|
1481
1599
|
/**
|
|
1482
1600
|
* Build COW actions array from a simple plan object.
|
|
1483
1601
|
* @param {import('./dexbot_class.js').DEXBot} bot
|
|
@@ -1489,6 +1607,12 @@ function buildActionsFromPlan(_bot, plan) {
|
|
|
1489
1607
|
? { ordersToPlace: plan }
|
|
1490
1608
|
: (plan || {});
|
|
1491
1609
|
const { ordersToPlace = [], ordersToRotate = [], ordersToUpdate = [], ordersToCancel = [] } = normalizedPlan;
|
|
1610
|
+
// Per-action origin: guard bypasses are scoped per action (not per batch),
|
|
1611
|
+
// so a future rotation entry merged into a correction plan cannot silently
|
|
1612
|
+
// inherit the spread-correction bypass. Actions built outside this helper
|
|
1613
|
+
// carry no origin and default to guarded (safe default).
|
|
1614
|
+
const planOrigin = normalizedPlan?.origin;
|
|
1615
|
+
const withOrigin = (action) => (planOrigin ? { ...action, origin: planOrigin } : action);
|
|
1492
1616
|
const actions = [];
|
|
1493
1617
|
for (const o of ordersToCancel) {
|
|
1494
1618
|
if (o?.orderId) {
|
|
@@ -1509,7 +1633,7 @@ function buildActionsFromPlan(_bot, plan) {
|
|
|
1509
1633
|
const orderType = r?.type || oldOrder?.type;
|
|
1510
1634
|
if (!id || !orderId || !newGridId || !orderType || !Number.isFinite(newPrice) || !(newSize > 0))
|
|
1511
1635
|
continue;
|
|
1512
|
-
actions.push({
|
|
1636
|
+
actions.push(withOrigin({
|
|
1513
1637
|
type: COW_ACTIONS.UPDATE,
|
|
1514
1638
|
id,
|
|
1515
1639
|
orderId,
|
|
@@ -1522,7 +1646,7 @@ function buildActionsFromPlan(_bot, plan) {
|
|
|
1522
1646
|
price: newPrice,
|
|
1523
1647
|
size: newSize
|
|
1524
1648
|
}
|
|
1525
|
-
});
|
|
1649
|
+
}));
|
|
1526
1650
|
}
|
|
1527
1651
|
for (const o of ordersToUpdate) {
|
|
1528
1652
|
const partialOrder = o?.partialOrder || o;
|
|
@@ -1534,7 +1658,7 @@ function buildActionsFromPlan(_bot, plan) {
|
|
|
1534
1658
|
: Number(partialOrder?.size || 0);
|
|
1535
1659
|
if (!id || !orderId)
|
|
1536
1660
|
continue;
|
|
1537
|
-
actions.push({
|
|
1661
|
+
actions.push(withOrigin({
|
|
1538
1662
|
type: COW_ACTIONS.UPDATE,
|
|
1539
1663
|
id,
|
|
1540
1664
|
orderId,
|
|
@@ -1546,12 +1670,12 @@ function buildActionsFromPlan(_bot, plan) {
|
|
|
1546
1670
|
type: orderType,
|
|
1547
1671
|
size: newSize
|
|
1548
1672
|
}
|
|
1549
|
-
});
|
|
1673
|
+
}));
|
|
1550
1674
|
}
|
|
1551
1675
|
for (const o of ordersToPlace) {
|
|
1552
1676
|
if (!o?.id)
|
|
1553
1677
|
continue;
|
|
1554
|
-
actions.push({ type: COW_ACTIONS.CREATE, id: o.id, order: o });
|
|
1678
|
+
actions.push(withOrigin({ type: COW_ACTIONS.CREATE, id: o.id, order: o }));
|
|
1555
1679
|
}
|
|
1556
1680
|
return actions;
|
|
1557
1681
|
}
|
|
@@ -2343,6 +2467,14 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2343
2467
|
const opContexts = [];
|
|
2344
2468
|
const skippedUpdateSlotIds = new Set();
|
|
2345
2469
|
let skippedUpdateCount = 0;
|
|
2470
|
+
// Per-batch LAST-FILL-GUARD disposition counters. Per-action pass lines
|
|
2471
|
+
// would spam big batches, so the guard emits one batch summary instead
|
|
2472
|
+
// (see the summary after the action loop below).
|
|
2473
|
+
const lastFillGuardStats = { checked: 0, passed: 0, skipped: 0, bypassed: 0 };
|
|
2474
|
+
// Whether any guard check in this batch refreshed the pivot from
|
|
2475
|
+
// still-queued fills — reported in the batch summary so a pivot change
|
|
2476
|
+
// that altered a guard decision is visible at info, not just debug.
|
|
2477
|
+
let lastFillGuardPivotRefreshed = false;
|
|
2346
2478
|
// Slots whose size-update op was broadcast with a post-fill-clamped
|
|
2347
2479
|
// target: the working grid still holds the planned (larger) size, so the
|
|
2348
2480
|
// slots are re-synced from master before commit to keep the committed
|
|
@@ -2458,18 +2590,35 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2458
2590
|
continue;
|
|
2459
2591
|
}
|
|
2460
2592
|
// LAST-FILL PRICE GUARD: pivot ± halfIncrement (BUY < pivot*(1-half), SELL > pivot*(1+half)).
|
|
2461
|
-
//
|
|
2593
|
+
// Bypass is per-action: only spread-correction CREATES skip so gap repair can close.
|
|
2594
|
+
// The batch-level origin is honored only for actions without their own origin stamp
|
|
2595
|
+
// (back-compat for plans that bypass buildActionsFromPlan). Cold (null) => disabled.
|
|
2462
2596
|
try {
|
|
2463
|
-
|
|
2597
|
+
const actionOrigin = action?.origin;
|
|
2598
|
+
const batchOrigin = cowResult?.origin;
|
|
2599
|
+
const isCorrectionCreate = actionOrigin === 'spread-correction'
|
|
2600
|
+
|| (actionOrigin == null && batchOrigin === 'spread-correction');
|
|
2601
|
+
if (!isCorrectionCreate) {
|
|
2602
|
+
try {
|
|
2603
|
+
if (refreshLastFillPivotFromQueue(bot))
|
|
2604
|
+
lastFillGuardPivotRefreshed = true;
|
|
2605
|
+
}
|
|
2606
|
+
catch { /* best-effort */ }
|
|
2464
2607
|
const lastPrice = bot.manager?._lastFilledPrice;
|
|
2465
2608
|
const lastType = bot.manager?._lastFilledType;
|
|
2466
|
-
const inc =
|
|
2609
|
+
const inc = resolveLastFillGuardIncrement(bot);
|
|
2467
2610
|
const check = isLastFillGuardBlocked(createPrice, order.size, order.type, lastPrice, lastType, inc);
|
|
2611
|
+
lastFillGuardStats.checked++;
|
|
2468
2612
|
if (check.blocked) {
|
|
2613
|
+
lastFillGuardStats.skipped++;
|
|
2469
2614
|
const dir = order.type === ORDER_TYPES.BUY ? 'above' : 'below';
|
|
2470
2615
|
bot.manager.logger.log(`[LAST-FILL-GUARD] Skipping ${order.type} CREATE for ${order.id} at ${Format.formatPrice6(createPrice)}: ${dir} last filled ${Format.formatPrice6(check.pivot)} (halfInc ${check.halfInc}% thr ${Format.formatPrice6(check.threshold)}); re-planned after market moves`, 'warn');
|
|
2471
2616
|
continue;
|
|
2472
2617
|
}
|
|
2618
|
+
lastFillGuardStats.passed++;
|
|
2619
|
+
}
|
|
2620
|
+
else {
|
|
2621
|
+
lastFillGuardStats.bypassed++;
|
|
2473
2622
|
}
|
|
2474
2623
|
}
|
|
2475
2624
|
catch (_e) { /* guard is best-effort */ }
|
|
@@ -2564,23 +2713,34 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2564
2713
|
continue;
|
|
2565
2714
|
}
|
|
2566
2715
|
// LAST-FILL PRICE GUARD (UPDATE rotation): pivot ± halfIncrement — same as CREATE.
|
|
2716
|
+
// No origin bypass here, ever: a rotation reprices, so it always obeys the guard —
|
|
2717
|
+
// even if a future correction plan carries rotation entries (per-action scoping).
|
|
2718
|
+
// The pivot is refreshed from still-queued fills first: fills detected mid-broadcast
|
|
2719
|
+
// sit in _incomingFillQueue until the fill cycle ends, and without this the later
|
|
2720
|
+
// chunks of a long broadcast would be checked against a stale pivot.
|
|
2567
2721
|
try {
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2722
|
+
try {
|
|
2723
|
+
if (refreshLastFillPivotFromQueue(bot))
|
|
2724
|
+
lastFillGuardPivotRefreshed = true;
|
|
2725
|
+
}
|
|
2726
|
+
catch { /* best-effort */ }
|
|
2727
|
+
const lastPrice = bot.manager?._lastFilledPrice;
|
|
2728
|
+
const lastType = bot.manager?._lastFilledType;
|
|
2729
|
+
const inc = resolveLastFillGuardIncrement(bot);
|
|
2730
|
+
const check = isLastFillGuardBlocked(newPrice, newSize, orderType, lastPrice, lastType, inc);
|
|
2731
|
+
lastFillGuardStats.checked++;
|
|
2732
|
+
if (check.blocked) {
|
|
2733
|
+
lastFillGuardStats.skipped++;
|
|
2734
|
+
skippedUpdateCount++;
|
|
2735
|
+
if (action.id)
|
|
2736
|
+
skippedUpdateSlotIds.add(action.id);
|
|
2737
|
+
if (action.newGridId)
|
|
2738
|
+
skippedUpdateSlotIds.add(action.newGridId);
|
|
2739
|
+
const dir = orderType === ORDER_TYPES.BUY ? 'above' : 'below';
|
|
2740
|
+
bot.manager.logger.log(`[LAST-FILL-GUARD] Skipping ${orderType} UPDATE for ${action.id} -> ${action.newGridId} at ${Format.formatPrice6(newPrice)}: ${dir} last filled ${Format.formatPrice6(check.pivot)} (halfInc ${check.halfInc}% thr ${Format.formatPrice6(check.threshold)})`, 'warn');
|
|
2741
|
+
continue;
|
|
2583
2742
|
}
|
|
2743
|
+
lastFillGuardStats.passed++;
|
|
2584
2744
|
}
|
|
2585
2745
|
catch (_e) { /* best-effort */ }
|
|
2586
2746
|
const { amountToSell, minToReceive } = buildCreateOrderArgs({ type: orderType, size: newSize, price: newPrice }, assetA, assetB);
|
|
@@ -2693,6 +2853,31 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2693
2853
|
`${crossedOrderLabel(fbCrossed)}; re-planned after its cancel confirms.`, 'warn');
|
|
2694
2854
|
continue;
|
|
2695
2855
|
}
|
|
2856
|
+
// LAST-FILL GUARD (fallback variant): this CREATE
|
|
2857
|
+
// replaces a rotation UPDATE at a repriced level,
|
|
2858
|
+
// so it obeys the same guard with a refreshed
|
|
2859
|
+
// pivot — no origin bypass, same as rotations.
|
|
2860
|
+
try {
|
|
2861
|
+
if (refreshLastFillPivotFromQueue(bot))
|
|
2862
|
+
lastFillGuardPivotRefreshed = true;
|
|
2863
|
+
}
|
|
2864
|
+
catch { /* best-effort */ }
|
|
2865
|
+
try {
|
|
2866
|
+
const fbInc = resolveLastFillGuardIncrement(bot);
|
|
2867
|
+
const fbCheck = isLastFillGuardBlocked(fbPrice, fbSize, fbType, bot.manager?._lastFilledPrice, bot.manager?._lastFilledType, fbInc);
|
|
2868
|
+
lastFillGuardStats.checked++;
|
|
2869
|
+
if (fbCheck.blocked) {
|
|
2870
|
+
lastFillGuardStats.skipped++;
|
|
2871
|
+
const fbDir = fbType === ORDER_TYPES.BUY ? 'above' : 'below';
|
|
2872
|
+
bot.manager.logger.log(`[LAST-FILL-GUARD] Skipping CREATE fallback for ${targetSlotId} at ` +
|
|
2873
|
+
`${Format.formatPrice6(fbPrice)}: ${fbDir} last filled ` +
|
|
2874
|
+
`${Format.formatPrice6(fbCheck.pivot)} (halfInc ${fbCheck.halfInc}% thr ` +
|
|
2875
|
+
`${Format.formatPrice6(fbCheck.threshold)}); re-planned after market moves`, 'warn');
|
|
2876
|
+
continue;
|
|
2877
|
+
}
|
|
2878
|
+
lastFillGuardStats.passed++;
|
|
2879
|
+
}
|
|
2880
|
+
catch (_fbGuardErr) { /* guard is best-effort */ }
|
|
2696
2881
|
const fbArgs = buildCreateOrderArgs({ type: fbType, size: fbSize, price: fbPrice }, assetA, assetB);
|
|
2697
2882
|
const fbResult = await chainOrders.buildCreateOrderOp(bot.account, fbArgs.amountToSell, fbArgs.sellAssetId, fbArgs.minToReceive, fbArgs.receiveAssetId, null);
|
|
2698
2883
|
if (fbResult) {
|
|
@@ -2723,6 +2908,39 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2723
2908
|
}
|
|
2724
2909
|
}
|
|
2725
2910
|
}
|
|
2911
|
+
// Batch-level LAST-FILL-GUARD summary: per-action pass lines would spam
|
|
2912
|
+
// big batches, so one line per batch records the mode, pivot, resolved
|
|
2913
|
+
// increment, and pass/skip/bypass counts — the guard's pass decisions
|
|
2914
|
+
// are what incident reconstruction needs. Origin folds in here as
|
|
2915
|
+
// mode=bypassed(<origin>); there is no second source of truth.
|
|
2916
|
+
// Cold (guard off) is warn, not info — a disabled guard must say so.
|
|
2917
|
+
// pivotRefreshed surfaces whether a mid-broadcast queued fill moved the
|
|
2918
|
+
// pivot under this batch's checks (the refresh itself stays debug).
|
|
2919
|
+
// Note the printed pivot is end-of-batch state: when pivotRefreshed is
|
|
2920
|
+
// true, early actions were checked against the older pivot.
|
|
2921
|
+
try {
|
|
2922
|
+
const totalGuarded = lastFillGuardStats.checked + lastFillGuardStats.bypassed;
|
|
2923
|
+
if (totalGuarded > 0) {
|
|
2924
|
+
const sumPivotRaw = bot.manager?._lastFilledPrice;
|
|
2925
|
+
const sumType = bot.manager?._lastFilledType;
|
|
2926
|
+
const sumInc = resolveLastFillGuardIncrement(bot);
|
|
2927
|
+
const cold = sumPivotRaw == null || !Number.isFinite(Number(sumPivotRaw)) || sumType == null;
|
|
2928
|
+
const batchOrigin = cowResult?.origin;
|
|
2929
|
+
const mode = cold
|
|
2930
|
+
? 'disabled(cold)'
|
|
2931
|
+
: (lastFillGuardStats.bypassed > 0 && lastFillGuardStats.checked === 0)
|
|
2932
|
+
? `bypassed(${batchOrigin || 'unknown'})`
|
|
2933
|
+
: (lastFillGuardStats.bypassed > 0
|
|
2934
|
+
? `active+bypassed(${batchOrigin || 'unknown'})`
|
|
2935
|
+
: 'active');
|
|
2936
|
+
const pivotStr = cold ? 'none' : `${Format.formatPrice6(Number(sumPivotRaw))}(${sumType})`;
|
|
2937
|
+
bot.manager.logger.log(`[LAST-FILL-GUARD] mode=${mode} pivot=${pivotStr} inc=${sumInc}% ` +
|
|
2938
|
+
`pivotRefreshed=${lastFillGuardPivotRefreshed} ` +
|
|
2939
|
+
`checked=${lastFillGuardStats.checked} passed=${lastFillGuardStats.passed} ` +
|
|
2940
|
+
`skipped=${lastFillGuardStats.skipped} bypassed=${lastFillGuardStats.bypassed}`, cold ? 'warn' : 'info');
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
catch { /* summary is best-effort */ }
|
|
2726
2944
|
if (skippedUpdateCount > 0) {
|
|
2727
2945
|
restoreSkippedUpdateSlotsInWorkingGrid(bot, workingGrid, skippedUpdateSlotIds, skippedUpdateCount);
|
|
2728
2946
|
}
|
|
@@ -3470,7 +3688,12 @@ async function processBatchResults(bot, result, opContexts) {
|
|
|
3470
3688
|
});
|
|
3471
3689
|
}
|
|
3472
3690
|
}
|
|
3473
|
-
|
|
3691
|
+
// Success line mirrors the failure-path fingerprint below
|
|
3692
|
+
// (type/price/size) so both are greppable by the same keys —
|
|
3693
|
+
// this is what ties a fill back to the slot that placed it.
|
|
3694
|
+
bot.manager.logger.log(`Placed ${ctx.order.type} order ${ctx.order.id} ` +
|
|
3695
|
+
`@${Format.formatPrice6(ctx.order.price)} x${Format.formatAmount(ctx.order.size)} ` +
|
|
3696
|
+
`-> ${chainOrderId}`, 'info');
|
|
3474
3697
|
}
|
|
3475
3698
|
else {
|
|
3476
3699
|
const fingerprint = [
|
|
@@ -3571,7 +3794,7 @@ async function processBatchResults(bot, result, opContexts) {
|
|
|
3571
3794
|
updateOperationCount
|
|
3572
3795
|
};
|
|
3573
3796
|
}
|
|
3574
|
-
export { isLastFillGuardBlocked, buildOutsideInPairGroupsForOrders, buildOutsideInPairGroupsForCreateEntries, extractOperationResults, findMissingCreateResultContexts, markMissingCreateResultsAsStructuralBlocker, formatUnmatchedChainOrderForLog, recordPendingBroadcast, clearPendingBroadcasts, clearPendingBroadcastsForSlots, popPushedWorkingGrid, buildChainOrderFingerprint, normalizeChainOrderForPendingMatch, findChainOrderForSlot, reconcileAfterUncertainBroadcast, reconcileAfterUncertainBroadcastImpl, autoCancelOneUnmatchedOrphan, shouldExecuteCreatePairMode, executeWithRetryOnUncertain, executeChunkedWithRetryOnUncertain, formatPartialBroadcastSummary, executeOperationsWithStrategy, validateOperationFunds, resolveIdealSizeForValidation, validateOrderSizeForExecution, buildActionsFromPlan, buildCowResultFromPlan, restoreSkippedUpdateSlotsInWorkingGrid, applyRotationTransitionsToWorkingGrid, pollChainForConfirmation, updateOrdersOnChainBatchCOW, processBatchResults, adoptPlacedBatchFromChain };
|
|
3797
|
+
export { isLastFillGuardBlocked, refreshLastFillPivotFromQueue, buildOutsideInPairGroupsForOrders, buildOutsideInPairGroupsForCreateEntries, extractOperationResults, findMissingCreateResultContexts, markMissingCreateResultsAsStructuralBlocker, formatUnmatchedChainOrderForLog, recordPendingBroadcast, clearPendingBroadcasts, clearPendingBroadcastsForSlots, popPushedWorkingGrid, buildChainOrderFingerprint, normalizeChainOrderForPendingMatch, findChainOrderForSlot, reconcileAfterUncertainBroadcast, reconcileAfterUncertainBroadcastImpl, autoCancelOneUnmatchedOrphan, shouldExecuteCreatePairMode, executeWithRetryOnUncertain, executeChunkedWithRetryOnUncertain, formatPartialBroadcastSummary, executeOperationsWithStrategy, validateOperationFunds, resolveIdealSizeForValidation, validateOrderSizeForExecution, buildActionsFromPlan, buildCowResultFromPlan, restoreSkippedUpdateSlotsInWorkingGrid, applyRotationTransitionsToWorkingGrid, pollChainForConfirmation, updateOrdersOnChainBatchCOW, processBatchResults, adoptPlacedBatchFromChain };
|
|
3575
3798
|
export default {
|
|
3576
3799
|
buildOutsideInPairGroupsForOrders,
|
|
3577
3800
|
buildOutsideInPairGroupsForCreateEntries,
|