dexbot 1.5.3 → 1.6.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 +62 -0
- package/README.md +4 -3
- 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 +3 -3
- 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 +13 -0
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +68 -4
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +8 -0
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +57 -2
- 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 +41 -1
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +135 -5
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +79 -12
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +554 -46
- 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 +36 -7
- 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/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 +143 -15
- 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 +182 -6
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/manager.d.ts +64 -2
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +231 -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 +11 -1
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +24 -1
- 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 +662 -12
- 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/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 +9 -2
- package/docs/COW_INVARIANTS.md +18 -1
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +8 -7
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +21 -9
- package/docs/GRID_RECALCULATION.md +5 -6
- 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 +4 -0
- package/modules/README.md +1 -1
- package/package.json +1 -1
- package/scripts/README.md +5 -3
- package/tests/README.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.6.0] - 2026-09-12 - Node-Failure Strike Ledger, Grid Regen, TV Order Overlay, Balance Heal, Live Config Pickup, Reserve Ladder, Boundary Recovery, Candle Cache Unification, Chart Upgrades, Shelf/Startup Hardening
|
|
6
|
+
|
|
7
|
+
### 2026-09-12
|
|
8
|
+
|
|
9
|
+
- **Fix(startup)**: guard startup excess cancels against shelf orders and regress the reserve trigger (issue #27) — hoisting the matched-excess selection made it reachable on every startup, but without the slot-N gate the cheapest-first order wiped fork-kept shelf orders on the next boot; `matchedExcess` is now filtered to `parseSlotIndex(id) !== null` in both planOnly and execute branches. Tests: `getTargetedSyncReason` block (surplus with empty floor reserves fires `buy reserves 0/2`, filled/disabled/empty-budget configs stay silent) and shelf block (exact rail cancel set, plan/execute parity, floor reserves + closest window + shelf survive) (`modules/order/grid_reconcile_internal.ts`, `tests/test_reserve_orders.ts`).
|
|
10
|
+
- **Fix(funds)**: skip shelf orders in geometric size recalc (issue #27 follow-up) — the divergence recalc distributed the side budget over every slot carrying the side's type and emitted on-chain UPDATEs for anything off-ideal, so fork-kept shelf orders with manual sizes took curve ideals on the first post-startup divergence pass; `_recalculateGridOrderSizesFromBlockchain` skips non-slot-N slots in the per-slot loop (same `parseSlotIndex` gate as reserve classification; shelf stays in the denominator so budget math is unchanged, only the mutation is skipped) (`modules/order/grid.ts`, `tests/test_cow_divergence_correction.ts` Test 7).
|
|
11
|
+
|
|
12
|
+
### 2026-09-11
|
|
13
|
+
|
|
14
|
+
- **Feat(node)**: node-failure strike ledger and broadcast-deferred fill rebalancing — a live bot run showed two compounding reliability gaps: a broadcast region outliving the fill lock's acquisition timeout cascaded into repeated "Lock acquisition timeout" consumer failures, and flapping nodes escaped blacklisting because a single successful health probe erased live-transport strikes while the transport kept re-selecting them on reconnect. New `node_connect_policy.ts` manages a failure ledger with `LIVE_FAILURE_HEALTH_SUCCESS_STREAK` (2 consecutive probe successes to reset, transport-sourced strikes clear on first success); `bitshares-native/transport.ts` feeds strikes via `onNodeFailure` observer (abnormal closes as `"connection"` strikes, keep-alive trips as `"keep-alive"` with follow-up suppression, preferred-candidate pass in `tryConnect`); `node_manager.ts` applies strikes and resets; `dexbot_cow_runtime.ts` / `dexbot_fill_runtime.ts` / `subscriptions.ts` handle broadcast deferrals and fill rebalancing; `docs/COW_INVARIANTS.md` updated (`docs/COW_INVARIANTS.md`, `modules/bitshares-native/*`, `modules/node_connect_policy.ts`, `modules/node_manager.ts`, `modules/bitshares_client.ts`, `modules/constants.ts`, `modules/dexbot_class.ts`, `modules/dexbot_cow_runtime.ts`, `modules/dexbot_fill_runtime.ts`, `tests/test_node_connect_policy.ts`, `tests/test_node_failover.ts`, `tests/test_fill_pipeline_robustness.ts`, `tests/test_native_transport.ts`).
|
|
15
|
+
- **Feat(trust-chain)**: guarded trust-chain free-balance heal and deferred-drain fill tolerance — a fill/broadcast chaos window left a persistent one-sided tracked-free drift that recovery could never repair, and deferred fill drains repeatedly tripped the fund invariant on already-processed ledger deltas. New `consumeDeferredDrainMarker()` helper marks drain residue; the cycle that runs parked fills stamps `manager._orphanFillsCreditedAt` to widen fund-invariant tolerance (x5) for that cycle only. New `_fundDriftLedger` records one-sided drift; `_tryTrustChainFreeHeal()` seeds tracked free balance from chain total minus committed grid sizes, gated by `FUND_INVARIANT_HEAL_ON_RECOVERY_FAIL` (`modules/order/accounting.ts`, `modules/dexbot_fill_runtime.ts`, `modules/dexbot_class.ts`, `modules/dexbot_maintenance_runtime.ts`, `modules/constants.ts`, `tests/test_fund_drift_heal.ts`, `tests/test_fill_pipeline_robustness.ts`).
|
|
16
|
+
- **Feat(tv)**: TradingView order overlay on the rewritten chart exporter — restores the on-chart order overlay lost in the exporter rewrite and folds in interaction, label, and data-source fixes iterated while validating. Re-adds order rendering from the orders-file (no-orders handling, MKT/DEEP liquidity panel), gates on order data with base-unit volume, derives canonical gridLo, restores drag-pan and X-range sync, uses plain-decimal price labels, removes last-price dashed line, turns off uPlot gridlines, wires the update marker end-to-end, moves timeframes onto the AMA row, and uses English BUYS/SELLS labels and en-US dates (`analysis/tradingview/tradingview_uplot_chart_generator.ts`, `analysis/tradingview/analyze_tradingview.ts`, `analysis/tradingview/README.md`, `scripts/README.md`).
|
|
17
|
+
|
|
18
|
+
- **Fix(credit)**: full offer id and hide Curr. CR pairs without live funds — Curr. CR lines print the full offer object id (e.g. `1.21.681`) instead of the short numeric segment, and skip the line entirely when there is no live offer or the available balance is null/zero so `no funds (...)` rows no longer print; Avar. CR still covers own positions (`scripts/analyze-credit.ts`).
|
|
19
|
+
- **Fix(reserve)**: reserve-aware targeted sync trigger plus live-config docs note (issue #27) — a live-applied `reserveOrders` increase never placed orders because the targeted-sync shortfall compared live window+reserves against target window+reserves, so a pre-existing window surplus masked the reserve deficit until fills or the 4h fetch; new `countLiveReserveOrders` (canonical `reserveEdgeIdSet` + live-anchor classification over the full master grid, intersected with live ACTIVE/PARTIAL orderIds, fail-closed without a full grid) feeds an independent `"<side> reserves live/required"` reason in `getTargetedSyncReason`, budget-gated like the window check. Docs: new "Editing a running bot" subsection in `docs/BITSHARES_ONBOARDING.md` mirroring `BOT_LIVE_CONFIG_KEYS` (live keys apply in ~1min, geometry needs `dexbot reset`, identity needs restart) (`modules/dexbot_maintenance_runtime.ts`, `docs/BITSHARES_ONBOARDING.md`, `tests/test_reserve_orders.ts`).
|
|
20
|
+
- **Feat(tv)**: monthly candles, reordered market badge, stat badges — new `1M` timeframe with true UTC-calendar-month bucketing in `aggregateCandles` and Mon-YYYY axis labels; market panel row order changed to SELL/Market/BUY; new bottom-right range panel (visible-window High red / Low green, mirroring SELL/BUY badge colors) and bottom-right volume badge (visible-window max volume only, hides with the volume chart), both refreshed on rerender and glued to the visible window via throttled setScale hooks on both charts (`analysis/tradingview/tradingview_uplot_chart_generator.ts`, `analysis/tradingview/README.md`).
|
|
21
|
+
- **Feat(tv)**: rigid plot pan, axis-gutter scaling, and Shift+wheel price zoom in the uPlot chart — plot drags pan time + price with locked span (sets a manual price range) instead of squashing the visible span mid-drag; Y scaling lives on the price-axis gutter (wheel/drag) and Shift+wheel (cursor-anchored, price pane only); new time-axis gutter drag scales the timeframe around its center, synced across panes; rAF-throttled auto-Y refit to the visible window on every x setScale respects a user-locked manual range, double-click on the price axis returns to autofit; zoom-out band widened (floor min/2, ceil max*2) (`analysis/tradingview/tradingview_uplot_chart_generator.ts`).
|
|
22
|
+
- **Fix(cache)**: trust only genuine query coverage when reusing cached LP candle windows — the immutable-gap pruner treated everything before the first locally cached bucket as proven-empty history, so stray boundary candles from a neighboring window's chunk file certified 700+ unqueried hours as empty and a whole month rendered flat; missing ranges are now pruned only when a chunk file's recorded `queriedRanges` genuinely cover them (one genuine full fetch still caches truly quiet spans permanently; previously skipped windows self-heal on the next run) (`market_adapter/inputs/window_cache.ts`, `tests/test_window_cache.ts`, `tests/test_fetch_lp_data_logic.ts`).
|
|
23
|
+
- **Feat(tv)**: clickable base/quote volume toggle with currency suffix — toolbar unit button plus clickable legend Vol value and V-max badge switch base/quote units, persisted per chart in localStorage (`volumeMode`) alongside the AMA settings; legend, hover tooltip, and V-max badge always render the currency suffix (e.g. `1.2M BTS`), axis ticks stay numeric; quote derived client-side as base x display close per candle; feed charts (volume = publish count) show the `feeds` suffix and disable the switch via the `volumeIsCount` payload flag (`analysis/tradingview/tradingview_uplot_chart_generator.ts`, `analysis/tradingview/README.md`).
|
|
24
|
+
- **Fix(tv)**: honest volume affordance on feed charts — legend Vol value and max badge no longer show a pointer cursor with a click-to-switch hint on feed charts where the toggle is dead by design (default cursor + feed title instead); count unit renamed from `pubs` to `feeds`; badge label shortened from `V max` to `max` (`analysis/tradingview/tradingview_uplot_chart_generator.ts`).
|
|
25
|
+
- **Feat(cache)**: unify Kibana candle caching on `runCachedWindows`, harden fetch robustness — pool, book, and feed fetches share one cache entry point (LP migrates off its bespoke manifest loop; sidecar `*.fetch_manifest.json` no longer written, legacy files still read); new `fetchRangeWithRetry` (per-range attempts + linear backoff + abort-signal timeout; LP keeps its 4-attempt budget, book/feed keep single-shot default); partial windows merge into output but are never persisted; `kibanaSearch` retries transient errors (3 attempts) for one-shot queries, `kibanaMaxPages` (500) runaway guard on paged fetchers, bidirectional fetch tolerates one-direction failure; single `isTransientNetworkError`/`sleepMs` (`modules/utils/errors.ts`) and single `slugPart` (`market_adapter/interval_utils.ts`); dead `consolidateByTimestamp` key removed (new `tests/test_fetch_book_data.ts`, `tests/test_kibana_candles.ts` cases; `market_adapter/inputs/*`, `market_adapter/core/*`).
|
|
26
|
+
- **Fix(reserve)**: exclude non-slot-N shelf ids from reserve classification and placement (issue #27 follow-up) — fork-kept shelf orders (live non-slot-N ids below the rail, e.g. `deep-*`) were classified as the reserve edge in every anchor outcome, so the targeted-sync reserve deficit could never fire while the shelf was live, and the Tier-2 live-anchor scan let the cheapest shelf drag the anchor to itself (`isSlotInRail` is fail-open for unparseable ids); `reserveEdgeIdSet`, `resolveLiveReserveEdgeAnchorPrice`, `_pickEdgeReserveSlots`, and `pickEdgeReserves` now gate to `parseSlotIndex(id) !== null` so classification and placement agree (no-op on grids that only mint slot-N ids) (`modules/order/utils/order.ts`, `modules/order/grid_reconcile_internal.ts`, `modules/order/manager.ts`, `tests/test_reserve_orders.ts`).
|
|
27
|
+
- **Fix(reserve)**: exclude window members from the live-reserve count (issue #27 follow-up) — `countLiveReserveOrders` classified reserves without excluding window members while every placement picker passes window ids as `excludeIds`, so when the active window reached the grid edge the edge pick landed on live window orders and the count read N/N with zero dedicated reserves; new `liveWindowIdSet` helper derives window member ids from master rail geometry (sliced to `activeOrders`, fail-open on unknown boundary) and `reserveEdgeIdSet` takes an optional `excludeIds` forward (`modules/order/utils/order.ts`, `modules/dexbot_maintenance_runtime.ts`, `tests/test_reserve_orders.ts`).
|
|
28
|
+
- **Fix(startup)**: startup excess plans matched-surplus cancels (issue #27 follow-up) — startup reconcile always runs `planOnly`, but the planOnly branch recorded only unmatched-orphan cancels while the matched-surplus leg (`cancelCount = chainCount - targetCount`, reserve edge last) lived only in the execute branch, so on a fully-placed grid the surplus was silently dropped every restart with zero cancel ops; the matched-excess selection is hoisted above the planOnly/execute split so both share one ordering (orphans first, matched after, reserve edge last), and planOnly omits `releaseUntrackedFunds` for matched slots (funds tracked on the grid slot) (`modules/order/grid_reconcile_internal.ts`, `tests/test_reserve_orders.ts`; full suite 276 passed).
|
|
29
|
+
- **Fix(feed)**: align feed volume and AMA timeframes — cross-feed publication counts are averaged across both feed legs instead of summed, comparable feed-rate values are preserved on weekly/monthly candles, and the first sampled AMA value anchors to its warmup average so higher-timeframe charts show no initialization gap (`market_adapter/inputs/kibana_feed_source.ts`, `market_adapter/candle_utils.ts`, `tests/test_kibana_feed_source.ts`).
|
|
30
|
+
|
|
31
|
+
### 2026-09-10
|
|
32
|
+
|
|
33
|
+
- **Feat(live-config)**: apply `bots.json` edits to the running bot without restart (issue #27) — the 1min bots-config poll only fingerprinted the market-adapter price feed (`name:gridPrice`), so window-count, reserve, fund, and weight edits sat unapplied until restart with nothing saying so, and monolithic (`unlock`) bots skipped the poll entirely via the wrapper-owned early return. New `checkAndApplyBotConfigChanges` runs at the top of the periodic adapter-sync path on every tick (startup, 1min poll, 4h chain fetch, wrapper-owned included) with a single shared snapshot read: it fingerprints the full normalized bot entry (key order/comments/whitespace-insensitive) and live-merges the safe allowlist `BOT_LIVE_CONFIG_KEYS` (`activeOrders`, `reserveOrders`, `botFunds`, `weightDistribution`, `min_BTS_value`, `debtPolicy`) into `bot.config` + `manager.config`, each converging through its existing consumer (targeted drift reconcile, `recalculateFunds`, dynamic-weight refresh, fee/acquisition reads). Anything outside the allowlist logs a one-time hint naming `dexbot reset <name>` for grid geometry vs restart for market/account identity and tuning; corrupt/unreadable snapshots preserve the stored fingerprint and never throw. The poll-disabled log now covers the adapter + live-config fallback, and the editor shows the live key list at save (`modules/dexbot_maintenance_runtime.ts`, `modules/runtime_settings.ts`, `modules/dexbot_class.ts` fingerprint fields, `modules/account_bots.ts`, `tests/test_dexbot_maintenance_runtime_market_adapter_watchdog.ts`).
|
|
34
|
+
- **Feat(live-config)**: live-apply `debtPolicy` behind a structural shape gate — the credit runtime reads policy through a live getter, so threshold/toggle/item edits take effect on the next credit maintenance/watchdog cycle with no grid impact; a malformed policy is diverted to the reset/restart hint instead of merged so a bad edit can never poison the running credit cycle, and a successful merge reconciles the runtime (`_setupCreditRuntime`, no-op `loadState` when already loaded) plus the watchdog interval (enable-from-zero creates/loads state and starts it, removal clears the policy and stops it). Drive-by fix: the full-resync path replaces `bot.config` with a new object, which left `CreditRuntime.config` pointing at the stale copy so `reset`-reloaded policies were silently ignored — the shared replace helper now re-points it (same files as above).
|
|
35
|
+
- **Feat(claw)**: agent settings patches touching only live-pickup keys no longer force a full grid-resync trigger — the default auto-trigger fired on `activeOrders`/`botFunds`/`weightDistribution`/`debtPolicy`, so an agent window-count tweak caused a full cancel/replace wave for an edit the running bot absorbs incrementally within a minute; patches touching any non-live trigger key still trigger, explicit `trigger: true/false` still overrides, and `previewBotSettingsUpdate.triggerRequired` follows the same rule (`claw/modules/dexbot_profiles.ts`, `claw/tests/test_dexbot_profiles.ts`).
|
|
36
|
+
- **Refactor(live-config)**: single-source live-config plumbing, stale-code removal — `BOT_LIVE_CONFIG_KEYS` lives in `modules/runtime_settings.ts` (consumed by the maintenance runtime, the editor hint, and the Claw trigger gate); one `cloneJsonValue`, one `diffBotConfigEntries` (replacing `diffLiveBotConfig` + `detectNonLiveBotConfigChanges`), one shared `replaceBotConfigFromEntryPreservingRuntime` for the resync path; dead `_appliedBotLiveSnapshot` field removed and the stale wrapper-owned poll test renamed to match the new behavior (same files as above).
|
|
37
|
+
- **Fix(boundary)**: anchor null-boundary recovery from live fill prices instead of fabricating a rail-top boundary — after GRID-LOAD rejected a poisoned persisted boundary with no safe re-derivation, the committed boundary stayed null while fills were the only remaining boundary mover; the first fills hit recovery with `config.startPrice="pool"` (an unresolved mode string), where every `price >= reference` comparison is false so the split fell through to the rail top (base at the rail edge, ceiling-clamped, then shifted by same-batch crawls), the active window ran off the rail, and ordinal pairing planned hundred-slot "rotations" that guards had to refuse. `deriveTargetBoundary` now anchors from position signals, weakest last: live fill prices (gap-side extreme, midpoint when both sides filled) → numeric config center → forwarded genesis center → bounded rail-center fallback; fill-anchored recovery batches skip the crawl (the anchor already contains the fill info, crawling would double-count; genesis/rail-center anchors still crawl); `calculateIdealBoundary` fails toward rail-center on non-numeric references instead of the rail top (`modules/order/utils/order.ts`, `modules/order/strategy.ts` forwards `genesisStartPrice`, `tests/test_boundary_anchor_recovery.ts` ANCHOR-001..009 including a replay of both incident batches).
|
|
38
|
+
- **Fix(recovery)**: erase the poisoned persisted boundary on unrecoverable GRID-LOAD rejection — `storeMasterGrid` never persists a null boundary, so the rejected value survived every flush and re-armed the identical rejection on every restart; new explicit-only `AccountOrders.clearPersistedBoundary()` runs best-effort when re-derivation fails, so the next boot loads boundary-less and the first fill batch re-anchors live (`modules/account_orders.ts`, `modules/order/grid.ts`).
|
|
39
|
+
- **Fix(cow)**: rail-edge truncation telemetry and structural-resync plumbing for refused plans — warn-only log when the planned window runs off the rail (sell-start past the last slot; no geometry refused, cross-guard/fund-validation/boundary-hold still judge), and the batch executor now honors `needsResync` from refused plans (unrecoverable boundary) by requesting a structural grid resync where bot context exists (`modules/order/manager.ts`, `modules/dexbot_class.ts`).
|
|
40
|
+
- **Fix(guard)**: freeze the last-fill-guard pivot once per batch — per-action refreshes mutated the pivot mid-batch so early actions were judged against a different pivot than later ones; all three guard sites pass through a frozen batch pivot (`skipRefresh`), per-action skip lines demote from warn to debug, batch summary unchanged (`modules/dexbot_cow_runtime.ts`).
|
|
41
|
+
- **Fix(boundary)**: persist uncommitted fill crawls across refused broadcasts and restarts — a processed fill whose derivation never commits (refused broadcast, aborted plan, pre-restart loss) lost its crawl permanently, so startup reconcile refilled the holes same-side instead of rotating (4 consumed buys re-bought at the filled prices after restart). Strategy now records every shift-eligible fill as a pending crawl (slot-level dedupe on push); derivations incorporate owed entries (deduped against the current batch, reserve slots excluded); any accepted non-null commit clears the record; startup applies owed crawls onto the restored boundary (validated placed-order-aware, reserve-aware, clears mark dirty on all paths) before reconcile, and drops them under a null boundary where the absolute fill anchor subsumes all history (`modules/order/strategy.ts`, `modules/order/utils/order.ts` incl. `consumePendingFillCrawls`, `modules/order/manager.ts`, `modules/order/utils/system.ts`, `modules/account_orders.ts` snapshot field + loader, `modules/dexbot_startup_runtime.ts`, `tests/test_pending_fill_crawls.ts` PEND-001..010).
|
|
42
|
+
- **Fix(tests)**: two pre-existing suite failures — restored the `[COW] No actions needed` debug log in the empty-action guard (dropped when the structural-resync block was added, breaking COW-COMMIT-003) and added the nine reserve/crossing exports (`resolveReserveCount`, `selectReserveEdgeSlots`, `resolveReserveFloorIds/CeilIds`, `geometryTypeForSlotIndex`, `isShiftEligibleFill`, `buildCrossingCheckCandidates`, `isCrossingCheckCandidate`, `chainOrderMatchesSlotWithTolerance`, `consumePendingFillCrawls`) to the dynamic-weights ESM mock name list, whose stub predated the reserve API (`modules/dexbot_class.ts`, `tests/test_dexbot_maintenance_runtime_dynamic_weights.ts`). Full suite: 274/274.
|
|
43
|
+
- **Feat(tv)**: opt-in MPA price-feed charts and explicit source selection — market candles and settlement-feed history answer different questions, so the chart shortcut no longer always charts pool/order-book fills: `--feed` charts settlement-price history for MPA pairs (single MPA or MPA/MPA cross via both legs), `--pool` forces LP candles, `--book` forces order-book fills, the default stays pool-first with order-book fallback, and prediction markets are rejected; an explicit `--feed` warns on stale/unknown feed age (flat-chart risk) but still charts, while auto mode never routes to feed. New `market_adapter/inputs/kibana_feed_source.ts` buckets settlement-price publications into OHLC candles in backing-per-MPA units, matching the live feed-price convention (cross pairs forward-fill both legs into one quote series). Chart defaults: `--scale` dropped from `tv` and the exporter (the chart already ships a persisted Log/Linear toggle and always opens on log), range highlight now off by default with `--range` re-enabling it (a stored browser choice still wins) (`scripts/tv.ts`, `analysis/tradingview/*`, `market_adapter/inputs/kibana_feed_source.ts`, READMEs, `tests/test_tv_feed_routing.ts`, `tests/test_kibana_feed_source.ts`).
|
|
44
|
+
- **Feat(tv)**: range-aware candle bucket cache shared by the pool and feed chart paths — repeated chart fetches re-queried every window because each run anchors its range at floored-now, shifting all windows and invalidating the exact time-range cache match, and the price-feed path had no disk cache at all; new `market_adapter/inputs/window_cache.ts` loads sibling chunks once and keeps in-range buckets, queries only missing buckets plus a bounded 48h tail refresh for late-indexed records, prunes leading no-trade gaps and already-queried ranges out of immutable history, extends sub-range queries by one bucket (an inclusive range would otherwise truncate the final bucket into a fake zero-volume candle; output is clamped to the window and fresh data wins by volume), deletes orphan chunks after complete runs (failures never delete), and opts forward-filled cross-rate data out of sub-range fetches (exact reuse still applies). Chunk metas record the ranges actually queried (`meta.queriedRanges`), so gap pruning consults real coverage instead of the file's overall `timeRange`, which over-claimed after sub-range rewrites (legacy files fall back to their `timeRange` claim), and same-filename overwrites keep prior in-window coverage via `priorQueriedInWindow`. The pool fetcher delegates to the shared planner/cache (manifest, retry and merge behavior unchanged, helper export names preserved), the feed fetcher gains a cached sequential path, and both modes print identical per-window progress through one formatter with per-page Kibana chatter and staging/render timing lines removed (`market_adapter/inputs/fetch_lp_data.ts`, `market_adapter/inputs/kibana_feed_source.ts`, `scripts/tv.ts`, `tests/test_window_cache.ts`, `tests/test_fetch_lp_data_logic.ts`).
|
|
45
|
+
- **Feat(reserve)**: anchor the reserve ladder at resolved min/maxPrice bounds (issue #25) — reserve BUYs ranked by raw price alone, so keep-low windows pushed them just above the active window while the minPrice floor zone stayed empty, the opposite of the static dip insurance the ladder is for; both edges now anchor toward their resolved config bound (floor toward minPrice, ceiling toward maxPrice). New `resolveReserveEdgeAnchorPrice(config, side)` resolves numeric and `"Nx"` relative bounds via `resolveConfiguredPriceBound` and returns null when unresolvable so callers keep the previous rank behavior, while new `compareReserveEdge` is the single-source comparator for every reserve pick (finite anchor: in-bound slots first, nearest the anchor first, floor ascending / ceiling descending, stale out-of-bound slots last; null anchor: plain rank); `resolveReserveFloorIds`/`resolveReserveCeilIds`/`selectReserveEdgeSlots` take an optional anchor, and all selection sites (target grid, initial activation, startup reconcile, edge pick, divergence corrections) resolve and pass the per-side anchor. Unresolvable bound, garbage input, or an unresolved `"pool"`/`"book"` startPrice degrades to the rank behavior; the documented limit at the time was that the anchor is the statically resolved config bound rather than the live grid's own rail bound (`modules/order/utils/order.ts`, `modules/order/strategy.ts`, `modules/order/manager.ts`, `modules/order/grid_reconcile_internal.ts`, `modules/order/utils/system.ts`, `tests/test_reserve_orders.ts`).
|
|
46
|
+
- **Feat(reserve)**: live-grid reserve edge anchors, closing the gap the config-bound anchor left open — a config-resolved anchor misses the rail the bot actually trades, because mode strings like `"pool"` are unresolvable, relative multipliers need a reference price, and a reload can flip raw/resolved bounds, which left the floor zone empty unless a manual shelf was placed; new `resolveLiveReserveEdgeAnchorPrice(manager, side)` resolves in explicit tiers (the genesis ladder extreme the loaded grid was built from, then the live in-rail extreme via `resolveGapBand` + `isSlotInRail`, then the config bound, then null for legacy rank). `deriveTargetBoundary` and `reserveEdgeIdSet` take the anchor explicitly, and the strategy resolves both sides once and shares them between placement and the no-crawl fill classification, so the two can never disagree about which slots are reserves. The startup pending-crawl recovery path resolves the same anchors, so a restart cannot rank a stale below-rail slot as a reserve and silently drop a crawl the live run recorded as ordinary market movement (regression-pinned by a restart test whose crawl is provably applied with the live anchor and dropped with the config fallback). All runtime sites switched off the config-bound anchor: target grid, initial activation, startup reconcile, edge pick, divergence corrections, and pending-crawl recovery (`modules/order/utils/order.ts`, `modules/order/strategy.ts`, `modules/order/manager.ts`, `modules/order/grid_reconcile_internal.ts`, `modules/order/utils/system.ts`, `tests/test_reserve_orders.ts`, `tests/test_pending_fill_crawls.ts`).
|
|
47
|
+
- **Refactor(reserve)**: single-source reserve edge ordering — `resolveReserveFloorIds`/`resolveReserveCeilIds` hand-rolled the ordering that `compareReserveEdge` defines, so which slots count as reserves had two spellings and any drift would misclassify fills as no-crawl reserves; both helpers are deleted and `reserveEdgeIdSet` now filters and sorts through `selectReserveEdgeSlots`/`compareReserveEdge`, also keying on the canonical side type so a stale SPREAD placeholder is never ranked as an edge order (parity fuzz over both former resolvers, 864k cases, showed zero price-level divergences). The dynamic-weights ESM mock name list drops the deleted twins and picks up `reserveEdgeIdSet`, `resolveLiveReserveEdgeAnchorPrice`, and `compareReserveEdge` (`modules/order/utils/order.ts`, `modules/order/grid_reconcile_internal.ts`, `tests/test_reserve_orders.ts`, `tests/test_dexbot_maintenance_runtime_dynamic_weights.ts`).
|
|
48
|
+
- **Fix(reserve)**: activate reserves only with their stored size, and hold back the missing reserve share at startup — the reserve picker re-derived per-slot sizes from a different slot list than the strategy (its own SPREAD-inclusive, in-rail derivation), so a startup-placed reserve could be sized by one rule and corrected by another; activation now requires the slot's own stored size to satisfy the minimum, re-types the pick to the activation side, and never derives a size locally. Startup reconcile holds back only the reserve share still missing on-chain (`reserveCount` minus reserves already placed) from its window plan, so an edge pick that is not activatable yet leaves its slot unplanned for the target-grid sizing pipeline instead of the plan parking a middle window slot there that the next cycle would have to rotate out; a live reserve is already part of matched-on-grid and never shrinks the window plan (fresh-grid deficit 5 → 3 window placements with 2 deferred; steady state unchanged at 5; two live reserves with an empty window → the full 3-slot window plan) (`modules/order/grid_reconcile_internal.ts`, `modules/order/grid.ts`, `tests/test_reserve_orders.ts`).
|
|
49
|
+
- **Fix(boundary)**: keep owed fill crawls hold-aware and reload-safe — the pending-crawl ledger records every shift-eligible fill as a relative boundary delta, consumed by a commit that uses the plan's boundary and replayed when a refused broadcast, aborted plan or restart leaves the crawl owed; auditing that contract against the committed-boundary writers found five leaks and one doc drift. A refill hold pins the committed boundary over the plan's target, so the shift those records encode was never applied — the commit clear now requires `boundaryHeld !== true` and routes through `_clearPendingFillCrawls()`, which logs the drop and marks the grid dirty so it reaches disk, with the hold flag hoisted in the COW runtime and passed to both commit sites so the uncertain-broadcast poll path cannot keep a pinned boundary while dropping its records. Reserve ladder orders live outside the boundary contract, so a skipped reserve CREATE must not pin geometry — `collectRefillSlotIds()` is now the single producer of the refill wire, shared by the COW plan path and the divergence fold, and fails open when reserve classification is unavailable. The rotation size-validation skip now records its slot ids like its five sibling skip sites, so the skip set and boundary hold see it. `applyPersistedPendingCrawls()` is shared by startup and recovery, and recovery applies stored crawls before its `persistGrid`, which would otherwise write the empty in-memory array over them and erase the owed movement without applying it. A grid rebuild, a rejected snapshot and `clearGrid` re-anchor the boundary absolutely, so relative deltas from the previous generation are dropped in memory and on disk. Docs: fund-driven sizing with a fill-driven boundary recorded in `modules/README.md` and `docs/COW_INVARIANTS.md` (INV-COW-006/007/008 cover boundary ownership, the refill hold with reserve exclusion, and the owed-crawl lifecycle). Crawls are only retained on paths that provably derived nothing, so a drop cannot strand a slot the plan already moved past; reload paths apply deltas onto the boundary they were recorded against (`modules/order/manager.ts`, `modules/dexbot_cow_runtime.ts`, `modules/order/utils/order.ts`, `modules/order/utils/system.ts`, `modules/dexbot_startup_runtime.ts`, `modules/dexbot_state_recovery.ts`, `modules/order/grid.ts`, `modules/account_orders.ts`, `tests/test_cow_boundary_hold.ts` HOLD-009, `tests/test_pending_fill_crawls.ts` PEND-012..018 incl. the replay-exactly-once generation invariant, `tests/test_reserve_orders.ts` refill-wire cases).
|
|
50
|
+
- **Fix(boundary)**: rail-gate startup placement, fold owed crawls into static rebuild centers, classify all deferred holds as non-blocking — startup placement could diverge from runtime activation, a static rebuild center discarded owed fill-crawl direction, a stale numeric recovery center could pin the boundary to a rail edge, and deferred chain-order holds were classified by exact reason string, letting a new defer reason re-freeze the pipeline. `getInitialOrdersToActivate` now filters window and reserve candidates through the shared `isSlotInRail` geometry (same predicate as `_pickVirtualSlotsToActivate`/`_pickEdgeReserveSlots`, fail-open for unknown geometry), so a stale stored rail can never be placed on the wrong side of the boundary. On a rebuild centered on a static config value (startPrice numeric, or AMA-driven whose live snapshot offsets the center — the gridPrice bounds reference does not make the ladder center fresh), owed fill crawls are folded into the rebuild center (one `incrementPercent` step per net crawl, reserve fills excluded, re-clamped to the post-guard bounds) before `_clearPendingFillCrawls('grid rebuild')`; a live-derived startPrice drops them because the derived price already contains the movement. `deriveTargetBoundary` rejects a Tier-2/Tier-3 reference that falls outside the live rail and falls through to the bounded Tier-4 rail center instead of pinning an edge (Tier-1 fill anchors stay exempt — live market wins). The pending-crawl ledger records nothing under `dryRun` and caps after push at exactly 500, matching the persisted `slice(-500)` cap. A new shared `isNonBlockingUnmatchedOrder` classifies any `*-deferred` reason as a deliberate hold, used at all three blocking sites (`validateCreateTargetSlots`, the COW pre-broadcast gate, snapshot recovery) — `boundary-unknown-deferred` is now correctly non-blocking (transient and re-evaluated), so a new defer reason cannot silently regress into a permanent blocker; hold counts (`unmatchedChainOrders`/`heldChainOrders`/`blockingChainOrders`) surface in `getMetrics`, the shutdown summary, and a deduped periodic `[HOLD]` warning. New `tests/test_hold_and_center_guards.ts` (HOLD-001, CENTER-001..003, PEND-CAP-001, REBUILD-FOLD-001..004 including the static-center fold and the mixed-mode gridPrice cases, RAIL-GATE-001), `tests/test_sync_out_of_grid_defer.ts` extended with OUT-OF-GRID-007 (`modules/order/manager.ts`, `modules/order/grid.ts`, `modules/order/strategy.ts`, `modules/order/utils/order.ts`, `modules/order/utils/validate.ts`, `modules/dexbot_cow_runtime.ts`, `modules/dexbot_state_recovery.ts`, `modules/dexbot_maintenance_runtime.ts`, `modules/dexbot_class.ts`).
|
|
51
|
+
- **Feat(grid)**: bidirectional grid-regeneration trigger (grow + shrink on fund removal) — the 3% available-funds trigger only fired upward, so after an external fund removal the grid stayed over-allocated until the next fill forced a resize. The divergence check now also flags a side when its grid-tracked size exceeds the botFunds-capped allocation by `GRID_REGENERATION_PERCENTAGE`, reusing the existing COW resize path (`modules/order/grid.ts`, `modules/constants.ts`, `modules/dexbot_maintenance_runtime.ts`, `tests/test_grid_logic.ts`, `docs/FUND_MOVEMENT_AND_ACCOUNTING.md`, `docs/GRID_RECALCULATION.md`).
|
|
52
|
+
- **Fix(credit)**: show short offer id behind avail funds in Curr. CR line — Curr. CR lines now append the live offer's short numeric id in grey (e.g. `| 53.76K BTS (123)`), falling back to the offer object id when the ranked id is missing; no suffix when no offer is found (`scripts/analyze-credit.ts`).
|
|
53
|
+
- **Fix(tv)**: namespace TradingView chart prefs per pool/pair — all generated TradingView charts shared one localStorage key, so opening a chart for one pair applied another pair's saved timeframe, indicators, scale, and pair orientation. The prefs key is now v3 namespaced per chart (pool, asset ids, base interval), computed at export time; the uPlot price/volume cursor-sync key is split into its own constant so namespacing prefs cannot break pane sync; asset nodes without id/symbol no longer collapse distinct charts onto `[object Object]` (`analysis/resolve_source.ts`, `analysis/tradingview/*`, `tests/test_tradingview_chart_storage_key.ts`).
|
|
54
|
+
|
|
55
|
+
### 2026-09-09
|
|
56
|
+
|
|
57
|
+
- **Fix(tradingview)**: range band ignores span slider on grid-less charts — pair/pool charts render with `grid: null`, which hid the span slider (`display:none`) and dropped the band into the uncontrollable ±2% width envelope instead of the 1.25x–2.0x slider span; slider now always renders inline (retagged grid → span), the no-grid fallback builds a symmetric AMA/span-AMA×span base (grid config supplies only tilt/guard params, never a price), and per-bar scaling embeds `computeAmaSlopeClipThreshold` verbatim so the band clips raw AMA slope at the adaptive 90th-percentile threshold before `applyAsymmetricBounds` + `applyNarrowingSideGuard` — grid and pool charts share one path, matching the live grid pipeline (`analysis/tradingview/tradingview_uplot_chart_generator.ts`).
|
|
58
|
+
- **Tune(range)**: widen orange range zone to 1.40x, lower TradingView slider floor — `RANGE_QUALITY` ORANGE_MIN / RED_MAX 1.45 → 1.40 (suizidal starts below 1.40x, orange tight zone widens 0.10 → 0.15) with the range legend now built from `RANGE_QUALITY` as single source of truth; TradingView range-span slider floor 1.25x → 1.2x (slider min, tooltip, all server- and client-side clamps, README flag row) (`modules/constants.ts`, `modules/account_bots.ts`, `analysis/tradingview/*`).
|
|
59
|
+
- **Feat(reserve)**: per-side reserve ladder, edge-pinned dip/spike insurance (issue #25) — no way to rest live BUY/SELL orders far outside the active window for crash wicks and fat fingers (widening minPrice relocates the window, activeOrders counts from the rail); new `reserveOrders: { buy, sell }` (default `{0,0}`, 0 disables per side; editor menu 5 Funding prompts both counts, non-negative-integer validation, legacy numeric form migrates to `{ buy: n, sell: 0 }`). Shared helpers in `modules/order/utils/order.ts` (single source): `resolveReserveCount`/`resolveReserveOrders`, `resolveReserveFloorIds`/`resolveReserveCeilIds` (price-rank edge sets, boundary-independent), `selectReserveEdgeSlots` (floor-first / ceiling-last, skips windowed ids); `deriveTargetBoundary` filters reserve-edge fills so reserves never crawl; placement is window + edge union (middle stays VIRTUAL) across `strategy.ts`, `utils/system.ts`, `manager.ts`, and `grid_reconcile_internal.ts` (startup desired split + edge-cancel-last for unmatched orphans and matched excess); fee/count maintenance counts reserves once (`grid.ts`, `grid_reconcile.ts`, `accounting.ts`, `dexbot_maintenance_runtime.ts`, `export.ts`). New `tests/test_reserve_orders.ts` (per-side clamp, floor/ceiling anchors, both-edges no-crawl, window+edge union, off-means-window-only).
|
|
60
|
+
|
|
61
|
+
- **Fix(tradingview)**: invert range band colors to red-above, green-below — the range envelope around AMA painted the upper segment green and the lower segment red; swaps `UP_FILL`/`DOWN_FILL` (plus boundary strokes) to the correct convention (chart cosmetics only, `analysis/tradingview/*`).
|
|
62
|
+
- **Fix(sync)**: defer out-of-grid orphans instead of clamping onto edge slots (issue #24) — `slotIndexForPrice` clamps below/above-grid prices onto slot-0/slot-(N-1), so the genesis Pass-2 path mis-adopted the first sub-grid orphan into the rail slot (overwriting the live orderId and poisoning slot bookkeeping) and queued every further same-zone orphan as a duplicate `cancelOnly`, wrongfully cancelling live correctly-priced orders; new `isChainPriceOutOfGrid` guard defers out-of-range chain orders with reason `out-of-grid-deferred` (no adopt, no cancel) while exact in-rail orphans still adopt (`modules/order/sync_engine.ts`, `modules/order/utils/math.ts`, `tests/test_sync_out_of_grid_defer.ts` OUT-OF-GRID-001..005).
|
|
63
|
+
- **Fix(sync)**: keep out-of-grid holds from freezing creates and refills — holds are permanent by design, so three paths keyed on "any unmatched order" froze around them: `validateCreateTargetSlots` flagged `chain_orphan_collision` on the hold's clamped candidate slot (permanently blocking that rail refill), the COW pre-broadcast gate rejected every CREATE batch (`UNMATCHED_CHAIN_ORDERS`), and snapshot recovery rejected the persisted grid (`full grid reset required`) on every restart while a hold existed; all three now filter `reason !== 'out-of-grid-deferred'` (holds stay visible to crossing guards and sync but block nothing), plus auto-cancel idle wording corrected and live order ids replaced with synthetic `1.7.91xxxx` in tests (`modules/order/utils/validate.ts`, `modules/dexbot_cow_runtime.ts`, `modules/dexbot_state_recovery.ts`, OUT-OF-GRID-006, `tests/test_uncertain_broadcast.ts` UNC-016f which fails pre-fix with `grid inconsistent after reload: 1 unmatched remain`).
|
|
64
|
+
|
|
65
|
+
- **Docs(reserve)**: reserve ladder references across user docs and removal of the stale boundary-sync section — the per-side `reserveOrders` feature had no user-facing documentation outside code comments and the changelog, and the architecture doc still described the fund-driven boundary sync deleted in 1.5.3; the README Bot Options Reference gains a `reserveOrders` row (floor/ceiling, default `{buy: 0, sell: 0}`, editor menu 5 Funding) with the S/B display notation aligned, and GRID_RECONCILE, COW_INVARIANTS, the developer guide glossary (new Reserve term), FUND_MOVEMENT_AND_ACCOUNTING (the active-order count includes reserves for the BTS fee budget), MPA_CREDIT_USAGE, and DEXBOT2_VS_POWER_LAW_CURVE each gain a code-grounded line (counts re-verified against `grid_reconcile.ts`, `getActiveOrdersTotal`, `accounting.ts`, and the maintenance runtime); `docs/architecture.md` drops the obsolete Fund-Driven Boundary Sync section (58 lines) with no remaining references in the README, architecture, reconcile, developer guide, or workflow docs (`README.md`, `docs/*`).
|
|
66
|
+
|
|
5
67
|
## [1.5.3] - 2026-09-09 - Boundary Ownership Hardening, COW Dedup, TradingView Range Highlight, Sync Materialize Fix
|
|
6
68
|
|
|
7
69
|
### 2026-09-07
|
package/README.md
CHANGED
|
@@ -209,9 +209,10 @@ Configuration options from `dexbot bot`, stored in `bots.json` in the profiles d
|
|
|
209
209
|
| **`gridPrice`** | num \| str \| null | Grid reference. Use `"ama"` for the recommended AMA center (`"ama"` picks the pair's default preset; `"ama1"`–`"ama4"` pin fastest to slowest); `null` falls back to `startPrice`; numeric values use that fixed value. |
|
|
210
210
|
| **`incrementPercent`** | number | Geometric step between orders. Default `0.5` = 0.5%. |
|
|
211
211
|
| **`targetSpreadPercent`** | number | Width of the empty spread zone between buy and sell orders. Default `2` = 2%. Profit per completed cycle ≈ `spread - increment - fees`. |
|
|
212
|
-
| **`weightDistribution`** | object | Advanced sizing control per side. Range `-1` to `2`: `-1` = super-valley, `0` = valley, `0.5` = neutral, `1` = mountain (default), `2` = super-mountain. Higher weight = more funds in orders near the market price; lower weight = more funds shifted toward the grid edge. Default `{
|
|
213
|
-
| **`botFunds`** | object | Capital: `{
|
|
214
|
-
| **`activeOrders`** | object | Target active orders per side: `{
|
|
212
|
+
| **`weightDistribution`** | object | Advanced sizing control per side. Range `-1` to `2`: `-1` = super-valley, `0` = valley, `0.5` = neutral, `1` = mountain (default), `2` = super-mountain. Higher weight = more funds in orders near the market price; lower weight = more funds shifted toward the grid edge. Default `{ Sell: 1.0, Buy: 1.0 }`; leave unchanged for normal setup. |
|
|
213
|
+
| **`botFunds`** | object | Capital: `{ Sell: "100%", Buy: 100% }`. Numbers or percentage strings |
|
|
214
|
+
| **`activeOrders`** | object | Target active orders per side: `{ S: 20, B: 20 }` |
|
|
215
|
+
| **`reserveOrders`** | object | Edge-pinned insurance orders resting live outside the active window, to catch fat fingers: `{ S: 0, B: 0 }` |
|
|
215
216
|
|
|
216
217
|
</details>
|
|
217
218
|
|
package/analysis/README.md
CHANGED
|
@@ -310,12 +310,11 @@ node dist/analysis/analyze_trade_heatmap.js \
|
|
|
310
310
|
|
|
311
311
|
Generates a standalone TradingView-style HTML chart with candle OHLC, SMA, AMA, VWMA, and volume panel. See [tradingview/README.md](tradingview/README.md) for full documentation.
|
|
312
312
|
|
|
313
|
-
```bash
|
|
314
313
|
# Recommended one-step: bot, pool, or pair (fetches candles + renders, default 3 months)
|
|
315
314
|
dexbot tv <bot-key>
|
|
316
315
|
dexbot tv 133
|
|
317
316
|
dexbot tv TOKENA/TOKENB
|
|
318
|
-
|
|
317
|
+
dexbot tv BTS/HONEST.USD --feed # opt-in: MPA price-feed history instead of market candles
|
|
319
318
|
# Manual: bot-key (auto-resolves candle file and AMA settings)
|
|
320
319
|
npm run analysis:tradingview -- --source market_adapter --bot-key <bot-key>
|
|
321
320
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { normalizePoolId } from '../../market_adapter/utils/chain.js';
|
|
6
|
-
import { toIntervalLabel } from '../../market_adapter/interval_utils.js';
|
|
6
|
+
import { toIntervalLabel, slugPart } from '../../market_adapter/interval_utils.js';
|
|
7
7
|
import { MARKET_ADAPTER } from '../../modules/constants.js';
|
|
8
8
|
import * as kibanaSource from '../../market_adapter/inputs/kibana_source.js';
|
|
9
9
|
import { PATHS } from '../../modules/paths.js';
|
|
@@ -29,13 +29,6 @@ import { getErrorMessage } from '../../modules/utils/errors.js';
|
|
|
29
29
|
*/
|
|
30
30
|
const DATA_DIR = PATHS.MARKET_ADAPTER.LP_DATA_DIR;
|
|
31
31
|
const HOURS_3Y = 3 * 365 * 24; // 26280
|
|
32
|
-
function slugPart(value: string | null | undefined): string {
|
|
33
|
-
return String(value || '')
|
|
34
|
-
.trim()
|
|
35
|
-
.toLowerCase()
|
|
36
|
-
.replace(/[^a-z0-9]+/g, '_')
|
|
37
|
-
.replace(/^_+|_+$/g, '') || 'unknown';
|
|
38
|
-
}
|
|
39
32
|
function slugPairFolder(symbolA: string, symbolB: string): string {
|
|
40
33
|
return `${slugPart(symbolA)}_${slugPart(symbolB)}`;
|
|
41
34
|
}
|
|
@@ -147,7 +140,6 @@ async function main() {
|
|
|
147
140
|
const candles = await kibanaSource.getLpCandlesForPool(poolId, assetA, assetB, {
|
|
148
141
|
intervalSeconds,
|
|
149
142
|
lookbackHours: lookback,
|
|
150
|
-
consolidateByTimestamp: true,
|
|
151
143
|
apiKey: null,
|
|
152
144
|
timeout: 60000,
|
|
153
145
|
});
|
|
@@ -94,7 +94,12 @@ interface CandidateInfo {
|
|
|
94
94
|
|
|
95
95
|
// Node pool from central node management (failover-capable) instead of a
|
|
96
96
|
// single hardcoded WSS endpoint.
|
|
97
|
-
|
|
97
|
+
// Single retry budget lives in withRetry below (honoring --retries), so the
|
|
98
|
+
// client-level retry is disabled here — otherwise the two budgets would
|
|
99
|
+
// stack (wrapper attempts x client attempts). Same convention as the paged
|
|
100
|
+
// production fetchers, which pass kibanaSearchRetries: 1 and own their
|
|
101
|
+
// page budget.
|
|
102
|
+
const KIBANA_CFG = { ...DEFAULT_CONFIG, timeout: 30000, kibanaSearchRetries: 1 };
|
|
98
103
|
|
|
99
104
|
const ASSET_PRECISION = {
|
|
100
105
|
'1.3.0': 5, // BTS
|
|
@@ -333,13 +338,13 @@ async function run() {
|
|
|
333
338
|
|
|
334
339
|
const [createRes, cancelRes, fillRes, updateRes]: any[] = await Promise.all([
|
|
335
340
|
withRetry(() => kibanaSearch(KIBANA_CFG, buildTopSellerAccountsQuery(lookbackH, 200, opts.minCreates)),
|
|
336
|
-
'top-seller query'),
|
|
341
|
+
'top-seller query', opts.maxRetries),
|
|
337
342
|
withRetry(() => kibanaSearch(KIBANA_CFG, buildTopCancellerAccountsQuery(lookbackH, 200, 5)),
|
|
338
|
-
'top-canceller query'),
|
|
343
|
+
'top-canceller query', opts.maxRetries),
|
|
339
344
|
withRetry(() => kibanaSearch(KIBANA_CFG, buildTopFilledAccountsQuery(lookbackH, 200, 3)),
|
|
340
|
-
'top-fills query'),
|
|
345
|
+
'top-fills query', opts.maxRetries),
|
|
341
346
|
withRetry(() => kibanaSearch(KIBANA_CFG, buildTopUpdaterAccountsQuery(lookbackH, 200, 1)),
|
|
342
|
-
'top-updater query'),
|
|
347
|
+
'top-updater query', opts.maxRetries),
|
|
343
348
|
]);
|
|
344
349
|
|
|
345
350
|
const createBuckets = createRes?.aggregations?.by_account?.buckets ?? [];
|
|
@@ -428,7 +433,8 @@ async function run() {
|
|
|
428
433
|
const priceResults: any[] = await Promise.all(
|
|
429
434
|
slice.map(r => withRetry(
|
|
430
435
|
() => kibanaSearch(KIBANA_CFG, buildOrderPriceQuery(r.id, lookbackH, null, 200)),
|
|
431
|
-
`price-query for ${r.id}
|
|
436
|
+
`price-query for ${r.id}`,
|
|
437
|
+
opts.maxRetries
|
|
432
438
|
))
|
|
433
439
|
);
|
|
434
440
|
|
|
@@ -6,6 +6,7 @@ import { MARKET_ADAPTER } from '../modules/constants.js';
|
|
|
6
6
|
import { createSource } from './price_sources.js';
|
|
7
7
|
import { resolveCandleFile, resolveAmaConfig, resolveAmaKey, loadBotSettings, computeBotKey } from './bot_key_utils.js';
|
|
8
8
|
import { findLatestLpData } from '../market_adapter/utils/data_discovery.js';
|
|
9
|
+
import { loadCandleFile } from './math_utils.js';
|
|
9
10
|
|
|
10
11
|
const INTERVAL_LABEL = MARKET_ADAPTER.RUNTIME_DEFAULTS.intervalLabel;
|
|
11
12
|
|
|
@@ -21,6 +22,9 @@ interface SourceResolution {
|
|
|
21
22
|
botKey?: string;
|
|
22
23
|
amaConfig: { erPeriod: number; fastPeriod: number; slowPeriod: number; erSmoothPeriod: number };
|
|
23
24
|
amaKey: string;
|
|
25
|
+
// Candle-file meta when the source is backed by a JSON file (pool id,
|
|
26
|
+
// asset ids/symbols, intervalSeconds); null for the centers-file fallback.
|
|
27
|
+
meta: any;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
function listAvailableBots(): void {
|
|
@@ -53,6 +57,7 @@ function resolveSource(config: SourceConfig, options: { quiet?: boolean } = {}):
|
|
|
53
57
|
botKey: config.botKey,
|
|
54
58
|
amaConfig: resolveAmaConfig(config.botKey),
|
|
55
59
|
amaKey: resolveAmaKey(config.botKey),
|
|
60
|
+
meta: loadCandleFile(candleFile).meta,
|
|
56
61
|
};
|
|
57
62
|
}
|
|
58
63
|
|
|
@@ -64,6 +69,7 @@ function resolveSource(config: SourceConfig, options: { quiet?: boolean } = {}):
|
|
|
64
69
|
botKey: config.botKey,
|
|
65
70
|
amaConfig: resolveAmaConfig(config.botKey),
|
|
66
71
|
amaKey: resolveAmaKey(config.botKey),
|
|
72
|
+
meta: null,
|
|
67
73
|
};
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -79,10 +85,11 @@ function resolveSource(config: SourceConfig, options: { quiet?: boolean } = {}):
|
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
const source = createSource('json', { filePath: filePath! });
|
|
88
|
+
const meta = loadCandleFile(filePath).meta;
|
|
82
89
|
if (config.botKey) {
|
|
83
|
-
return { source, botKey: config.botKey, amaConfig: resolveAmaConfig(config.botKey), amaKey: resolveAmaKey(config.botKey) };
|
|
90
|
+
return { source, botKey: config.botKey, amaConfig: resolveAmaConfig(config.botKey), amaKey: resolveAmaKey(config.botKey), meta };
|
|
84
91
|
}
|
|
85
|
-
return { source, amaConfig: resolveAmaConfig(''), amaKey: 'AMA3' };
|
|
92
|
+
return { source, amaConfig: resolveAmaConfig(''), amaKey: 'AMA3', meta };
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
throw new Error(`[Source] Unknown source type: ${config.type}. Use 'market_adapter' or 'json'.`);
|
|
@@ -4,7 +4,7 @@ This exporter generates a standalone HTML chart in the `analysis/charts/` folder
|
|
|
4
4
|
|
|
5
5
|
## Recommended: `dexbot tv` (One Step)
|
|
6
6
|
|
|
7
|
-
`dexbot tv` fetches the candles itself (chunked Kibana scans
|
|
7
|
+
`dexbot tv` fetches the candles itself (chunked Kibana scans: pools with order-book fallback for pairs) and then renders through this exporter — no manual fetch/export steps needed:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
# Bot chart with AMA overlay (bot key from profiles/bots.json, default: 3 months)
|
|
@@ -17,10 +17,14 @@ dexbot tv 1.19.133
|
|
|
17
17
|
# Any pair (pool-first, order-book fallback)
|
|
18
18
|
dexbot tv TOKENA/TOKENB
|
|
19
19
|
|
|
20
|
+
# MPA price-feed history instead of market candles (opt-in; MPA/MPA pairs
|
|
21
|
+
# cross both feeds into one quote, e.g. HONEST.USD/HONEST.EUR)
|
|
22
|
+
dexbot tv BTS/HONEST.USD --feed
|
|
23
|
+
|
|
20
24
|
# Options
|
|
21
25
|
dexbot tv <bot> --month 6 # months of 1h candles (default: 3)
|
|
22
26
|
dexbot tv <bot> --chart analysis/charts/custom.html
|
|
23
|
-
dexbot tv
|
|
27
|
+
dexbot tv BTS/HONEST.USD --book # override: order-book fills instead of the pool
|
|
24
28
|
```
|
|
25
29
|
|
|
26
30
|
Output: `analysis/charts/tv_<bot|pool_<id>|<a>_<b>>_1h_<N>m.html`, with a clickable `file://` link printed on completion. Unknown targets fail fast with the list of known bot keys.
|
|
@@ -33,10 +37,15 @@ The sections below cover manual usage (explicit candle files, direct runner flag
|
|
|
33
37
|
- Candle timeframe buttons: `1h`, `4h`, `1d`, `1w`
|
|
34
38
|
- Pair-orientation switcher for `A/B` and `B/A`
|
|
35
39
|
- SMA overlay
|
|
36
|
-
-
|
|
40
|
+
- AMA preset buttons `1–4` (one-click AMA1–4, active preset highlighted); numeric inputs kept
|
|
41
|
+
- Bot-grid range highlight, off by default (the bot's min/max around AMA with live asymmetric tilt; red above AMA, green below)
|
|
37
42
|
- Range-scale switch: fit the price axis to the range band
|
|
38
43
|
- VWMA overlay
|
|
39
|
-
-
|
|
44
|
+
- Order overlay for bot charts (active grid buys/sells as dashed levels, reserve line at the lowest grid buy, ceiling line at the highest grid sell, spread label; pair-aware, toggle in-chart)
|
|
45
|
+
- Market panel (top-right): `SELL` / `Market` / `BUY` rows with distance-to-market %
|
|
46
|
+
- Range panel (bottom-right, smaller type): visible-window candle High/Low (red/green, mirroring the SELL/BUY badge)
|
|
47
|
+
- Volume badge (bottom-right of the volume chart, same small type): visible-window max volume, always with the currency suffix (`1.2M BTS`); click the badge, the legend `Vol` value, or the toolbar unit button to switch base/quote units (quote ≈ base × close, persisted per chart like the AMA settings; feed charts show publish counts and disable the switch)
|
|
48
|
+
- Bottom volume panel with `Volume` toggle and per-bar hover tooltip
|
|
40
49
|
- Crosshair legend with current candle values
|
|
41
50
|
|
|
42
51
|
## Quick Start
|
|
@@ -154,7 +163,7 @@ market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
|
|
|
154
163
|
|
|
155
164
|
| Flag | Description |
|
|
156
165
|
|------|-------------|
|
|
157
|
-
| `--bot <
|
|
166
|
+
| `--bot <bot>` | Bot name from `profiles/bots.json` (auto-resolves pool) |
|
|
158
167
|
| `--pool <id>` | Manual mode, no blockchain needed (requires `--precA/--precB`) |
|
|
159
168
|
| `--interval <1m\|5m\|15m\|30m\|1h\|2h\|4h\|6h\|12h\|1d\|1w>` | Candle bucket size (bare numbers = seconds, e.g. `1800` = 30m) |
|
|
160
169
|
| `--lookback <N>h` | Hours back from now |
|
|
@@ -172,19 +181,24 @@ market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
|
|
|
172
181
|
| `--bot-key <key>` | Bot key for `market_adapter` source | — |
|
|
173
182
|
| `--chart <path>` | Output HTML file | `analysis/charts/tradingview_chart.html` |
|
|
174
183
|
| `--title <text>` | Chart title | auto-generated from meta |
|
|
184
|
+
| `--price-scale <log\|linear>` | Price-axis scale | `log` |
|
|
175
185
|
| `--sma-period <n>` | SMA period | `500` |
|
|
176
186
|
| `--ama-er-period <n>` | AMA ER period | `781` |
|
|
177
187
|
| `--ama-fast-period <n>` | AMA fast period | `5.2` |
|
|
178
188
|
| `--ama-slow-period <n>` | AMA slow period | `83.6` |
|
|
179
|
-
| `--price-scale <log\|linear>` | Price axis scale | `log` |
|
|
180
|
-
| `--scale <log\|linear>` | Alias for `--price-scale` | `log` |
|
|
181
189
|
| `--vwap-bars <n>` | Rolling VWMA window | `500` |
|
|
182
190
|
| `--no-sma` | Disable SMA | — |
|
|
183
191
|
| `--no-ama` | Disable AMA | — |
|
|
184
192
|
| `--no-vwap` | Disable VWMA | — |
|
|
193
|
+
| `--range` | Enable range highlight (off by default; toggle in-chart) | off |
|
|
185
194
|
| `--no-range` | Disable range highlight | — |
|
|
186
195
|
| `--range-scale` | Range Scaling: size the band by AMA slope like the grid build + fit price axis to it | — |
|
|
187
|
-
| `--range-span <mult>` | x-range around AMA, 1.
|
|
196
|
+
| `--range-span <mult>` | x-range around AMA, 1.2–2 (default: bot grid setting) | bot grid |
|
|
197
|
+
| `--orders-file <path>` | Order-grid JSON override for the overlay (default: `profiles/orders/<botKey>.json`) | bot orders |
|
|
198
|
+
| `--no-orders` | Disable the order overlay (levels, reserve/ceiling lines, spread label) | — |
|
|
199
|
+
| `--update-marker-ts <sec>` | Draw an "updated from here" line at the given unix timestamp | — |
|
|
200
|
+
| `--update-marker-bars <n>` | Bar count shown in the update-marker tag (e.g. `(+12)`) | — |
|
|
201
|
+
| `--no-update-marker` | Suppress the update marker even when the candle file has stamped meta | — |
|
|
188
202
|
| `--quiet` | Suppress progress logs | — |
|
|
189
203
|
|
|
190
204
|
## Notes
|
|
@@ -196,9 +210,13 @@ market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
|
|
|
196
210
|
- VWMA is disabled by default.
|
|
197
211
|
- AMA is auto-enabled when the bot has `gridPrice: "ama"` (or ama1-4), otherwise disabled by default.
|
|
198
212
|
- The AMA controls start with the bot-specific AMA, then pair-specific entry from `profiles/market_profiles.json` when available, falling back to AMA3 values from `modules/constants.ts`.
|
|
199
|
-
- The AMA `
|
|
200
|
-
- The pair switcher inverts the candles client-side, so you can inspect both `A/B` and `B/A` views from one export.
|
|
201
|
-
-
|
|
213
|
+
- The AMA preset buttons `1–4` select the AMA1–4 defaults (active preset highlighted); numeric inputs remain for fine-tuning.
|
|
214
|
+
- The pair switcher inverts the candles client-side, so you can inspect both `A/B` and `B/A` views from one export. The order overlay inverts with it (same levels in display units, side colors preserved).
|
|
215
|
+
- The order overlay resolves from `profiles/orders/<botKey>.json` (same files `scripts/analyze-orders.ts` reads): active/partial grid orders only. Pool/pair charts without a bot key render without it, silently. `--no-orders` removes the whole overlay (levels, reserve/ceiling lines, spread label); the in-chart `Orders` checkbox does the same when orders are present. Grid bounds span the full grid, never calculated: the reserve line sits on the lowest grid buy, the ceiling line on the highest grid sell — live (active/partial) and planned (virtual) slots alike; a missing side hides its line instead of drawing an invented level. Bounds render even when no live levels exist yet (levels, spread, and panel BUY/SELL rows need live orders).
|
|
216
|
+
- The update marker falls back to `prevUpdateLastCandleSec` / `prevUpdateNewBars` stamped in the candle-file `meta` when the flags are absent; those fields are written by external incremental-fetch tooling, not by anything in this repo.
|
|
217
|
+
- `Ctrl+0` (or `Cmd+0`) resets the time-axis zoom to the full dataset.
|
|
218
|
+
- Mouse: drag the candles to pan time + price (price drag sets a manual range); wheel zooms time, except over the price axis where it zooms price. Shift+wheel zooms price anywhere over the price pane (cursor-anchored). Dragging the price-axis gutter scales price, dragging the time-axis gutter scales the timeframe; double-click the price axis to return to autofit. While the price range is manual, timeframe moves no longer refit it.
|
|
219
|
+
- Indicator, timeframe, scale, and overlay-visibility changes are persisted in browser `localStorage` per pool/pair chart (`dexbot2-tradingview-uplot-v3:<pool>:<A>_<B>:<baseSecs|base>`); cursor sync between the price/volume panes uses a separate constant key.
|
|
202
220
|
- The price axis defaults to log base `10`, with a toolbar switch for `Log` / `Linear`.
|
|
203
221
|
- If you regenerate the HTML and then open it later, no CDN access is needed — `uPlot` is loaded from the vendored local copy at `analysis/uplot/`.
|
|
204
222
|
|
|
@@ -38,6 +38,11 @@ function parseArgs() {
|
|
|
38
38
|
rangeEnabled: boolean;
|
|
39
39
|
rangeScaleEnabled: boolean;
|
|
40
40
|
rangeSpan: number | undefined;
|
|
41
|
+
ordersFile: string | null;
|
|
42
|
+
noOrders: boolean;
|
|
43
|
+
noUpdateMarker: boolean;
|
|
44
|
+
updateMarkerTsSec: number | null;
|
|
45
|
+
updateMarkerNewBars: number | null;
|
|
41
46
|
quiet: boolean;
|
|
42
47
|
listBots: boolean;
|
|
43
48
|
} = {
|
|
@@ -53,9 +58,14 @@ function parseArgs() {
|
|
|
53
58
|
amaEnabled: true,
|
|
54
59
|
vwapEnabled: false,
|
|
55
60
|
vwapBars: 500,
|
|
56
|
-
rangeEnabled:
|
|
61
|
+
rangeEnabled: false,
|
|
57
62
|
rangeScaleEnabled: false,
|
|
58
63
|
rangeSpan: undefined,
|
|
64
|
+
ordersFile: null,
|
|
65
|
+
noOrders: false,
|
|
66
|
+
noUpdateMarker: false,
|
|
67
|
+
updateMarkerTsSec: null,
|
|
68
|
+
updateMarkerNewBars: null,
|
|
59
69
|
quiet: false,
|
|
60
70
|
listBots: false,
|
|
61
71
|
};
|
|
@@ -78,9 +88,16 @@ function parseArgs() {
|
|
|
78
88
|
else if (arg === '--no-sma') config.smaEnabled = false;
|
|
79
89
|
else if (arg === '--no-ama') config.amaEnabled = false;
|
|
80
90
|
else if (arg === '--no-vwap') config.vwapEnabled = false;
|
|
91
|
+
else if (arg === '--vwap-bars') config.vwapBars = Math.max(5, parseInt(args[++i], 10) || 500);
|
|
92
|
+
else if (arg === '--range') config.rangeEnabled = true;
|
|
81
93
|
else if (arg === '--no-range') config.rangeEnabled = false;
|
|
82
94
|
else if (arg === '--range-scale') config.rangeScaleEnabled = true;
|
|
83
95
|
else if (arg === '--range-span') config.rangeSpan = parseFloat(args[++i]);
|
|
96
|
+
else if (arg === '--orders-file') config.ordersFile = String(args[++i] || '');
|
|
97
|
+
else if (arg === '--no-orders') config.noOrders = true;
|
|
98
|
+
else if (arg === '--no-update-marker') config.noUpdateMarker = true;
|
|
99
|
+
else if (arg === '--update-marker-ts') config.updateMarkerTsSec = Math.max(0, parseInt(args[++i], 10) || 0) || null;
|
|
100
|
+
else if (arg === '--update-marker-bars') config.updateMarkerNewBars = Math.max(0, parseInt(args[++i], 10) || 0) || null;
|
|
84
101
|
else if (arg === '--list-bots') config.listBots = true;
|
|
85
102
|
else if (arg === '--quiet') config.quiet = true;
|
|
86
103
|
}
|
|
@@ -93,6 +110,61 @@ function loadJsonMeta(filePath: any) {
|
|
|
93
110
|
return loadCandleFile(filePath);
|
|
94
111
|
}
|
|
95
112
|
|
|
113
|
+
// ── Order overlay: live grid levels (buys/sells) + full-grid bounds ──
|
|
114
|
+
// Canonical source is profiles/orders/<botKey>.json (same files
|
|
115
|
+
// scripts/analyze-orders.ts reads); --orders-file overrides, --no-orders
|
|
116
|
+
// disables. Pool/pair charts without a bot key render without overlay,
|
|
117
|
+
// silently — no hardcoded personal paths.
|
|
118
|
+
function resolveOrdersFile(botKey: string | null | undefined, explicit: string | null | undefined, disabled: boolean): string | null {
|
|
119
|
+
if (disabled) return null;
|
|
120
|
+
if (explicit) {
|
|
121
|
+
try { if (fs.existsSync(explicit)) return explicit; } catch { /* fall through to warning */ }
|
|
122
|
+
console.warn(`[TradingView] --orders-file not found: ${explicit} (rendering without order overlay)`);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (!botKey) return null;
|
|
126
|
+
try {
|
|
127
|
+
const ordersDir = (PATHS as any).ORDERS_DIR || path.join(path.dirname(PATHS.PROFILES.BOTS_JSON), 'orders');
|
|
128
|
+
const direct = path.join(ordersDir, `${botKey}.json`);
|
|
129
|
+
if (fs.existsSync(direct)) return direct;
|
|
130
|
+
} catch { /* silent when absent */ }
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Single-pass read of the order grid, like the runtime sees it: live
|
|
135
|
+
// (active/partial) levels for the overlay plus full-grid bounds (live +
|
|
136
|
+
// planned/virtual slots). Virtual slots carry the same slot geometry
|
|
137
|
+
// without an on-chain order, so excluding them would shrink the shown
|
|
138
|
+
// grid to the live extremes. Spread-type slots are neither buys nor
|
|
139
|
+
// sells and stay out of both.
|
|
140
|
+
function loadOrdersData(filePath: string | null): { buys: number[]; sells: number[]; low: number | null; high: number | null } {
|
|
141
|
+
if (!filePath) return { buys: [], sells: [], low: null, high: null };
|
|
142
|
+
try {
|
|
143
|
+
const od = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
144
|
+
const grid = Array.isArray(od?.grid) ? od.grid : (Array.isArray(od?.orders) ? od.orders : []);
|
|
145
|
+
const buys: number[] = [];
|
|
146
|
+
const sells: number[] = [];
|
|
147
|
+
let low: number | null = null;
|
|
148
|
+
let high: number | null = null;
|
|
149
|
+
for (const s of grid) {
|
|
150
|
+
const price = Number((s as any)?.price);
|
|
151
|
+
if (!Number.isFinite(price) || price <= 0) continue;
|
|
152
|
+
const st = (s as any)?.state;
|
|
153
|
+
if (st !== 'active' && st !== 'partial' && st !== 'virtual') continue;
|
|
154
|
+
if ((s as any)?.type === 'buy') {
|
|
155
|
+
if (low == null || price < low) low = price;
|
|
156
|
+
if (st !== 'virtual') buys.push(price);
|
|
157
|
+
} else if ((s as any)?.type === 'sell') {
|
|
158
|
+
if (high == null || price > high) high = price;
|
|
159
|
+
if (st !== 'virtual') sells.push(price);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
buys.sort((a, b) => a - b);
|
|
163
|
+
sells.sort((a, b) => a - b);
|
|
164
|
+
return { buys, sells, low, high };
|
|
165
|
+
} catch { return { buys: [], sells: [], low: null, high: null }; }
|
|
166
|
+
}
|
|
167
|
+
|
|
96
168
|
function inferTitle(meta: any, fallback: string) {
|
|
97
169
|
const pool = meta?.pool ? `Pool ${String(meta.pool).replace(/^1\.19\./, '')}` : null;
|
|
98
170
|
const a = meta?.assetA?.symbol || meta?.assetA?.id || null;
|
|
@@ -112,7 +184,7 @@ async function main() {
|
|
|
112
184
|
return;
|
|
113
185
|
}
|
|
114
186
|
|
|
115
|
-
const { source, botKey, amaConfig } = resolveSource({ ...config.source.config, type: config.source.type }, { quiet: config.quiet });
|
|
187
|
+
const { source, botKey, amaConfig, meta: sourceMeta } = resolveSource({ ...config.source.config, type: config.source.type }, { quiet: config.quiet });
|
|
116
188
|
if (!config.quiet) console.log(`[TradingView] Loading candles from ${source.name}...`);
|
|
117
189
|
|
|
118
190
|
const candles = await source.fetchCandles();
|
|
@@ -124,7 +196,9 @@ async function main() {
|
|
|
124
196
|
const filePath = config.source.config.filePath;
|
|
125
197
|
const rawJson = isJsonSource ? loadJsonMeta(filePath) : { meta: null, candles: null };
|
|
126
198
|
const botMeta = botKey ? loadBotMeta(botKey) : null;
|
|
127
|
-
|
|
199
|
+
// Prefer meta from the actual candle file (has pool + asset ids) over
|
|
200
|
+
// the bots.json fallback, which only knows the asset symbols.
|
|
201
|
+
const jsonMeta = rawJson.meta || sourceMeta || (botMeta ? {
|
|
128
202
|
assetA: { symbol: botMeta.assetA },
|
|
129
203
|
assetB: { symbol: botMeta.assetB },
|
|
130
204
|
intervalSeconds: 3600,
|
|
@@ -147,6 +221,13 @@ async function main() {
|
|
|
147
221
|
? Number(botMeta.asymmetricBounds.minScaleSlots)
|
|
148
222
|
: null,
|
|
149
223
|
} : null;
|
|
224
|
+
// Order overlay (canonical profiles/orders/<botKey>.json; silent when absent)
|
|
225
|
+
const ordersFile = resolveOrdersFile(botKey, config.ordersFile, config.noOrders);
|
|
226
|
+
const ordersData = loadOrdersData(ordersFile);
|
|
227
|
+
const orderBuys = ordersData.buys;
|
|
228
|
+
const orderSells = ordersData.sells;
|
|
229
|
+
const gridBounds = { low: ordersData.low, high: ordersData.high };
|
|
230
|
+
if (!config.quiet && ordersFile) console.log(`[TradingView] Order overlay: ${orderBuys.length} buys + ${orderSells.length} sells from ${ordersFile}`);
|
|
150
231
|
const html = generateHTML({
|
|
151
232
|
candles,
|
|
152
233
|
meta: jsonMeta || {
|
|
@@ -170,6 +251,14 @@ async function main() {
|
|
|
170
251
|
priceScale: config.priceScale === 'linear' ? 'linear' : 'log',
|
|
171
252
|
defaultTimeframe: '1h',
|
|
172
253
|
marketAdapter: MARKET_ADAPTER,
|
|
254
|
+
orders: { buys: orderBuys, sells: orderSells },
|
|
255
|
+
gridBounds,
|
|
256
|
+
// Update marker ("updated from here" line): explicit CLI flags win,
|
|
257
|
+
// otherwise fall back to stamped data-file meta when present.
|
|
258
|
+
updateMarkerTsSec: config.noUpdateMarker ? null : (config.updateMarkerTsSec
|
|
259
|
+
?? (Number((jsonMeta as any)?.prevUpdateLastCandleSec) > 0 ? Number((jsonMeta as any).prevUpdateLastCandleSec) : null)),
|
|
260
|
+
updateMarkerNewBars: config.noUpdateMarker ? null : (config.updateMarkerNewBars
|
|
261
|
+
?? (Number((jsonMeta as any)?.prevUpdateNewBars) || null)),
|
|
173
262
|
}, title);
|
|
174
263
|
|
|
175
264
|
writeChartFile(config.chartFile, html);
|