dexbot 1.5.3 → 1.6.1
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 +75 -0
- package/README.md +9 -7
- package/analysis/README.md +1 -2
- package/analysis/ama_fitting/fetch_lp_candles.ts +1 -9
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/analyze_derivatives.ts +1 -1
- package/analysis/bot_usage/discover_bot_accounts.ts +12 -6
- package/analysis/resolve_source.ts +9 -2
- package/analysis/tradingview/README.md +29 -11
- package/analysis/tradingview/analyze_tradingview.ts +92 -3
- package/analysis/tradingview/tradingview_uplot_chart_generator.ts +1120 -182
- package/analysis/trend_detection/package.json +1 -1
- package/claw/docs/DEXBOT2_TUNING_CHEAT_SHEET.md +1 -1
- package/claw/modules/dexbot_profiles.ts +22 -2
- 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/skills/launcher-ops/references/launcher-workflow.md +1 -1
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/claw/tests/test_dexbot_profiles.ts +65 -0
- package/dist/analysis/ama_fitting/fetch_lp_candles.js +1 -9
- package/dist/analysis/ama_fitting/fetch_lp_candles.js.map +1 -1
- package/dist/analysis/analyze_derivatives.js +1 -1
- package/dist/analysis/bot_usage/discover_bot_accounts.js +11 -6
- package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +2 -0
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
- package/dist/analysis/resolve_source.d.ts +1 -0
- package/dist/analysis/resolve_source.d.ts.map +1 -1
- package/dist/analysis/resolve_source.js +6 -2
- package/dist/analysis/resolve_source.js.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.d.ts +5 -0
- package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.js +112 -3
- package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts +5 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +1118 -182
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
- package/dist/bot.js +1 -1
- package/dist/bot.js.map +1 -1
- package/dist/dexbot.js +4 -4
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/ama_signal_runner.js +2 -2
- package/dist/market_adapter/candle_utils.d.ts +6 -0
- package/dist/market_adapter/candle_utils.d.ts.map +1 -1
- package/dist/market_adapter/candle_utils.js +6 -0
- package/dist/market_adapter/candle_utils.js.map +1 -1
- package/dist/market_adapter/core/kibana_candles.d.ts +17 -1
- package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -1
- package/dist/market_adapter/core/kibana_candles.js +101 -19
- package/dist/market_adapter/core/kibana_candles.js.map +1 -1
- package/dist/market_adapter/core/kibana_client.d.ts +2 -0
- package/dist/market_adapter/core/kibana_client.d.ts.map +1 -1
- package/dist/market_adapter/core/kibana_client.js +31 -4
- package/dist/market_adapter/core/kibana_client.js.map +1 -1
- package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -1
- package/dist/market_adapter/core/market_adapter_service.js +0 -6
- package/dist/market_adapter/core/market_adapter_service.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_book_data.d.ts +14 -0
- package/dist/market_adapter/inputs/fetch_book_data.d.ts.map +1 -0
- package/dist/market_adapter/inputs/fetch_book_data.js +138 -0
- package/dist/market_adapter/inputs/fetch_book_data.js.map +1 -0
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts +16 -2
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.js +109 -281
- package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
- package/dist/market_adapter/inputs/kibana_feed_source.d.ts +87 -0
- package/dist/market_adapter/inputs/kibana_feed_source.d.ts.map +1 -0
- package/dist/market_adapter/inputs/kibana_feed_source.js +570 -0
- package/dist/market_adapter/inputs/kibana_feed_source.js.map +1 -0
- package/dist/market_adapter/inputs/kibana_source.d.ts.map +1 -1
- package/dist/market_adapter/inputs/kibana_source.js +10 -5
- package/dist/market_adapter/inputs/kibana_source.js.map +1 -1
- package/dist/market_adapter/inputs/window_cache.d.ts +166 -0
- package/dist/market_adapter/inputs/window_cache.d.ts.map +1 -0
- package/dist/market_adapter/inputs/window_cache.js +500 -0
- package/dist/market_adapter/inputs/window_cache.js.map +1 -0
- package/dist/market_adapter/interval_utils.d.ts +8 -1
- package/dist/market_adapter/interval_utils.d.ts.map +1 -1
- package/dist/market_adapter/interval_utils.js +14 -1
- package/dist/market_adapter/interval_utils.js.map +1 -1
- package/dist/modules/account_bots.d.ts +1 -0
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +21 -5
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/account_orders.d.ts +25 -4
- package/dist/modules/account_orders.d.ts.map +1 -1
- package/dist/modules/account_orders.js +67 -4
- package/dist/modules/account_orders.js.map +1 -1
- package/dist/modules/bitshares-native/chain_client.d.ts +2 -0
- package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -1
- package/dist/modules/bitshares-native/chain_client.js +3 -1
- package/dist/modules/bitshares-native/chain_client.js.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.js +13 -3
- package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
- package/dist/modules/bitshares-native/transport.d.ts +2 -0
- package/dist/modules/bitshares-native/transport.d.ts.map +1 -1
- package/dist/modules/bitshares-native/transport.js +122 -28
- package/dist/modules/bitshares-native/transport.js.map +1 -1
- package/dist/modules/bitshares_client.d.ts.map +1 -1
- package/dist/modules/bitshares_client.js +59 -8
- 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 +11 -0
- package/dist/modules/bot_settings.js.map +1 -1
- package/dist/modules/constants.d.ts +18 -0
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +90 -4
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +11 -0
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +92 -4
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +26 -1
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +157 -28
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_fill_runtime.d.ts +66 -1
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +295 -10
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +102 -12
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +644 -48
- 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 +13 -0
- package/dist/modules/dexbot_startup_runtime.js.map +1 -1
- package/dist/modules/dexbot_state_recovery.d.ts +15 -1
- package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
- package/dist/modules/dexbot_state_recovery.js +52 -8
- package/dist/modules/dexbot_state_recovery.js.map +1 -1
- package/dist/modules/fund_registry.d.ts +1 -1
- package/dist/modules/fund_registry.js +1 -1
- package/dist/modules/market_adapter_whitelist.js +1 -1
- package/dist/modules/market_adapter_whitelist.js.map +1 -1
- package/dist/modules/node_connect_policy.d.ts +52 -0
- package/dist/modules/node_connect_policy.d.ts.map +1 -0
- package/dist/modules/node_connect_policy.js +35 -0
- package/dist/modules/node_connect_policy.js.map +1 -0
- package/dist/modules/node_manager.d.ts +32 -0
- package/dist/modules/node_manager.d.ts.map +1 -1
- package/dist/modules/node_manager.js +76 -9
- package/dist/modules/node_manager.js.map +1 -1
- package/dist/modules/order/accounting.d.ts +30 -0
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +176 -3
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/export.d.ts.map +1 -1
- package/dist/modules/order/export.js +1 -0
- 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 +189 -25
- 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 +5 -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 +216 -9
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/logger.js +1 -1
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/manager.d.ts +77 -2
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +283 -14
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/strategy.d.ts +3 -0
- package/dist/modules/order/strategy.d.ts.map +1 -1
- package/dist/modules/order/strategy.js +79 -7
- package/dist/modules/order/strategy.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +16 -1
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +16 -2
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +30 -3
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +258 -13
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +666 -13
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +36 -0
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +102 -6
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/validate.d.ts.map +1 -1
- package/dist/modules/order/utils/validate.js +6 -2
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/dist/modules/paths.d.ts +2 -0
- package/dist/modules/paths.d.ts.map +1 -1
- package/dist/modules/paths.js +1 -0
- package/dist/modules/paths.js.map +1 -1
- package/dist/modules/runtime_settings.d.ts +13 -0
- package/dist/modules/runtime_settings.d.ts.map +1 -1
- package/dist/modules/runtime_settings.js +20 -0
- package/dist/modules/runtime_settings.js.map +1 -1
- package/dist/modules/utils/errors.d.ts +16 -0
- package/dist/modules/utils/errors.d.ts.map +1 -1
- package/dist/modules/utils/errors.js +31 -0
- package/dist/modules/utils/errors.js.map +1 -1
- package/dist/pm2.js +7 -7
- package/dist/scripts/analyze-credit.d.ts.map +1 -1
- package/dist/scripts/analyze-credit.js +13 -3
- package/dist/scripts/analyze-credit.js.map +1 -1
- package/dist/scripts/analyze-orders.js +2 -2
- package/dist/scripts/analyze-orders.js.map +1 -1
- package/dist/scripts/diagnose-kibana-candles.d.ts.map +1 -1
- package/dist/scripts/diagnose-kibana-candles.js +0 -1
- package/dist/scripts/diagnose-kibana-candles.js.map +1 -1
- package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +1 -1
- package/dist/scripts/generate_market_adapter_whitelist.js +3 -2
- package/dist/scripts/generate_market_adapter_whitelist.js.map +1 -1
- package/dist/scripts/test-credit-renewal.js +1 -1
- package/dist/scripts/test-credit-renewal.js.map +1 -1
- package/dist/scripts/tv.d.ts +24 -2
- package/dist/scripts/tv.d.ts.map +1 -1
- package/dist/scripts/tv.js +190 -65
- package/dist/scripts/tv.js.map +1 -1
- package/docs/BITSHARES_ONBOARDING.md +19 -4
- package/docs/COW_INVARIANTS.md +18 -1
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +9 -7
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +21 -9
- package/docs/GRID_RECALCULATION.md +13 -13
- package/docs/GRID_RECONCILE.md +12 -2
- package/docs/MPA_CREDIT_USAGE.md +2 -2
- package/docs/README.md +5 -6
- package/docs/architecture.md +0 -58
- package/docs/developer_guide.md +2 -1
- package/docs/docker.md +1 -1
- package/market_adapter/README.md +17 -10
- package/modules/README.md +1 -1
- package/package.json +1 -1
- package/scripts/README.md +9 -7
- package/tests/README.md +2 -2
- package/analysis/results/ama_sweep_results_lp_pool_133_1h.json +0 -2455
- package/analysis/results/bot_fitting_results_lp_pool_133_1h.json +0 -218
- package/analysis/tradingview/h-bts_tradingview.html +0 -1570
- package/analysis/tradingview/t-bts_tradingview.html +0 -1570
|
@@ -111,7 +111,7 @@ function _snapshotFundState(manager) {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
import { floatToBlockchainInt, getPrecisionByOrderType, getPrecisionsForManager, calculateOrderCreationFees, calculateOrderSizes, calculateRotationOrderSizes, calculateAvailableFundsValue, calculateGridSideDivergenceMetric, getPrecisionSlack, getMinOrderSize, getSingleDustThreshold, getGridBestPrices, calculateSpreadFromOrders, allocateFundsByWeights, calculateGapSlots as _mathGapSlots, priceSlotEqual, getBtsSide, getSellStartIdx, resolveGapBand, countGapBandSpread, validatePersistedBoundary, adjustBudgetForBtsFees, clamp, buildGenesisFromPriceLevels, assertSlotPriceInvariant, hashPriceLevels, } from './utils/math.js';
|
|
114
|
-
import { filterOrdersByType, checkSizesBeforeMinimum, checkSizeThreshold, resolveConfiguredPriceBound, shouldFlagOutOfSpread, isOrderHealthy, isPhantomOrder, isSlotAvailable, isOrderOnChain, isOrderPlaced, hasOnChainId, isEmptyGridSlot, parseSlotIndex, calculateIdealBoundary, assignGridRoles, resolveOnChainRetypeType } from './utils/order.js';
|
|
114
|
+
import { filterOrdersByType, checkSizesBeforeMinimum, checkSizeThreshold, resolveConfiguredPriceBound, shouldFlagOutOfSpread, isOrderHealthy, isPhantomOrder, isSlotAvailable, isOrderOnChain, isOrderPlaced, hasOnChainId, isEmptyGridSlot, parseSlotIndex, calculateIdealBoundary, assignGridRoles, resolveOnChainRetypeType, resolveReserveCount, reserveEdgeIdSet, resolveLiveReserveEdgeAnchorPrice } from './utils/order.js';
|
|
115
115
|
import { loadAmaCenterPrice, loadAmaCenterSnapshot, withBlockchainRetry } from './utils/system.js';
|
|
116
116
|
import * as MathUtils from './utils/math.js';
|
|
117
117
|
import { derivePriceWithPoolRef } from './utils/withPoolRef.js';
|
|
@@ -226,8 +226,8 @@ export async function _getSizingContext(manager, side, { skipRecalc = false } =
|
|
|
226
226
|
// BTS fees are paid for ALL order operations regardless of side, so the
|
|
227
227
|
// BTS-holding side reserves fees for both buy and sell target counts.
|
|
228
228
|
if (budget > 0) {
|
|
229
|
-
const targetBuy = Math.max(0, manager.config.activeOrders?.buy ?? 1);
|
|
230
|
-
const targetSell = Math.max(0, manager.config.activeOrders?.sell ?? 1);
|
|
229
|
+
const targetBuy = Math.max(0, manager.config.activeOrders?.buy ?? 1) + resolveReserveCount(manager.config, 'buy');
|
|
230
|
+
const targetSell = Math.max(0, manager.config.activeOrders?.sell ?? 1) + resolveReserveCount(manager.config, 'sell');
|
|
231
231
|
const totalTarget = targetBuy + targetSell;
|
|
232
232
|
const btsOrderType = getBtsSide(manager.config?.assetA, manager.config?.assetB);
|
|
233
233
|
const isBtsSide = isBuy ? (btsOrderType === ORDER_TYPES.BUY) : (btsOrderType === ORDER_TYPES.SELL);
|
|
@@ -680,6 +680,21 @@ export async function loadGrid(manager, grid, boundaryIdx = null, genesisInput =
|
|
|
680
680
|
manager.logger?.log?.(`[GRID-LOAD] Could not re-derive a safe boundary; continuing without one. ` +
|
|
681
681
|
`The next sync cycle will reconcile grid geometry against the chain.`, 'error');
|
|
682
682
|
}
|
|
683
|
+
// The rejected value must not survive: storeMasterGrid
|
|
684
|
+
// never persists a null boundary, so without an explicit
|
|
685
|
+
// erase the poison re-arms this rejection on every boot
|
|
686
|
+
// (Sep-9: boundary=96 rejected identically 3 restarts in
|
|
687
|
+
// a row). Best-effort — load continues boundary-less
|
|
688
|
+
// either way and the next fill batch re-anchors live.
|
|
689
|
+
try {
|
|
690
|
+
const acct = manager?.accountOrders;
|
|
691
|
+
if (acct && typeof acct.clearPersistedBoundary === 'function') {
|
|
692
|
+
await acct.clearPersistedBoundary();
|
|
693
|
+
manager.logger?.log?.(`[GRID-LOAD] Erased poisoned persisted boundary (${boundaryIdx}); ` +
|
|
694
|
+
`restart will load boundary-less instead of re-rejecting.`, 'warn');
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
catch { /* load continues boundary-less either way */ }
|
|
683
698
|
}
|
|
684
699
|
}
|
|
685
700
|
// Restore boundary index for StrategyEngine
|
|
@@ -1080,6 +1095,65 @@ export async function initializeGrid(manager) {
|
|
|
1080
1095
|
`short of ${Math.floor(Number(minScaleSlots))} levels; holding at ${guard.minPrice.toFixed(8)}`, 'info');
|
|
1081
1096
|
resolvedMinP = guard.minPrice;
|
|
1082
1097
|
}
|
|
1098
|
+
// P4 / unanchored rebuild anchor: when the new generation is centered
|
|
1099
|
+
// on a static config value (not a live market source), owed fill
|
|
1100
|
+
// crawls carry the only fresh market direction — fold their net
|
|
1101
|
+
// direction into the rebuild center before the ladder is generated.
|
|
1102
|
+
// A live center already contains the movement, so its owed crawls are
|
|
1103
|
+
// dropped as a stale generation (see the clear below).
|
|
1104
|
+
const owedCrawls = Array.isArray(manager._pendingFillCrawls)
|
|
1105
|
+
? manager._pendingFillCrawls
|
|
1106
|
+
: [];
|
|
1107
|
+
// Whether the ladder CENTER already reflects current market movement.
|
|
1108
|
+
// gpSource describes the gridPrice/bounds reference, NOT the center:
|
|
1109
|
+
// the center is gridStartPrice, built from mp (config.startPrice).
|
|
1110
|
+
// Only a derived startPrice (a non-numeric mode like "pool"/"book"
|
|
1111
|
+
// resolved to a fresh number above) or an AMA-driven center is live:
|
|
1112
|
+
// with gpSource === "ama" the center itself is still mp * (1 +
|
|
1113
|
+
// offset), but the offset comes from the live AMA snapshot, so the
|
|
1114
|
+
// center moves with the market. A numeric startPrice with a live
|
|
1115
|
+
// gridPrice still has a static center — a live gridPrice (bounds
|
|
1116
|
+
// reference only) does not make that static center fresh.
|
|
1117
|
+
const startPriceWasDerived = typeof mpRaw !== 'number' || Number.isNaN(Number(mpRaw));
|
|
1118
|
+
const centerIsLive = startPriceWasDerived || gpSource === 'ama';
|
|
1119
|
+
if (owedCrawls.length > 0 && !centerIsLive) {
|
|
1120
|
+
// Reserve fills never crawl (static insurance). Classify them
|
|
1121
|
+
// against the OLD generation (manager.orders is replaced below)
|
|
1122
|
+
// with the same live anchors the runtime derivation uses, so the
|
|
1123
|
+
// fold can never be driven by a reserve fill.
|
|
1124
|
+
const oldSlots = Array.from(manager.orders?.values?.() ?? []);
|
|
1125
|
+
const edgeAnchors = {
|
|
1126
|
+
buy: resolveLiveReserveEdgeAnchorPrice(manager, 'buy'),
|
|
1127
|
+
sell: resolveLiveReserveEdgeAnchorPrice(manager, 'sell')
|
|
1128
|
+
};
|
|
1129
|
+
const reserveBuyIds = reserveEdgeIdSet(oldSlots, manager.config, ORDER_TYPES.BUY, edgeAnchors.buy);
|
|
1130
|
+
const reserveSellIds = reserveEdgeIdSet(oldSlots, manager.config, ORDER_TYPES.SELL, edgeAnchors.sell);
|
|
1131
|
+
let netShift = 0;
|
|
1132
|
+
for (const e of owedCrawls) {
|
|
1133
|
+
if (e?.side === ORDER_TYPES.BUY && reserveBuyIds?.has(e.slotId))
|
|
1134
|
+
continue;
|
|
1135
|
+
if (e?.side === ORDER_TYPES.SELL && reserveSellIds?.has(e.slotId))
|
|
1136
|
+
continue;
|
|
1137
|
+
if (e?.side === ORDER_TYPES.SELL)
|
|
1138
|
+
netShift++;
|
|
1139
|
+
else if (e?.side === ORDER_TYPES.BUY)
|
|
1140
|
+
netShift--;
|
|
1141
|
+
}
|
|
1142
|
+
const stepPct = Number(manager.config?.incrementPercent);
|
|
1143
|
+
if (netShift !== 0 && Number.isFinite(stepPct) && stepPct > 0) {
|
|
1144
|
+
// One crawl moves the boundary one slot; the geometric ladder
|
|
1145
|
+
// step is incrementPercent, so shift the center by netShift steps.
|
|
1146
|
+
const folded = gridStartPrice * Math.pow(1 + stepPct / 100, netShift);
|
|
1147
|
+
// Re-clamp to the post-guard resolved bounds so a fold cannot
|
|
1148
|
+
// push the center outside the rail it is about to generate.
|
|
1149
|
+
const clampMin = Number.isFinite(Number(resolvedMinP)) ? Number(resolvedMinP) : rMinP;
|
|
1150
|
+
const clampMax = Number.isFinite(Number(resolvedMaxP)) ? Number(resolvedMaxP) : rMaxP;
|
|
1151
|
+
gridStartPrice = Math.max(clampMin, Math.min(clampMax, folded));
|
|
1152
|
+
manager.config.startPrice = gridStartPrice;
|
|
1153
|
+
manager.logger?.log?.(`[BOUNDARY] Folded ${owedCrawls.length} owed fill crawl(s) (net ${netShift}) into ` +
|
|
1154
|
+
`static grid center -> ${gridStartPrice.toFixed(8)}`, 'info');
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1083
1157
|
manager.config.minPrice = resolvedMinP;
|
|
1084
1158
|
manager.config.maxPrice = resolvedMaxP;
|
|
1085
1159
|
manager._lastGridPricingContext = {
|
|
@@ -1112,6 +1186,12 @@ export async function initializeGrid(manager) {
|
|
|
1112
1186
|
manager._gapSlots = gapSlots;
|
|
1113
1187
|
if (genesis)
|
|
1114
1188
|
manager._genesis = genesis;
|
|
1189
|
+
// A rebuilt grid is a NEW generation: the boundary below is re-derived
|
|
1190
|
+
// absolutely from the fresh price ladder, so owed fill crawls recorded
|
|
1191
|
+
// against the previous generation must not survive — their relative
|
|
1192
|
+
// deltas would shift the new anchor again for movement it already
|
|
1193
|
+
// contains (and their slot ids now name different prices).
|
|
1194
|
+
manager._clearPendingFillCrawls?.('grid rebuild');
|
|
1115
1195
|
// RC-8: Update boundary with notification to dependent systems
|
|
1116
1196
|
// Persist master boundary for StrategyEngine
|
|
1117
1197
|
if (manager.boundaryIdx !== boundaryIdx) {
|
|
@@ -1309,11 +1389,17 @@ export async function recalculateGrid(manager, opts) {
|
|
|
1309
1389
|
* @returns {any}
|
|
1310
1390
|
*/
|
|
1311
1391
|
export function checkAndUpdateGridIfNeeded(manager) {
|
|
1312
|
-
const
|
|
1392
|
+
const rawThreshold = manager.config?.gridLimits?.GRID_REGENERATION_PERCENTAGE;
|
|
1393
|
+
// NOTE: a configured 0 (or non-numeric) falls back to the global default
|
|
1394
|
+
// instead of disabling. 0 never worked as "disable" (ratio >= 0 is always
|
|
1395
|
+
// true, i.e. constant regen), so no working configuration is broken by this.
|
|
1396
|
+
const threshold = (Number.isFinite(Number(rawThreshold)) && Number(rawThreshold) > 0)
|
|
1397
|
+
? Number(rawThreshold)
|
|
1398
|
+
: GRID_LIMITS.GRID_REGENERATION_PERCENTAGE;
|
|
1313
1399
|
const chainSnap = manager.getChainFundsSnapshot();
|
|
1314
1400
|
const gridBuy = Number(manager.funds?.total?.grid?.buy || 0);
|
|
1315
1401
|
const gridSell = Number(manager.funds?.total?.grid?.sell || 0);
|
|
1316
|
-
const result = { buyUpdated: false, sellUpdated: false };
|
|
1402
|
+
const result = { buyUpdated: false, sellUpdated: false, buyShrink: false, sellShrink: false };
|
|
1317
1403
|
const sides = [
|
|
1318
1404
|
{ name: 'buy', grid: gridBuy, orderType: ORDER_TYPES.BUY },
|
|
1319
1405
|
{ name: 'sell', grid: gridSell, orderType: ORDER_TYPES.SELL }
|
|
@@ -1321,21 +1407,50 @@ export function checkAndUpdateGridIfNeeded(manager) {
|
|
|
1321
1407
|
for (const s of sides) {
|
|
1322
1408
|
if (s.grid <= 0)
|
|
1323
1409
|
continue;
|
|
1324
|
-
const
|
|
1410
|
+
const feeActiveOrders = manager.config.activeOrders && typeof manager.config.activeOrders === 'object'
|
|
1411
|
+
? {
|
|
1412
|
+
...manager.config.activeOrders,
|
|
1413
|
+
buy: Math.max(0, Number(manager.config.activeOrders.buy) || 0) + resolveReserveCount(manager.config, 'buy'),
|
|
1414
|
+
sell: Math.max(0, Number(manager.config.activeOrders.sell) || 0) + resolveReserveCount(manager.config, 'sell'),
|
|
1415
|
+
}
|
|
1416
|
+
: manager.config.activeOrders;
|
|
1417
|
+
const availableFunds = calculateAvailableFundsValue(s.name, manager.accountTotals, manager.funds, manager.config.assetA, manager.config.assetB, feeActiveOrders, manager.config.min_BTS_value, manager.config.feeParams ?? null);
|
|
1325
1418
|
// Denominator: side's allocated capital (or chain total fallback).
|
|
1326
1419
|
const allocated = s.name === 'buy' ? chainSnap.allocatedBuy : chainSnap.allocatedSell;
|
|
1327
1420
|
const denominator = (allocated > 0) ? allocated : (s.grid + availableFunds);
|
|
1328
1421
|
const ratio = (denominator > 0) ? (availableFunds / denominator) * 100 : 0;
|
|
1329
|
-
|
|
1330
|
-
|
|
1422
|
+
// DOWNSIDE: grid-tracked size exceeds the (botFunds-capped) allocation,
|
|
1423
|
+
// so the side must shrink toward the new budget via the same resize path.
|
|
1424
|
+
// s.grid is funds.total.grid (ACTIVE + PARTIAL + VIRTUAL — planned size,
|
|
1425
|
+
// not just on-chain committed). Deliberately NOT based on per-side
|
|
1426
|
+
// chain-total drops: a normal fill moves value across sides (pays one
|
|
1427
|
+
// asset, receives the other), so one side's total routinely drops >=3%
|
|
1428
|
+
// on ordinary fills — and the fill pipeline already re-sizes from the
|
|
1429
|
+
// post-fill budget. External removal is caught here because the
|
|
1430
|
+
// committed/planned grid stays put while the allocation sinks.
|
|
1431
|
+
// NOTE: with an explicit 0 allocation the shared fallback denominator
|
|
1432
|
+
// yields overAlloc ≈ +100%, i.e. a 0%-funded side holding size always
|
|
1433
|
+
// triggers — which is the desired unwind (zero budget sizes everything
|
|
1434
|
+
// to 0 and the correction pass cancels the surplus; self-clearing).
|
|
1435
|
+
const overAlloc = (denominator > 0) ? ((s.grid - allocated) / denominator) * 100 : 0;
|
|
1436
|
+
const growTrigger = ratio >= threshold;
|
|
1437
|
+
const shrinkTrigger = overAlloc >= threshold;
|
|
1438
|
+
manager.logger?.log?.(`[DIVERGENCE] ${s.name.toUpperCase()} ratio check: availableFunds=${availableFunds.toFixed(5)}, allocated=${allocated.toFixed(5)}, ratio=${ratio.toFixed(4)}% (threshold=${threshold}%) → ${growTrigger ? 'TRIGGER-GROW' : 'no trigger'} | overAlloc=${overAlloc.toFixed(4)}% → ${shrinkTrigger ? 'TRIGGER-SHRINK' : 'no trigger'}`, 'debug');
|
|
1439
|
+
if (growTrigger || shrinkTrigger) {
|
|
1331
1440
|
// RC-3: Use Set for automatic duplicate prevention
|
|
1332
1441
|
if (!(manager._gridSidesUpdated instanceof Set))
|
|
1333
1442
|
manager._gridSidesUpdated = new Set();
|
|
1334
1443
|
manager._gridSidesUpdated.add(s.orderType);
|
|
1335
|
-
if (s.name === 'buy')
|
|
1444
|
+
if (s.name === 'buy') {
|
|
1336
1445
|
result.buyUpdated = true;
|
|
1337
|
-
|
|
1446
|
+
if (shrinkTrigger)
|
|
1447
|
+
result.buyShrink = true;
|
|
1448
|
+
}
|
|
1449
|
+
else {
|
|
1338
1450
|
result.sellUpdated = true;
|
|
1451
|
+
if (shrinkTrigger)
|
|
1452
|
+
result.sellShrink = true;
|
|
1453
|
+
}
|
|
1339
1454
|
}
|
|
1340
1455
|
}
|
|
1341
1456
|
return result;
|
|
@@ -1428,7 +1543,11 @@ export async function _recalculateGridOrderSizesFromBlockchain(manager, orderTyp
|
|
|
1428
1543
|
// geometric progression including empties, then re-type the picked slot
|
|
1429
1544
|
// to BUY/SELL before placement. The COW boundary-shift path re-types
|
|
1430
1545
|
// the working grid by geometry first, so crossers stay in the correct
|
|
1431
|
-
// side's denominator.
|
|
1546
|
+
// side's denominator. Reserve edge slots are deliberately excluded from
|
|
1547
|
+
// that startup re-derivation: they activate only with the size the
|
|
1548
|
+
// target-grid sizing pipeline has already written (exact values, one
|
|
1549
|
+
// sizing rule), and an unsized reserve waits for that pipeline instead
|
|
1550
|
+
// of being placed with a locally guessed size.
|
|
1432
1551
|
const orderSource = collectActions ? workingGrid : manager.orders;
|
|
1433
1552
|
const allSideSlots = Array.from(orderSource.values())
|
|
1434
1553
|
.filter((o) => o.type === orderType)
|
|
@@ -1447,6 +1566,15 @@ export async function _recalculateGridOrderSizesFromBlockchain(manager, orderTyp
|
|
|
1447
1566
|
// Apply new sizes to all slots on the side
|
|
1448
1567
|
for (let i = 0; i < allSideSlots.length; i++) {
|
|
1449
1568
|
const slot = allSideSlots[i];
|
|
1569
|
+
// Shelf/manual orders (non-slot-N ids, e.g. fork-kept deep-*
|
|
1570
|
+
// below the rail) sit outside window accounting: geometric
|
|
1571
|
+
// ideals must never resize them on-chain. Same gate as reserve
|
|
1572
|
+
// classification and startup cancel candidates (issue #27
|
|
1573
|
+
// follow-up). No-op upstream (grids only mint slot-N).
|
|
1574
|
+
// NB: they stay in the denominator above (budget math
|
|
1575
|
+
// unchanged); only the per-slot mutation below is skipped.
|
|
1576
|
+
if (parseSlotIndex(slot?.id) === null)
|
|
1577
|
+
continue;
|
|
1450
1578
|
let newSize = newSizes[i] || 0;
|
|
1451
1579
|
// FUND CAPPING FOR COMMITTED (ON-CHAIN) ORDERS:
|
|
1452
1580
|
// Only ACTIVE/PARTIAL orders are constrained by available funds.
|
|
@@ -1744,8 +1872,8 @@ export async function monitorDivergence(manager, calculatedGrid, persistedGrid)
|
|
|
1744
1872
|
const { getOrderTypeFromUpdatedFlags } = require('./utils/order');
|
|
1745
1873
|
return {
|
|
1746
1874
|
needsUpdate: true,
|
|
1747
|
-
buy: { updated: ratioResult.buyUpdated, ratio: ratioResult.buyUpdated, rms: false, metric: 0 },
|
|
1748
|
-
sell: { updated: ratioResult.sellUpdated, ratio: ratioResult.sellUpdated, rms: false, metric: 0 },
|
|
1875
|
+
buy: { updated: ratioResult.buyUpdated, ratio: ratioResult.buyUpdated, rms: false, metric: 0, shrink: ratioResult.buyShrink === true },
|
|
1876
|
+
sell: { updated: ratioResult.sellUpdated, ratio: ratioResult.sellUpdated, rms: false, metric: 0, shrink: ratioResult.sellShrink === true },
|
|
1749
1877
|
orderType: getOrderTypeFromUpdatedFlags(ratioResult.buyUpdated, ratioResult.sellUpdated)
|
|
1750
1878
|
};
|
|
1751
1879
|
}
|
|
@@ -1756,8 +1884,8 @@ export async function monitorDivergence(manager, calculatedGrid, persistedGrid)
|
|
|
1756
1884
|
const { getOrderTypeFromUpdatedFlags } = require('./utils/order');
|
|
1757
1885
|
return {
|
|
1758
1886
|
needsUpdate: buyUpdated || sellUpdated,
|
|
1759
|
-
buy: { updated: buyUpdated, ratio: ratioResult.buyUpdated, rms: rmsResult.buy.updated, metric: rmsResult.buy.metric },
|
|
1760
|
-
sell: { updated: sellUpdated, ratio: ratioResult.sellUpdated, rms: rmsResult.sell.updated, metric: rmsResult.sell.metric },
|
|
1887
|
+
buy: { updated: buyUpdated, ratio: ratioResult.buyUpdated, rms: rmsResult.buy.updated, metric: rmsResult.buy.metric, shrink: ratioResult.buyShrink === true },
|
|
1888
|
+
sell: { updated: sellUpdated, ratio: ratioResult.sellUpdated, rms: rmsResult.sell.updated, metric: rmsResult.sell.metric, shrink: ratioResult.sellShrink === true },
|
|
1761
1889
|
orderType: getOrderTypeFromUpdatedFlags(buyUpdated, sellUpdated)
|
|
1762
1890
|
};
|
|
1763
1891
|
}
|
|
@@ -1768,14 +1896,21 @@ export async function monitorDivergence(manager, calculatedGrid, persistedGrid)
|
|
|
1768
1896
|
* @returns {{onChainBuys: Array<import('./types').Order>, onChainSells: Array<import('./types').Order>}}
|
|
1769
1897
|
*/
|
|
1770
1898
|
function _getOnChainOrders(manager) {
|
|
1899
|
+
// Slot-N gated: fork-kept shelf/manual orders (non-slot-N ids, e.g.
|
|
1900
|
+
// deep-*) sit outside window accounting — same gate as reserve
|
|
1901
|
+
// classification and startup cancel candidates (issue #27 follow-up).
|
|
1902
|
+
// Without this, a live shelf masks an empty window side (oneSideEmpty
|
|
1903
|
+
// stays false) and skews the spread inputs. No-op on grids that only
|
|
1904
|
+
// mint slot-N ids.
|
|
1905
|
+
const isGridSlot = (o) => o?.orderId && Number(o?.size || 0) > 0 && parseSlotIndex(o?.id) !== null;
|
|
1771
1906
|
const onChainBuys = [
|
|
1772
1907
|
...manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.ACTIVE),
|
|
1773
1908
|
...manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.PARTIAL)
|
|
1774
|
-
].filter(
|
|
1909
|
+
].filter(isGridSlot);
|
|
1775
1910
|
const onChainSells = [
|
|
1776
1911
|
...manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.ACTIVE),
|
|
1777
1912
|
...manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.PARTIAL)
|
|
1778
|
-
].filter(
|
|
1913
|
+
].filter(isGridSlot);
|
|
1779
1914
|
return { onChainBuys, onChainSells };
|
|
1780
1915
|
}
|
|
1781
1916
|
/**
|
|
@@ -1847,11 +1982,11 @@ export async function checkSpreadCondition(manager, _BitShares, updateOrdersOnCh
|
|
|
1847
1982
|
const toleranceSteps = 0.5;
|
|
1848
1983
|
const buyCount = manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.ACTIVE)
|
|
1849
1984
|
.concat(manager.getOrdersByTypeAndState(ORDER_TYPES.BUY, ORDER_STATES.PARTIAL))
|
|
1850
|
-
.filter((o) => o?.orderId && Number(o?.size || 0) > 0)
|
|
1985
|
+
.filter((o) => o?.orderId && Number(o?.size || 0) > 0 && parseSlotIndex(o?.id) !== null)
|
|
1851
1986
|
.length;
|
|
1852
1987
|
const sellCount = manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.ACTIVE)
|
|
1853
1988
|
.concat(manager.getOrdersByTypeAndState(ORDER_TYPES.SELL, ORDER_STATES.PARTIAL))
|
|
1854
|
-
.filter((o) => o?.orderId && Number(o?.size || 0) > 0)
|
|
1989
|
+
.filter((o) => o?.orderId && Number(o?.size || 0) > 0 && parseSlotIndex(o?.id) !== null)
|
|
1855
1990
|
.length;
|
|
1856
1991
|
manager.outOfSpread = shouldFlagOutOfSpread(currentSpread, nominalSpread, toleranceSteps, buyCount, sellCount, manager.config.incrementPercent);
|
|
1857
1992
|
if (manager.outOfSpread === 0)
|
|
@@ -1890,7 +2025,12 @@ export async function checkSpreadCondition(manager, _BitShares, updateOrdersOnCh
|
|
|
1890
2025
|
}
|
|
1891
2026
|
// Limit spread = nominal + half increment tolerance (0.5 steps).
|
|
1892
2027
|
const limitSpread = nominalSpread + (manager.config.incrementPercent * toleranceSteps);
|
|
1893
|
-
|
|
2028
|
+
// One-sided book: currentSpread is Infinity (no opposing quote), so
|
|
2029
|
+
// log the empty side instead of a bogus "0% > limit" comparison.
|
|
2030
|
+
const spreadDesc = oneSideEmpty
|
|
2031
|
+
? `one-sided (${onChainBuys.length === 0 ? 'no buys' : 'no sells'})`
|
|
2032
|
+
: `${Format.formatPercent(currentSpread)} > ${Format.formatPercent(limitSpread)}`;
|
|
2033
|
+
manager.logger?.log?.(`Spread too wide (${spreadDesc}), correcting with ${manager.outOfSpread} extra slot(s)...`, 'warn');
|
|
1894
2034
|
// Refresh funds before the side decision below: the top-of-tick recalc may
|
|
1895
2035
|
// predate fills processed since, and determineOrderSideByFunds reads
|
|
1896
2036
|
// manager.funds/accountTotals directly (not via _getSizingContext). No-op
|
|
@@ -2384,11 +2524,16 @@ export async function prepareSpreadCorrectionOrders(manager, preferredSide, outO
|
|
|
2384
2524
|
const missingSlots = configuredMissingSlots > 0
|
|
2385
2525
|
? Math.floor(configuredMissingSlots)
|
|
2386
2526
|
: 1;
|
|
2387
|
-
// STRATEGY:
|
|
2527
|
+
// STRATEGY: Window-Contiguous Correction (Safe Bridging)
|
|
2388
2528
|
// Instead of calculating a "mid-price" (which can be dangerous in wide gaps),
|
|
2389
|
-
// we
|
|
2529
|
+
// we extend the live window contiguously so a fund-constrained correction
|
|
2530
|
+
// never leaves an interior hole.
|
|
2390
2531
|
// 1. Priority: Update existing PARTIAL orders at the edge (Highest Buy / Lowest Sell).
|
|
2391
|
-
// 2. Fallback: Activate
|
|
2532
|
+
// 2. Fallback: Activate empty slots window-contiguous-first (Lowest Buy-rail /
|
|
2533
|
+
// Highest Sell-rail, adjacent to the live window top). A rail with no
|
|
2534
|
+
// live orders has no window to extend, so it falls back to
|
|
2535
|
+
// spread-edge-first (Highest Buy / Lowest Sell) to close the spread
|
|
2536
|
+
// near market first.
|
|
2392
2537
|
const allOrders = Array.from(manager.orders.values());
|
|
2393
2538
|
// Boundary-correct type computation — hoisted before edge-partial and
|
|
2394
2539
|
// candidate filters so all call sites (including the edge-partial filter
|
|
@@ -2441,11 +2586,30 @@ export async function prepareSpreadCorrectionOrders(manager, preferredSide, outO
|
|
|
2441
2586
|
// boundary-correct type so a SPREAD slot that, after a boundary shift, now sits
|
|
2442
2587
|
// in the BUY or SELL zone is excluded — it would otherwise be placed on the
|
|
2443
2588
|
// correction side at a price the grid already considers the opposite side.
|
|
2589
|
+
// Candidate ordering: window-contiguous-first when the rail has live
|
|
2590
|
+
// orders to extend (BUY lowest first, SELL highest first, both adjacent
|
|
2591
|
+
// to the live window top). A fully-empty rail has no window anchor —
|
|
2592
|
+
// "lowest buy" would be the rail bottom, placing deep orders while the
|
|
2593
|
+
// near-market gap stays open — so it falls back to spread-edge-first
|
|
2594
|
+
// (BUY highest, SELL lowest) to close the spread near market first.
|
|
2595
|
+
// The gap-band promotion path below stays edge-first by necessity
|
|
2596
|
+
// (boundary derivation requires contiguity), so under fund shortage
|
|
2597
|
+
// in-rail holes heal before band slots.
|
|
2598
|
+
const railHasLiveOrders = allOrders.some((o) => getSlotCorrectType(o) === railType && isOrderPlaced(o));
|
|
2599
|
+
const sortCandidates = (a, b) => {
|
|
2600
|
+
const edgeFirst = railType === ORDER_TYPES.BUY
|
|
2601
|
+
? b.price - a.price
|
|
2602
|
+
: a.price - b.price;
|
|
2603
|
+
const windowFirst = railType === ORDER_TYPES.BUY
|
|
2604
|
+
? a.price - b.price
|
|
2605
|
+
: b.price - a.price;
|
|
2606
|
+
return railHasLiveOrders ? windowFirst : edgeFirst;
|
|
2607
|
+
};
|
|
2444
2608
|
const typedSpreadCandidates = allOrders
|
|
2445
2609
|
.filter((o) => o.type === ORDER_TYPES.SPREAD
|
|
2446
2610
|
&& isSlotAvailable(o)
|
|
2447
2611
|
&& getSlotCorrectType(o) === railType)
|
|
2448
|
-
.sort(
|
|
2612
|
+
.sort(sortCandidates)
|
|
2449
2613
|
.slice(0, missingSlots);
|
|
2450
2614
|
// Secondary candidates: orphaned virtual slots that have lost their
|
|
2451
2615
|
// order (e.g. stale-cleaned after a race condition during a crash, or a
|
|
@@ -2477,7 +2641,7 @@ export async function prepareSpreadCorrectionOrders(manager, preferredSide, outO
|
|
|
2477
2641
|
&& o.state === ORDER_STATES.VIRTUAL
|
|
2478
2642
|
&& !o.orderId
|
|
2479
2643
|
&& getSlotCorrectType(o) === railType)
|
|
2480
|
-
.sort(
|
|
2644
|
+
.sort(sortCandidates)
|
|
2481
2645
|
.slice(0, missingSlots);
|
|
2482
2646
|
// If the funded rail is full, the spread itself may be stale: the
|
|
2483
2647
|
// nearest empty slots are still in the gap band. Promote contiguous
|