dexbot 1.4.24 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +7 -2
  3. package/analysis/README.md +61 -5
  4. package/analysis/ama_fitting/package.json +1 -1
  5. package/analysis/analyze_derivatives.ts +1 -1
  6. package/analysis/bot_key_utils.ts +272 -3
  7. package/analysis/bot_usage/discover_bot_accounts.ts +64 -9
  8. package/analysis/bot_usage/kibana_bot_queries.ts +40 -1
  9. package/analysis/chart_utils.ts +6 -3
  10. package/analysis/grid_correction_check.ts +932 -0
  11. package/analysis/resolve_bot_accounts.ts +199 -0
  12. package/analysis/trade_profitability.ts +36 -8
  13. package/analysis/tradingview/README.md +25 -0
  14. package/analysis/tradingview/analyze_tradingview.ts +2 -2
  15. package/analysis/trend_detection/package.json +1 -1
  16. package/claw/modules/claw_launcher.ts +5 -4
  17. package/claw/modules/credit_runtime_adapter.ts +2 -1
  18. package/claw/modules/dexbot_profiles.ts +4 -3
  19. package/claw/package.json +1 -1
  20. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  21. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  22. package/claw/tests/test_claw_mcp_transport.ts +2 -2
  23. package/dist/analysis/analyze_derivatives.js +1 -1
  24. package/dist/analysis/bot_key_utils.d.ts +28 -1
  25. package/dist/analysis/bot_key_utils.d.ts.map +1 -1
  26. package/dist/analysis/bot_key_utils.js +385 -3
  27. package/dist/analysis/bot_key_utils.js.map +1 -1
  28. package/dist/analysis/bot_usage/discover_bot_accounts.js +60 -10
  29. package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
  30. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +45 -1
  31. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
  32. package/dist/analysis/bot_usage/kibana_bot_queries.js +39 -1
  33. package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
  34. package/dist/analysis/chart_utils.d.ts +2 -1
  35. package/dist/analysis/chart_utils.d.ts.map +1 -1
  36. package/dist/analysis/chart_utils.js +4 -1
  37. package/dist/analysis/chart_utils.js.map +1 -1
  38. package/dist/analysis/grid_correction_check.d.ts +90 -0
  39. package/dist/analysis/grid_correction_check.d.ts.map +1 -0
  40. package/dist/analysis/grid_correction_check.js +901 -0
  41. package/dist/analysis/grid_correction_check.js.map +1 -0
  42. package/dist/analysis/resolve_bot_accounts.d.ts +3 -0
  43. package/dist/analysis/resolve_bot_accounts.d.ts.map +1 -0
  44. package/dist/analysis/resolve_bot_accounts.js +216 -0
  45. package/dist/analysis/resolve_bot_accounts.js.map +1 -0
  46. package/dist/analysis/trade_profitability.d.ts.map +1 -1
  47. package/dist/analysis/trade_profitability.js +40 -8
  48. package/dist/analysis/trade_profitability.js.map +1 -1
  49. package/dist/analysis/tradingview/analyze_tradingview.js +2 -2
  50. package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
  51. package/dist/bot.d.ts.map +1 -1
  52. package/dist/bot.js +2 -1
  53. package/dist/bot.js.map +1 -1
  54. package/dist/dexbot.d.ts.map +1 -1
  55. package/dist/dexbot.js +134 -22
  56. package/dist/dexbot.js.map +1 -1
  57. package/dist/market_adapter/ama_signal_runner.js +2 -2
  58. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +2 -1
  59. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -1
  60. package/dist/market_adapter/inputs/fetch_lp_data.d.ts +4 -1
  61. package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
  62. package/dist/market_adapter/inputs/fetch_lp_data.js +3 -2
  63. package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
  64. package/dist/modules/account_bots.d.ts +42 -1
  65. package/dist/modules/account_bots.d.ts.map +1 -1
  66. package/dist/modules/account_bots.js +221 -22
  67. package/dist/modules/account_bots.js.map +1 -1
  68. package/dist/modules/account_orders.d.ts +7 -10
  69. package/dist/modules/account_orders.d.ts.map +1 -1
  70. package/dist/modules/account_orders.js +26 -2
  71. package/dist/modules/account_orders.js.map +1 -1
  72. package/dist/modules/bitshares_client.d.ts +4 -1
  73. package/dist/modules/bitshares_client.d.ts.map +1 -1
  74. package/dist/modules/bitshares_client.js +12 -4
  75. package/dist/modules/bitshares_client.js.map +1 -1
  76. package/dist/modules/bot_settings.d.ts.map +1 -1
  77. package/dist/modules/bot_settings.js +2 -1
  78. package/dist/modules/bot_settings.js.map +1 -1
  79. package/dist/modules/chain_orders.d.ts.map +1 -1
  80. package/dist/modules/chain_orders.js +21 -2
  81. package/dist/modules/chain_orders.js.map +1 -1
  82. package/dist/modules/cli_colors.d.ts +39 -0
  83. package/dist/modules/cli_colors.d.ts.map +1 -0
  84. package/dist/modules/cli_colors.js +42 -0
  85. package/dist/modules/cli_colors.js.map +1 -0
  86. package/dist/modules/config.d.ts.map +1 -1
  87. package/dist/modules/config.js +27 -0
  88. package/dist/modules/config.js.map +1 -1
  89. package/dist/modules/constants.d.ts +14 -1
  90. package/dist/modules/constants.d.ts.map +1 -1
  91. package/dist/modules/constants.js +66 -11
  92. package/dist/modules/constants.js.map +1 -1
  93. package/dist/modules/credit_pricing.d.ts +61 -0
  94. package/dist/modules/credit_pricing.d.ts.map +1 -0
  95. package/dist/modules/credit_pricing.js +237 -0
  96. package/dist/modules/credit_pricing.js.map +1 -0
  97. package/dist/modules/credit_runtime.d.ts +1 -0
  98. package/dist/modules/credit_runtime.d.ts.map +1 -1
  99. package/dist/modules/credit_runtime.js +49 -107
  100. package/dist/modules/credit_runtime.js.map +1 -1
  101. package/dist/modules/dexbot_class.d.ts +25 -0
  102. package/dist/modules/dexbot_class.d.ts.map +1 -1
  103. package/dist/modules/dexbot_class.js +82 -12
  104. package/dist/modules/dexbot_class.js.map +1 -1
  105. package/dist/modules/dexbot_cow_runtime.d.ts +39 -17
  106. package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
  107. package/dist/modules/dexbot_cow_runtime.js +875 -134
  108. package/dist/modules/dexbot_cow_runtime.js.map +1 -1
  109. package/dist/modules/dexbot_fill_runtime.d.ts +11 -1
  110. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
  111. package/dist/modules/dexbot_fill_runtime.js +108 -1
  112. package/dist/modules/dexbot_fill_runtime.js.map +1 -1
  113. package/dist/modules/dexbot_maintenance_runtime.d.ts +29 -14
  114. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  115. package/dist/modules/dexbot_maintenance_runtime.js +279 -63
  116. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  117. package/dist/modules/dexbot_startup_runtime.d.ts +1 -0
  118. package/dist/modules/dexbot_startup_runtime.d.ts.map +1 -1
  119. package/dist/modules/dexbot_startup_runtime.js +58 -7
  120. package/dist/modules/dexbot_startup_runtime.js.map +1 -1
  121. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  122. package/dist/modules/dexbot_state_recovery.js +55 -3
  123. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  124. package/dist/modules/fund_registry.d.ts +1 -1
  125. package/dist/modules/fund_registry.js +1 -1
  126. package/dist/modules/graceful_shutdown.d.ts +12 -1
  127. package/dist/modules/graceful_shutdown.d.ts.map +1 -1
  128. package/dist/modules/graceful_shutdown.js +39 -1
  129. package/dist/modules/graceful_shutdown.js.map +1 -1
  130. package/dist/modules/key_store.d.ts.map +1 -1
  131. package/dist/modules/key_store.js +38 -1
  132. package/dist/modules/key_store.js.map +1 -1
  133. package/dist/modules/launcher/adapter_requirement.d.ts +38 -0
  134. package/dist/modules/launcher/adapter_requirement.d.ts.map +1 -0
  135. package/dist/modules/launcher/adapter_requirement.js +141 -0
  136. package/dist/modules/launcher/adapter_requirement.js.map +1 -0
  137. package/dist/modules/launcher/bot_supervisor.d.ts +2 -2
  138. package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
  139. package/dist/modules/launcher/bot_supervisor.js +32 -3
  140. package/dist/modules/launcher/bot_supervisor.js.map +1 -1
  141. package/dist/modules/launcher/launch_modes.d.ts.map +1 -1
  142. package/dist/modules/launcher/launch_modes.js +5 -4
  143. package/dist/modules/launcher/launch_modes.js.map +1 -1
  144. package/dist/modules/launcher/monolithic_runtime.d.ts +1 -1
  145. package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
  146. package/dist/modules/launcher/monolithic_runtime.js +12 -8
  147. package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
  148. package/dist/modules/launcher/status_reporting.d.ts +6 -6
  149. package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
  150. package/dist/modules/launcher/status_reporting.js +9 -6
  151. package/dist/modules/launcher/status_reporting.js.map +1 -1
  152. package/dist/modules/order/accounting.d.ts.map +1 -1
  153. package/dist/modules/order/accounting.js +20 -3
  154. package/dist/modules/order/accounting.js.map +1 -1
  155. package/dist/modules/order/export.d.ts +1 -1
  156. package/dist/modules/order/export.js +1 -1
  157. package/dist/modules/order/grid.d.ts +1 -1
  158. package/dist/modules/order/grid.d.ts.map +1 -1
  159. package/dist/modules/order/grid.js +335 -43
  160. package/dist/modules/order/grid.js.map +1 -1
  161. package/dist/modules/order/grid_reconcile.d.ts +4 -2
  162. package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
  163. package/dist/modules/order/grid_reconcile.js +242 -222
  164. package/dist/modules/order/grid_reconcile.js.map +1 -1
  165. package/dist/modules/order/grid_reconcile_internal.d.ts +11 -1
  166. package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
  167. package/dist/modules/order/grid_reconcile_internal.js +132 -6
  168. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  169. package/dist/modules/order/logger.d.ts +23 -1
  170. package/dist/modules/order/logger.d.ts.map +1 -1
  171. package/dist/modules/order/logger.js +60 -17
  172. package/dist/modules/order/logger.js.map +1 -1
  173. package/dist/modules/order/manager.d.ts +68 -14
  174. package/dist/modules/order/manager.d.ts.map +1 -1
  175. package/dist/modules/order/manager.js +281 -52
  176. package/dist/modules/order/manager.js.map +1 -1
  177. package/dist/modules/order/strategy.d.ts.map +1 -1
  178. package/dist/modules/order/strategy.js +25 -8
  179. package/dist/modules/order/strategy.js.map +1 -1
  180. package/dist/modules/order/sync_engine.d.ts.map +1 -1
  181. package/dist/modules/order/sync_engine.js +262 -167
  182. package/dist/modules/order/sync_engine.js.map +1 -1
  183. package/dist/modules/order/utils/math.d.ts +47 -5
  184. package/dist/modules/order/utils/math.d.ts.map +1 -1
  185. package/dist/modules/order/utils/math.js +175 -11
  186. package/dist/modules/order/utils/math.js.map +1 -1
  187. package/dist/modules/order/utils/order.d.ts +16 -15
  188. package/dist/modules/order/utils/order.d.ts.map +1 -1
  189. package/dist/modules/order/utils/order.js +199 -32
  190. package/dist/modules/order/utils/order.js.map +1 -1
  191. package/dist/modules/order/utils/slot.d.ts +8 -0
  192. package/dist/modules/order/utils/slot.d.ts.map +1 -0
  193. package/dist/modules/order/utils/slot.js +16 -0
  194. package/dist/modules/order/utils/slot.js.map +1 -0
  195. package/dist/modules/order/utils/system.d.ts +52 -4
  196. package/dist/modules/order/utils/system.d.ts.map +1 -1
  197. package/dist/modules/order/utils/system.js +109 -16
  198. package/dist/modules/order/utils/system.js.map +1 -1
  199. package/dist/modules/order/utils/validate.d.ts +6 -9
  200. package/dist/modules/order/utils/validate.d.ts.map +1 -1
  201. package/dist/modules/order/utils/validate.js +107 -85
  202. package/dist/modules/order/utils/validate.js.map +1 -1
  203. package/dist/modules/order/working_grid.d.ts +5 -0
  204. package/dist/modules/order/working_grid.d.ts.map +1 -1
  205. package/dist/modules/order/working_grid.js +21 -0
  206. package/dist/modules/order/working_grid.js.map +1 -1
  207. package/dist/modules/paths.d.ts +4 -2
  208. package/dist/modules/paths.d.ts.map +1 -1
  209. package/dist/modules/paths.js +79 -24
  210. package/dist/modules/paths.js.map +1 -1
  211. package/dist/modules/utils/chain_logs.d.ts +11 -0
  212. package/dist/modules/utils/chain_logs.d.ts.map +1 -0
  213. package/dist/modules/utils/chain_logs.js +23 -0
  214. package/dist/modules/utils/chain_logs.js.map +1 -0
  215. package/dist/modules/utils/sanitize_key.d.ts +7 -0
  216. package/dist/modules/utils/sanitize_key.d.ts.map +1 -1
  217. package/dist/modules/utils/sanitize_key.js +15 -0
  218. package/dist/modules/utils/sanitize_key.js.map +1 -1
  219. package/dist/pm2.d.ts +11 -1
  220. package/dist/pm2.d.ts.map +1 -1
  221. package/dist/pm2.js +66 -15
  222. package/dist/pm2.js.map +1 -1
  223. package/dist/scripts/analyze-credit.d.ts +4 -0
  224. package/dist/scripts/analyze-credit.d.ts.map +1 -0
  225. package/dist/scripts/analyze-credit.js +760 -0
  226. package/dist/scripts/analyze-credit.js.map +1 -0
  227. package/dist/scripts/analyze-orders.d.ts +1 -10
  228. package/dist/scripts/analyze-orders.d.ts.map +1 -1
  229. package/dist/scripts/analyze-orders.js +34 -36
  230. package/dist/scripts/analyze-orders.js.map +1 -1
  231. package/dist/scripts/generate_market_adapter_whitelist.d.ts +2 -5
  232. package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +1 -1
  233. package/dist/scripts/generate_market_adapter_whitelist.js +83 -1
  234. package/dist/scripts/generate_market_adapter_whitelist.js.map +1 -1
  235. package/dist/scripts/test-credit-renewal.d.ts.map +1 -1
  236. package/dist/scripts/test-credit-renewal.js +37 -5
  237. package/dist/scripts/test-credit-renewal.js.map +1 -1
  238. package/dist/scripts/tv.d.ts +11 -0
  239. package/dist/scripts/tv.d.ts.map +1 -0
  240. package/dist/scripts/tv.js +357 -0
  241. package/dist/scripts/tv.js.map +1 -0
  242. package/dist/scripts/update.js +77 -10
  243. package/dist/scripts/update.js.map +1 -1
  244. package/dist/unlock.d.ts.map +1 -1
  245. package/dist/unlock.js +20 -8
  246. package/dist/unlock.js.map +1 -1
  247. package/docs/DEXBOT_COMPARISON.md +11 -11
  248. package/docs/EVOLUTION.md +10 -8
  249. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
  250. package/docs/GRID_RECALCULATION.md +6 -0
  251. package/docs/MPA_CREDIT_USAGE.md +3 -1
  252. package/docs/README.md +2 -2
  253. package/docs/WORKFLOW.md +3 -1
  254. package/market_adapter/README.md +10 -0
  255. package/package.json +3 -1
  256. package/scripts/README.md +16 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,73 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.5.0] - 2026-09-05 - Credit Overview & Whitelist-Scoped CR, TradingView Shortcut, Reload Command, Guard & Rotation Hardening
6
+
7
+ ### 2026-09-02
8
+
9
+ - **Fix(editor)**: bold Mountain weight in selector display (`modules/account_bots.ts`).
10
+
11
+ ### 2026-09-03
12
+
13
+ - **Feat(cli)**: add `dexbot credit` live per-bot debt overview and centralize CLI color palette — new `scripts/analyze-credit.ts` (wired as `dexbot credit [<bot>]`) queries live `get_margin_positions` + paginated `get_credit_deals_by_borrower` per preferredAccount and prints summed debt/collateral per asset per bot (lending-asset filtered, shared accounts tagged, chain chatter muted, inclusive-pagination overlap dropped to avoid double-counting); new `modules/cli_colors.ts` (`CLI_COLORS`) is the single source of truth for every ANSI shade repo-wide with no visual change (`scripts/analyze-credit.ts`, `dexbot.ts`, `modules/cli_colors.ts`, `README.md`, `docs/WORKFLOW.md`, `tests/test_analyze_credit.ts`).
14
+
15
+ ### 2026-09-04
16
+
17
+ - **Feat(credit)**: whitelist-scoped CR reporting and shared credit-pricing math — new browser-safe `modules/credit_pricing.ts` holds the canonical math (collateral-map normalization, core/legacy-reversed orientation, offer conversion rates, per-deal and value-weighted average CR, fee helpers) with `credit_runtime.ts` delegating to it (conversion-rate fallback extended offer → pool-derived → universal market price incl. BTS-bridge hops); analyzer per-pair `Curr. CR` and per-bot `Avar. CR` are now scoped to whitelisted `debtPolicy.lending` pairs priced on the current offer, color-coded vs `maxCollateralRatio`, with `Excluded` split by reason and uncapped offer pagination; new `derivePriceViaBridges`/`derivePriceWithBridges` in `modules/order/utils/system.ts` (LP legs bridge only on opt-in) (`modules/credit_pricing.ts`, `modules/credit_runtime.ts`, `modules/order/utils/system.ts`, new `tests/test_credit_pricing.ts`, `tests/test_price_bridges.ts`, `tests/test_credit_conversion_fallback.ts`).
18
+ - **Feat(cli)**: add `dexbot reload` that leaves the credential daemon untouched — mirrors `restart` on every runtime surface (unlock `reload-all` recycles bots + market adapter via SIGUSR2 skipping the daemon re-unlock block, supervisor `reload`/`reload-all`, `pm2 restart` with no `ensureCredentialDaemon`, `reload dexbot-cred` rejected) so bot/adapter recycling no longer costs a password prompt (`unlock.ts`, `modules/launcher/bot_supervisor.ts`, `modules/launcher/launch_modes.ts`, `modules/launcher/monolithic_runtime.ts`, `pm2.ts`, `dexbot.ts`, `README.md`, `docs/WORKFLOW.md`).
19
+ - **Fix(identity)**: enforce case-insensitive bot-name identity across lookups — new `isSameBotName` in `modules/utils/sanitize_key.ts` (sanitized compare with blank-input guard) used by bot_settings, dexbot, unlock, bot, pm2, claw launcher/profiles/credit adapter, maintenance runtime, market-adapter inputs and helper scripts; PM2 stop/delete/restart/reload resolve to the canonical stored name, example pair-derived bot names in comments/help/fixtures replaced with a generic placeholder (published changelog history untouched).
20
+ - **Fix(update)**: run `npm install` only when dependencies actually changed — new `needsDepsInstall(preUpdateHead)` gate in `scripts/update.ts` (install only when node_modules missing/incomplete, manifests changed since pre-update commit, or manifests worktree-dirty; fail-open on git errors) plus a `lockRegenerated` double-install guard (`scripts/update.ts`).
21
+ - **Fix(adapter)**: centralize market-adapter ownership in unlock wrapper on one 1min interval — new `modules/launcher/adapter_requirement.ts` (semantic name:gridPrice fingerprint, corrupt/unreadable bots.json reported not thrown); wrapper exports `DEXBOT_ADAPTER_OWNER=wrapper` so supervised bots skip adapter sync and create no poll timer (wrapper-less modes keep the in-bot fallback); `TIMING.BOTS_CONFIG_POLL_INTERVAL_MS` now 60s shared with the watchdog interval; fixes `''`-fingerprint false-change and corrupt-skip regressions (`modules/launcher/adapter_requirement.ts`, `unlock.ts`, `modules/constants.ts`, `modules/launcher/*`, `modules/dexbot_class.ts`).
22
+ - **Feat(accounts)**: cache bot account IDs in bots.json for offline resolution — byte-preserving in-place `accountId` patch in `analysis/bot_key_utils.ts` (comment/string-aware scanner, re-parse assert, atomic write, full-rewrite fallback) with `persistBotAccountId`/`getStoredBotAccountId`/`findBotKeyByAccountRef`; new `analysis/resolve_bot_accounts.ts` batch backfill CLI (`npm run analysis:resolve-accounts`, `--dry-run`/`--refresh`/`--bot-key`/`--json`); editor stamps IDs via `ensureBotAccountId`, analysis tools and `test-credit-renewal` prefer the cache with `--refresh-account` re-verify; process-wide console floor (`setGlobalConsoleLevel`) silences node-init chatter during supervised lookups (`analysis/bot_key_utils.ts`, `analysis/resolve_bot_accounts.ts`, `modules/account_bots.ts`, `modules/order/logger.ts`, `modules/bitshares_client.ts`, `tests/test_bot_account_id.js`, `tests/test_logger_console_floor.js`).
23
+ - **Fix(guard)**: close stale-pivot race in last-fill guard and harden spread correction — `refreshLastFillPivotFromQueue()` peeks the incoming fill queue before every COW guard check (CREATE, rotation UPDATE, fallback CREATE) and `recordLastFilledPrices()` now runs per chunk so later broadcast chunks gate on the true latest fill; per-action plan-origin stamping keeps spread-correction CREATE bypass while rotation UPDATEs never bypass; `determineOrderSideByFunds()` returns null instead of cross-asset raw-unit compare when priceless with both sides funded; spread check defers on post-gate fills with funds recalc before side decision; batch-level LAST-FILL-GUARD summary + per-batch pivot log lines added (`modules/dexbot_cow_runtime.ts`, `modules/dexbot_class.ts`, `modules/order/grid.ts`, `modules/dexbot_maintenance_runtime.ts`, `modules/order/manager.ts`, new `tests/test_stale_guard_pivot_fixes.js`).
24
+
25
+ ### 2026-09-05
26
+
27
+ - **Feat(cli)**: add `dexbot tv` one-step TradingView chart command — `dexbot tv <bot|pool-id|AssetA/AssetB> [--month N]` pulls 1h candles in chunked 1-month windows and delegates rendering to the TradingView exporter, replacing the manual fetch-then-export two-step; fetcher chunking reused via exported `fetchCandlesSequentially`/`outputPath`/`buildFetchWindowsFromRange`, chain-log mute centralized in `modules/utils/chain_logs.ts`, saved-chart paths print as clickable `file://` links via `toFileUrl` (`scripts/tv.ts`, `dexbot.ts`, `market_adapter/inputs/fetch_lp_data.ts`, `modules/utils/chain_logs.ts`, `analysis/chart_utils.ts`, docs).
28
+ - **Fix(cow)**: clamp COW rotation size to booked remainder for partial surplus — new `clampRotationSizeForPartial` in `modules/order/utils/validate.ts` applied at both rotation-pairing sites (`pairRotations`, `optimizeRebalanceActions`) so rotations sourced from PARTIAL surplus target the booked remainder instead of the hole size, ending the plan→skip→restore loop that stranded holes and left non-contiguous rails (`modules/order/utils/validate.ts`, `modules/order/manager.ts`, `modules/order/utils/system.ts`, `tests/test_cow_master_plan.ts` COW-019).
29
+ - **Fix(credit)**: distinguish Avar/Curr CR lines in `dexbot credit` output — display-only: Avar. CR label orange+bold printed first, Curr. CR label grey+bold with trailing comma, values keep health colors (`scripts/analyze-credit.ts`).
30
+ - **Feat(discovery)**: split HIGH discovery tier into DEXBot2 vs DEXBot1-style by op-77 usage — DEXBot2 broadcasts native `limit_order_update` ops (type 77) for COW re-prices while DEXBot1-style only cancels+recreates, so presence of op 77 in-window assigns flavor; display-only split (score formula unchanged) with Updates column in HIGH tier, summary and `--output-json` carrying flavor/updates (`analysis/bot_usage/kibana_bot_queries.ts`, `analysis/bot_usage/discover_bot_accounts.ts`).
31
+ - **Fix(cli)**: forward `--help`/`-h` to the `credit` and `tv` sub-scripts — the CLI intercepted help flags anywhere in argv and always printed generic help, so `dexbot credit --help` / `dexbot tv --help` never reached the scripts that own their usage text; the dispatcher now resolves the requested command first and lets only scripts with offline help handling (`credit`, `tv`) keep the flag, everything else keeps the generic help (`dexbot.ts`).
32
+
33
+ ## [1.4.25] - 2026-09-02 - Genesis-Frozen Price-Slot Determinism, Self-Trade & Fill-Guard Hardening, Grid Orphan & Recovery Hardening
34
+
35
+ ### 2026-08-29
36
+
37
+ - **Fix(grid)**: restore strict ERROR diagnostics by fixing hidden fund-invariant and precision failures — re-enables strict diagnostics that were masked by NaN fund-invariant comparisons and float precision drift; fixes fund-invariant tolerance fallback, amount precision helpers, and related silent-failure paths so ERROR logs fire correctly (`modules/order/accounting.ts`, `modules/order/utils/math.ts`, `modules/constants.ts`, etc.).
38
+ - **Fix(gap-band)**: enforce gap-band invariant across COW plan, sync, reconcile and recovery (P1–P5) — introduces authoritative gap-band checks that reject placements violating the reserved gap, validates gap-band geometry in COW planning, sync adoption, startup reconcile and recovery persistence so boundaries cannot drift into the forbidden band (`modules/order/grid.ts`, `modules/order/utils/math.ts`, `modules/dexbot_cow_runtime.ts`, `modules/order/sync_engine.ts`, `modules/order/grid_reconcile*.ts`, `modules/dexbot_state_recovery.ts`).
39
+ - **Fix(grid)**: prevent same-slot re-placement and truncation-drop orphans after COW commit — guards against re-creating an order on a slot that was just committed and against truncation-ambiguous reads dropping live orphans; keeps slot provenance across COW commit so placed orders are not re-issued or lost on a truncated read (`modules/dexbot_cow_runtime.ts`, `modules/order/sync_engine.ts`, `modules/order/grid_reconcile_internal.ts`).
40
+ - **Fix(grid)**: make chain evidence authoritative and remove destructive gap-band cancellation — cancels that swept the gap band based on stale local state are removed; chain-observed orders are the sole authority for gap/rail membership and gap-band sweeps are no longer triggered from local-only evidence (`modules/order/grid_reconcile*.ts`, `modules/order/sync_engine.ts`).
41
+ - **Fix(grid)**: heal and prevent sized-orphan phantom orders in spread and COW commit — spread-correction and COW commit paths that left VIRTUAL slots with `size>0` and no `orderId` (sized orphans causing 2.02% vs 3.04% spread divergence) are healed on reload (virtual-size zeroing with `createUncertain` preservation) and prevented at commit (or­phan exclusion from sizing denominator, durable markers) (`modules/order/grid.ts`, `modules/order/manager.ts`, `modules/account_orders.ts`, `modules/dexbot_cow_runtime.ts`).
42
+
43
+ ### 2026-08-30
44
+
45
+ - **Fix(recovery)**: stop recovery re-anchoring and make orphan adoption durable — structural resync no longer re-anchors to the latest AMA center (state repair only); orphan adoption widened with `ORPHAN_ADOPTION_TOLERANCE_MULTIPLIER` (×4) on empty slots, reconcile prefers in-place price updates over pre-emptive cancels, and boundary/slot persistence is hardened (`modules/dexbot_maintenance_runtime.ts`, `modules/order/sync_engine.ts`, `modules/order/grid_reconcile.ts`, `modules/constants.ts`).
46
+ - **Fix(engine)**: harden order engine against orphan-cascade failure modes — closes four unvalidated local-state paths (stale-slot fill price poisoning the anchor, off-market placement, phantom virtualization from empty reads, blind unknown-fill proceed credit) via broadcast orphan gates, anchor outlier rejection, price-sanity clamp, suspect empty-read guard (3 confirms), adoption retry, and durable `createUncertain` markers; placement now wrapped in `startBroadcasting` so fill-driven rebalances cannot duplicate plans (`modules/order/grid_reconcile.ts`, `modules/order/manager.ts`, `modules/dexbot_maintenance_runtime.ts`, `modules/constants.ts`, `modules/order/utils/order.ts`, `modules/dexbot_fill_runtime.ts`, `modules/dexbot_cow_runtime.ts`).
47
+ - **Fix(maintenance)**: make trigger reset reliable — skip idle gate, drain stalled fills, repair infeasible boundaries — trigger-file resets now execute immediately (`skipIdle`), fill queues deferred by `batchInFlight`/`recoverySyncInFlight` are drained on pipeline clear via `_onBroadcastRegionEnd` (wired at both `OrderManager` creation sites), and `NO_FEASIBLE_BOUNDARY` (overlapping BUY/SELL inside gap) escalates to a minimal cancel ladder so placements resume instead of freezing in adoption-only mode (`modules/dexbot_maintenance_runtime.ts`, `modules/order/manager.ts`, `modules/dexbot_startup_runtime.ts`, `modules/order/utils/math.ts`, `modules/order/grid_reconcile*.ts`).
48
+ - **Docs**: consolidate orphan-fix plans and add grid monotonicity gate — four test-branch incident/plan docs merged into `docs/CONSOLIDATED_ORPHAN_FIX_SUMMARY.md` (LANDED/REVERTED/SUPERSEDED annotated) and new `npm run analysis:grid-check` (`analysis/grid_correction_check.ts`) validates consecutive same-direction fills are monotonic (sell rising / buy falling) via Kibana `fill_order` history (`analysis/*`, `docs/*`, `modules/constants.ts`).
49
+
50
+ ### 2026-08-31
51
+
52
+ - **Fix(launcher)**: ensure bot detection within 5min and correct disable/enable active check — new `TIMING.BOTS_CONFIG_POLL_INTERVAL_MS` (5min) decouples fingerprint polling from the heavy 240min chain fetch so new/reenabled bots are observed within 5min; `dexbot.ts` `setBotActiveState` now treats `undefined` as active (`active !== false`) fixing `enable all` churn on implicit-active bots (`modules/constants.ts`, `modules/dexbot_maintenance_runtime.ts`, `modules/dexbot_class.ts`, `modules/dexbot_startup_runtime.ts`, `dexbot.ts`).
53
+ - **Fix(security)**: close shutdown race that wiped live signing tokens — `_shutdownImpl` no longer nulls `botHmacSecret` while deferred SAFE-REBALANCE continuations are pending; tokens are wiped only on process `exit` via `registerExitWipe`; `SOURCE_AUTH_DENIED` retries re-read `daemon-policies.json`, `OrderManager` gains `_shuttingDown`/`_awaitBroadcastIdle` abort, and monolithic mode retries failed bot startups bounded (3×30s) (`modules/dexbot_class.ts`, `modules/graceful_shutdown.ts`, `modules/key_store.ts`, `modules/order/manager.ts`, `modules/dexbot_fill_runtime.ts`, `modules/dexbot_cow_runtime.ts`, `dexbot.ts`).
54
+ - **Fix(grid)**: block self-trading grid placements across all broadcast paths — adds tolerance-widened crossing guard (`findCrossedOrder` in `modules/order/utils/math.ts`, `buildCrossingCandidates` merging master/pending/chain) to COW CREATE/rotation NOT-FOUND fallback and to startup reconcile/relocation/price-correction paths; rotation now uses swept-band exclusion instead of stale slot-type rotation that caused chunked-broadcast self-fills (`modules/dexbot_cow_runtime.ts`, `modules/order/utils/math.ts`, `modules/order/grid_reconcile_internal.ts`, `modules/order/utils/order.ts`, `modules/order/strategy.ts`).
55
+ - **Fix(fill)**: restore main fill queue handling stalled by broadcasting gate — removes `isBroadcastingActive` from `consumeFillQueue` defer gate (already serialized by `_fillProcessingLock`/stale-plan guard; defer had no reschedule and starved the queue during chunked broadcasts) and drains deferred fills when `_batchInFlight`/`_recoverySyncInFlight` clears (`modules/dexbot_fill_runtime.ts`, `modules/dexbot_maintenance_runtime.ts`, `modules/dexbot_class.ts`).
56
+ - **Fix(grid)**: re-price duplicate-price grid stragglers instead of cancelling them — burst re-anchor that left multiple BUYs at the same level (e.g. 902.08089 ×2, 894.01113 ×3) and self-filled is now fixed by re-pricing stragglers to the next unique ladder step away from market (clamped to `[minPrice,maxPrice]`, preserving `orderId`/`size`) via `correctOrderPriceOnChain` UPDATEs instead of virtualize→cancel; adds whole-burst swept-band caching and asymmetric multi-sell-sweep buy exclusion for chunked and unified bursts (`modules/order/strategy.ts`, `modules/dexbot_class.ts`, `tests/test_grid_robustness.ts`).
57
+
58
+ ### 2026-09-01
59
+
60
+ - **Fix(engine)**: revert anchor divergence and boundary-evidence, restore last-fill guard — removes `MarketAnchor`/`BOUNDARY-EVIDENCE`/`BAND-EXCLUSION` paths that drifted from self-trade fills and cancelled stranded orders, restores `deriveTargetBoundary`/`snapRail` grid-dedupe and seeds `LAST-FILL-GUARD` from book at startup/post-fill to block BUY above last filled BUY and SELL below last filled SELL with cross-guard per-type collision checks (`modules/constants.ts`, `modules/order/utils/order.ts`, `modules/order/strategy.ts`, `modules/order/grid_reconcile.ts`, `modules/dexbot_class.ts`, `modules/order/manager.ts`, `modules/dexbot_cow_runtime.ts`).
61
+ - **Fix(grid)**: close duplicate-order incident class from trigger-reset phantom empty read — a trigger reset that observed a single 0-order lagging-node read rebuilt over a live order and queued the wrong duplicate for cancel (books 0.5626 vs chain 0.5626+0.6659). Fix: trigger `GRID-RESYNC` empty reads now require a 2s confirming re-read (`SYNC_EMPTY_READ_CONFIRM_DELAY_MS`), rotation/plan UPDATEs clamp or skip growing PARTIAL slots above booked remaining size, Pass 1 size-consistency tiebreak rebinds a slot to the same-price chain order matching booked size (exact), and `updateOrdersOnChainBatchCOW` drains pending price corrections before planning (`modules/dexbot_maintenance_runtime.ts`, `modules/constants.ts`, `modules/dexbot_cow_runtime.ts`, `modules/order/sync_engine.ts`, `modules/order/manager.ts`).
62
+ - **Fix(g guard)**: last-fill guard pivot ± halfIncrement (BUY < pivot*(1-half), SELL > pivot*(1+half)) — replaces dual-pivot + tolerance with single global pivot (most recent fill) ± half grid increment (e.g. i=0.5% @1000 → BUY<997.5, SELL>1002.5); fixes SELL 1013>1001 after BUY 1001 being wrongly blocked and closes half-step profit hole; spread-correction bypass retained (`modules/order/manager.ts`, `modules/dexbot_cow_runtime.ts`).
63
+
64
+ ### 2026-09-02
65
+
66
+ - **Feat(genesis)**: implement genesis-frozen price-slot determinism (Phase 1–2, log mode) — `slot-N` becomes the single source of truth (`price = genesis.priceLevels[N]`); adds `GridGenesis`/`hashPriceLevels`/`priceLevelsForGenesis`/`priceForSlot`/`slotIndexForPrice` (binary search, lower-wins tie)/`priceSlotEqual` (int equality)/`buildGenesisFromPriceLevels`/`assertSlotPriceInvariant`, canonical `parseSlotIndex` (`modules/order/utils/slot.ts`), `createOrderGrid` dedup+genesis build, `loadGrid` genesis validation/canonical re-sort/type re-assignment via `parseSlotIndex` with legacy migration, `account_orders`/`working_grid`/`system`/`startup`/`recovery` genesis threading, and `GRID_PRICE_SLOT_DETERMINISM_PLAN.md` (`modules/order/utils/math.ts`, `modules/order/grid.ts`, `modules/account_orders.ts`, `modules/order/working_grid.ts`, `modules/order/utils/system.ts`, `modules/dexbot_startup_runtime.ts`, `modules/dexbot_state_recovery.ts`, `modules/order/grid_reconcile.ts`, `docs/*`, `tests/test_grid_price_slot_invariant.ts`).
67
+ - **Feat(genesis)**: complete genesis-frozen price-slot determinism (phases 3–9) — replaces all tolerance-based price matching with `slotId` equality / `priceSlotEqual`; sync engine Pass 2 adopts by nearest slot via `slotIndexForPrice` with `isSlotInRail` gap check and VIRTUAL/free slot bind; COW guards `hasSlotPriceCollision`/`validateCreateTargetSlots` use `priceSlotEqual`; fill/grid/rail paths (`isSlotInRail` fail-closed, `chainOrderMatchesSlot`, `hasDuplicatePriceLevel`, `grid_reconcile` cleanup) unified on genesis; legacy tolerance retained only for no-genesis migration (`modules/order/sync_engine.ts`, `modules/dexbot_cow_runtime.ts`, `modules/order/utils/validate.ts`, `modules/dexbot_fill_runtime.ts`, `modules/order/*`, `tests/*` 12 suites).
68
+ - **Feat(editor)**: add Range quality legend with tiered coloring — `RANGE_QUALITY` thresholds (`GREEN_MIN 2.0` wide / `YELLOW_MIN 1.55` efficient / `ORANGE_MIN 1.45` tight / `RED_MAX 1.45` suicidal `<1.45x`) drive a pre-entry legend and live `colorRangeValueByQuality` in `askNumberOrMultiplier`/`askMaxPrice` and summary Pair display (cyan) (`modules/constants.ts`, `modules/account_bots.ts`).
69
+ - **Feat(whitelist)**: add scoped `--bot` overwrite with validation and warnings — `dexbot white --bot <botKey>` overwrites only the targeted whitelist key (merge preserves unknown flags like `derivativeSignals`) while additive-only behavior is preserved without `--bot`; `parseOptions` now collects `botKeys` (`--bot`/`--bot-key`/`--botKey`, `=` and comma forms, repeatable) with missing/flag-like validation (throws, main prints usage and exits 1), `buildWhitelist` filters to targeted keys and warns when `--bot` target not found or has non-AMA `gridPrice` (`scripts/generate_market_adapter_whitelist.ts`, `dexbot.ts`, `docs/*`, `tests/test_market_adapter_fixes.ts`).
70
+ - **Fix(editor)**: align Range quality wording — legend `≤1.35x: suizidal` → `<1.45x: suizidal` to match `getRangeQuality` (`<1.45x` red, `1.45x–1.55x` orange) and make `RANGE_QUALITY.RED_MAX` authoritative (`modules/constants.ts`, `modules/account_bots.ts`).
71
+
5
72
  ## [1.4.24] - 2026-08-28 - Native Fill Gap Recovery, Eager Coalesced Retry, LP Collateral Offer-First Pricing
6
73
 
7
74
  ### 2026-08-28
package/README.md CHANGED
@@ -178,6 +178,7 @@ Keep the default settings first, and tune these:
178
178
  This writes `market_adapter_whitelist.json` in the profiles directory. New AMA bots get AMA
179
179
  live writes and range scaling. Use `dexbot white --dynamic-weight` for
180
180
  newly generated dynamic-weight entries; existing entries are preserved.
181
+ To overwrite an existing bot: `dexbot white --dynamic-weight --bot <botKey>`.
181
182
 
182
183
  6. **Start DEXBot2** with `dexbot start`.
183
184
 
@@ -234,12 +235,13 @@ Defaults in [`modules/constants.ts`](modules/constants.ts) are overridable at gl
234
235
 
235
236
  ## 🎯 Zero-Dependency Process Management
236
237
 
237
- `dexbot start` is the recommended production runtime (global install). Repo-root users can run `./unlock` instead. It runs the selected bot set as one monolithic bot process, with the credential daemon and market adapter in separate helper processes. Monolithic start/stop/restart controls apply to the whole runtime, not to individual bots.
238
+ `dexbot start` is the recommended production runtime (global install). Repo-root users can run `./unlock` instead. It runs the selected bot set as one monolithic bot process, with the credential daemon and market adapter in separate helper processes. Monolithic start/stop/reload/restart controls apply to the whole runtime, not to individual bots.
238
239
 
239
240
  ```bash
240
241
  dexbot start/stop # Stop/start the monolithic runtime
241
242
  dexbot start --dryrun # Dry-run (no transactions broadcast)
242
- dexbot restart # Restart the monolithic runtime
243
+ dexbot reload # Reload the runtime (leaves credential daemon untouched)
244
+ dexbot restart # Restart the runtime (re-unlocks credential daemon)
243
245
  dexbot delete # Shut down and clean up
244
246
  ```
245
247
 
@@ -259,6 +261,8 @@ dexbot enable {all|<bot>} # Enable bot in config
259
261
  dexbot stat # Runtime status (unlock or PM2)
260
262
  dexbot order [<bot>] # Analyze order grids
261
263
  dexbot order --export # Export as HTML to analysis/charts/
264
+ dexbot tv <bot|pool|A/B> # TradingView 1h chart with AMA overlay (default: 3 months)
265
+ dexbot credit [<bot>] # Live summed MPA + borrowed-credit positions per asset per bot
262
266
 
263
267
  dexbot update # Update DEXBot2
264
268
  dexbot clear # Clear log files
@@ -271,6 +275,7 @@ PM2 is optional — `dexbot start` is the native solution.
271
275
 
272
276
  ```bash
273
277
  dexbot pm2 [<bot>] # Start with PM2
278
+ dexbot pm2 reload {all|<bot>} # Reload managed apps, dexbot-cred untouched
274
279
  dexbot pm2 restart {all|<bot>|dexbot-cred} # Safe restart
275
280
  dexbot pm2 stop {all|<bot>} # Stop (via wrapper)
276
281
  dexbot pm2 delete {all|<bot>} # Delete (via wrapper)
@@ -21,9 +21,10 @@ Tools that inspect DEXBot trading behavior and the market data it operates on. O
21
21
  | Tool | Ask this when… | One-line command |
22
22
  |------|----------------|------------------|
23
23
  | [`trade_profitability.ts`](#trade-profitability-analyzer-trade_profitabilityts) | "Is my bot making money?" — PnL, R-multiples, drawdown | `npm run analysis:trade-pnl -- <account-id>` |
24
+ | [`grid_correction_check.ts`](#grid-correction-check-grid_correction_checkts) | "Is my grid placing orders monotonically?" — sell/buy price inversion detector | `npm run analysis:grid-check -- --bot-key <bot-key>` |
24
25
  | [`analyze_risk_profile.ts`](#risk-profile-analyzer-analyze_risk_profilets) | "How wide should my Safe Range clamps be?" | `node dist/analysis/analyze_risk_profile.js --bot-key <bot-key>` |
25
26
  | [`analyze_trade_heatmap.ts`](#trade-heatmap-analyze_trade_heatmapts) | "Where did trade volume cluster vs the AMA?" | `node dist/analysis/analyze_trade_heatmap.js --bot-key <bot-key>` |
26
- | [`tradingview/analyze_tradingview.ts`](#tradingview-chart-tradingviewanalyze_tradingviewts) | "Just give me a candle chart" | `npm run analysis:tradingview -- --source market_adapter --bot-key <bot-key>` |
27
+ | [`tradingview/analyze_tradingview.ts`](#tradingview-chart-tradingviewanalyze_tradingviewts) | "Just give me a candle chart" | `dexbot tv <bot-key>` |
27
28
  | [`analyze_dynamic_weight.ts`](#dynamic-weight-research-analyze_dynamic_weightts) | "Are buy/sell weights tuned for this regime?" | `node dist/analysis/analyze_dynamic_weight.js --bot-key <bot-key>` |
28
29
  | [`analyze_volatility.ts`](#volatility-analyze_volatilityts) | "Both weights clipped too hard / not enough?" | `node dist/analysis/analyze_volatility.js --bot-key <bot-key>` |
29
30
  | [`analyze_regime.ts`](#supporting-sub-signals) | "Is the trend/chaos gate too aggressive?" | `node dist/analysis/analyze_regime.js --bot-key <bot-key>` |
@@ -137,8 +138,8 @@ Fetches `fill_order` operations for a BitShares account from Kibana within a spe
137
138
  # Account by ID, last 7 days (default)
138
139
  node dist/analysis/trade_profitability.js 1.2.123456
139
140
 
140
- # Account by name with on-chain resolution
141
- node dist/analysis/trade_profitability.js "my-account-name" --lookup --hours 720
141
+ # Account by name (auto-resolved in the background)
142
+ node dist/analysis/trade_profitability.js "my-account-name" --hours 720
142
143
 
143
144
  # Absolute window with asset filter
144
145
  node dist/analysis/trade_profitability.js 1.2.123456 \
@@ -161,7 +162,8 @@ node dist/analysis/trade_profitability.js 1.2.123456 \
161
162
  | `--end <iso>` | — | End time |
162
163
  | `--hours <n>` | `168` (7d) | Lookback hours (alternative to start/end) |
163
164
  | `--asset <id>` | all | Filter to one base asset ID |
164
- | `--lookup` | off | Resolve account name to 1.2.x ID via BitShares node |
165
+ | `--lookup` | off | Legacy (no-op): account names always resolve automatically |
166
+ | `--refresh-account` | off | Force re-resolution and update the stored `accountId` |
165
167
  | `--node <url>` | first healthy from built-in pool (10 nodes) | BitShares node for account + asset resolution |
166
168
  | `--csv <file>` | — | Export chronologically sorted trade list |
167
169
  | `--json <file>` | — | Export full analysis with per-pair PnL data |
@@ -219,6 +221,50 @@ node dist/analysis/trade_profitability.js 1.2.123456 \
219
221
 
220
222
  </details>
221
223
 
224
+ ### Grid Correction Check (`grid_correction_check.ts`)
225
+
226
+ Validates grid discipline from the same Kibana fill pipeline as `trade_profitability.ts`: two consecutive same-direction fills on a pair must be monotonic — sell prices rising, buy prices falling (equal is OK). An inversion means the bot placed an order below its own previous sell (or above its own previous buy), e.g. an orphaned order filling outside grid accounting. Used as the external regression gate for the orphan-fix plans in `docs/CONSOLIDATED_ORPHAN_FIX_SUMMARY.md`.
227
+
228
+ **Pipeline:** Kibana `fill_order` query (paginated `search_after`) → on-chain asset precision resolution → buy/sell classification → chronological sort → per-order aggregation (multi-fill orders collapsed to weighted-avg price by default) → consecutive same-direction pair comparison → violation report with daily histogram.
229
+
230
+ ```bash
231
+ # Per-order aggregated check (default), last 7 days
232
+ npm run analysis:grid-check -- --bot-key <bot-key> --hours 168
233
+
234
+ # 30 days, JSON + CSV export of violations
235
+ npm run analysis:grid-check -- --bot-key <bot-key> --hours 720 --json out.json --csv out.csv
236
+
237
+ # Raw fill granularity instead of per-order aggregation
238
+ npm run analysis:grid-check -- --bot-key <bot-key> --per-fill --hours 168
239
+
240
+ # Forgive small adverse moves within 0.1%
241
+ npm run analysis:grid-check -- --bot-key <bot-key> --hours 168 --tolerance 0.1
242
+ ```
243
+
244
+ Exit code `0` = pass, `2` = violations found, `1` = fatal error. Bot keys resolve via `profiles/bots.json` (`--list-bots` to enumerate); the account defaults to the bot's stored `accountId` when present (no chain lookup — the ID is auto-saved next to `preferredAccount` after the first successful name resolution, re-verified with `--refresh-account`), otherwise `preferredAccount` is resolved on-chain, and can be overridden with `--account <1.2.x|name>`.
245
+
246
+ <details><summary>Options (click to expand)</summary>
247
+
248
+ | Flag | Default | Description |
249
+ |------|---------|-------------|
250
+ | `--bot-key <key>` | — | Bot key or name from `profiles/bots.json` (required) |
251
+ | `--hours <n>` | `168` | Lookback hours from now |
252
+ | `--start <iso>` / `--end <iso>` | — | Absolute time window |
253
+ | `--account <id>` | bot `preferredAccount` | Override account ID or name |
254
+ | `--lookup` | off | Legacy (no-op): account names always resolve via BitShares node when no stored ID exists |
255
+ | `--refresh-account` | off | Force re-resolution of `preferredAccount` and update the stored `accountId` when it changed |
256
+ | `--node <url>` | first built-in node | Node for account/asset resolution |
257
+ | `--per-fill` | off | Check at fill granularity instead of per-order aggregated |
258
+ | `--include-cross-pair` | off | Also check consecutive fills across different pairs |
259
+ | `--tolerance <pct>` | `0` | Adverse price move (%) forgiven before flagging |
260
+ | `--json <file>` / `--csv <file>` | — | Export violations |
261
+ | `--verbose` | off | Print the full trade sequence |
262
+ | `--list-bots` | — | List available bot keys and exit |
263
+
264
+ </details>
265
+
266
+ **Notes:** strict sat-level comparison is the ground truth (`--tolerance` only forgives small inversions); multi-fill orders are collapsed to a weighted-average price for the default per-order mode, so a single order's partial fills at identical prices never count as inversions.
267
+
222
268
  ## Charts & Visualization
223
269
 
224
270
  ### Trade Heatmap (`analyze_trade_heatmap.ts`)
@@ -265,7 +311,12 @@ node dist/analysis/analyze_trade_heatmap.js \
265
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.
266
312
 
267
313
  ```bash
268
- # Bot-key (auto-resolves candle file and AMA settings)
314
+ # Recommended one-step: bot, pool, or pair (fetches candles + renders, default 3 months)
315
+ dexbot tv <bot-key>
316
+ dexbot tv 133
317
+ dexbot tv TOKENA/TOKENB
318
+
319
+ # Manual: bot-key (auto-resolves candle file and AMA settings)
269
320
  npm run analysis:tradingview -- --source market_adapter --bot-key <bot-key>
270
321
 
271
322
  # From an explicit candle file
@@ -445,6 +496,7 @@ These npm scripts wrap common analysis runners:
445
496
  |--------|---------|
446
497
  | `npm run analysis:tradingview` | `node dist/analysis/tradingview/analyze_tradingview.js` |
447
498
  | `npm run analysis:trade-pnl` | `node dist/analysis/trade_profitability.js` |
499
+ | `npm run analysis:grid-check` | `node dist/analysis/grid_correction_check.js` |
448
500
  | `npm run analysis:derivatives` | `node dist/analysis/analyze_derivatives.js` (legacy SMA/MACD/RSI layer, reference only) |
449
501
  | `npm run ama:chart:lp-local` | `node dist/analysis/ama_fitting/generate_unified_comparison_chart.js` (chart also auto-generated by optimizer) |
450
502
 
@@ -457,6 +509,9 @@ npm run analysis:tradingview -- --source market_adapter --bot-key <bot-key>
457
509
  # Trade PnL
458
510
  npm run analysis:trade-pnl -- 1.2.123456 --hours 720
459
511
 
512
+ # Grid correction check (monotonicity regression gate)
513
+ npm run analysis:grid-check -- --bot-key <bot-key> --hours 168
514
+
460
515
  # File-based
461
516
  npm run analysis:tradingview -- --file market_adapter/data/market_adapter_<bot-key>_1h.json
462
517
  npm run ama:chart:lp-local -- --data market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
@@ -465,4 +520,5 @@ npm run ama:chart:lp-local -- --data market_adapter/data/lp/<pair>/lp_pool_<id>_
465
520
  ## Related Docs
466
521
 
467
522
  - [Market Adapter](../market_adapter/README.md) — live AMA pricing, grid triggers, dynamic weights, and recalc triggers
523
+ - [Consolidated Orphan-Fix Summary](../docs/CONSOLIDATED_ORPHAN_FIX_SUMMARY.md) — orphan/gap-band root-cause plans; `grid_correction_check` is their regression gate
468
524
  - [DEXBot2 Tuning Cheat Sheet](../claw/docs/DEXBOT2_TUNING_CHEAT_SHEET.md) — grid tuning reference for live bots
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ama_fitting",
3
- "version": "1.4.24",
3
+ "version": "1.5.0",
4
4
  "description": "Tools for fitting AMA parameters to market data",
5
5
  "main": "../../dist/analysis/ama_fitting/optimizer_high_resolution.js",
6
6
  "scripts": {
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * node dist/analysis/analyze_derivatives.js \\
16
16
  * --source market_adapter \
17
- * --bot-key XRP-BTS
17
+ * --bot-key AAA-BBB
18
18
  *
19
19
  * Output:
20
20
  * analysis/charts/derivative_chart.html
@@ -4,7 +4,8 @@ import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { PATHS } from '../modules/paths.js';
6
6
  import { getStorage } from '../modules/storage/index.js';
7
- const { readJSON } = getStorage();
7
+ const { readJSON, writeJSON } = getStorage();
8
+ import { parseJsonWithComments } from '../modules/order/utils/system.js';
8
9
  import { MARKET_ADAPTER } from '../modules/constants.js';
9
10
  import { loadMarketProfiles } from './tradingview/tradingview_uplot_chart_generator.js';
10
11
  import { sanitizeKey } from '../modules/utils/sanitize_key.js';
@@ -22,7 +23,13 @@ function loadBotSettings(filePath = PATHS.PROFILES.BOTS_JSON) {
22
23
  try {
23
24
  return readJSON(filePath);
24
25
  } catch (_) {
25
- return null;
26
+ // The bot editor reads/writes through a comment-tolerant parser, so
27
+ // operator comments may legally exist — fall back before giving up.
28
+ try {
29
+ return parseJsonWithComments(fs.readFileSync(filePath, 'utf8'));
30
+ } catch (_) {
31
+ return null;
32
+ }
26
33
  }
27
34
  }
28
35
 
@@ -70,6 +77,268 @@ function loadBotMeta(botKey: any, filePath = PATHS.PROFILES.BOTS_JSON) {
70
77
  return loose || null;
71
78
  }
72
79
 
80
+ /**
81
+ * Locate the source span of the entryIndex-th object literal inside the
82
+ * top-level "bots" array of a (possibly comment-bearing) JSON document.
83
+ * Strings and // + block comments are skipped while scanning, so braces
84
+ * inside them never disturb the balance count. Returns null when the
85
+ * document shape is unexpected — callers fall back to a full rewrite.
86
+ */
87
+ function findBotsEntrySpan(raw: string, entryIndex: number): { start: number; end: number } | null {
88
+ const m = /"bots"\s*:\s*\[/.exec(raw);
89
+ if (!m) return null;
90
+ let i = m.index + m[0].length; // just past '['
91
+ const n = raw.length;
92
+ let count = 0;
93
+ const skipWsComments = () => {
94
+ while (i < n) {
95
+ const c = raw[i];
96
+ if (c === ' ' || c === '\t' || c === '\n' || c === '\r') { i++; continue; }
97
+ if (c === '/' && raw[i + 1] === '/') { while (i < n && raw[i] !== '\n') i++; continue; }
98
+ if (c === '/' && raw[i + 1] === '*') { i += 2; while (i < n && !(raw[i] === '*' && raw[i + 1] === '/')) i++; i += 2; continue; }
99
+ break;
100
+ }
101
+ };
102
+ const skipString = () => {
103
+ const q = raw[i];
104
+ i++;
105
+ while (i < n) {
106
+ const c = raw[i];
107
+ if (c === '\\') { i += 2; continue; }
108
+ if (c === q) { i++; return; }
109
+ i++;
110
+ }
111
+ };
112
+ // Skip one JSON value starting at raw[i]; false on malformed input.
113
+ const skipValue = (): boolean => {
114
+ skipWsComments();
115
+ if (i >= n) return false;
116
+ const c = raw[i];
117
+ if (c === '"' || c === "'") { skipString(); return true; }
118
+ if (c === '{' || c === '[') {
119
+ const open = c;
120
+ const close = c === '{' ? '}' : ']';
121
+ i++;
122
+ let depth = 1;
123
+ while (i < n && depth > 0) {
124
+ const d = raw[i];
125
+ if (d === '"' || d === "'") { skipString(); continue; }
126
+ if (d === '/' && raw[i + 1] === '/') { while (i < n && raw[i] !== '\n') i++; continue; }
127
+ if (d === '/' && raw[i + 1] === '*') { i += 2; while (i < n && !(raw[i] === '*' && raw[i + 1] === '/')) i++; i += 2; continue; }
128
+ if (d === open) depth++;
129
+ else if (d === close) depth--;
130
+ i++;
131
+ }
132
+ return depth === 0;
133
+ }
134
+ while (i < n && raw[i] !== ',' && raw[i] !== ']' && raw[i] !== '}') i++;
135
+ return true;
136
+ };
137
+ for (;;) {
138
+ skipWsComments();
139
+ if (i >= n) return null;
140
+ if (raw[i] === ']') return null; // array ended before entryIndex
141
+ if (raw[i] === '{') {
142
+ const start = i;
143
+ if (!skipValue()) return null;
144
+ if (count === entryIndex) return { start, end: i };
145
+ count++;
146
+ } else if (!skipValue()) {
147
+ return null;
148
+ }
149
+ skipWsComments();
150
+ if (i < n && raw[i] === ',') { i++; continue; }
151
+ if (i < n && raw[i] === ']') return null;
152
+ return null; // malformed separator
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Locate the string value of a top-level key inside one bot-entry source
158
+ * span. Unlike a regex, this scan skips over // and block comments as well
159
+ * as string literals, so a key-looking fragment inside a comment (e.g.
160
+ * `// "accountId": "1.2.9999" (old)`) can never be mistaken for the real
161
+ * key. Returns the span of the quoted value (including quotes), or null
162
+ * when the key is absent or its value is not a string.
163
+ */
164
+ function findEntryKeyValueSpan(entrySrc: string, key: string): { valueStart: number; valueEnd: number } | null {
165
+ const n = entrySrc.length;
166
+ const isWs = (c: string) => c === ' ' || c === '\t' || c === '\n' || c === '\r';
167
+ let i = 0;
168
+ let depth = 0;
169
+ while (i < n) {
170
+ const c = entrySrc[i];
171
+ if (isWs(c)) { i++; continue; }
172
+ if (c === '/' && entrySrc[i + 1] === '/') { while (i < n && entrySrc[i] !== '\n') i++; continue; }
173
+ if (c === '/' && entrySrc[i + 1] === '*') { i += 2; while (i < n && !(entrySrc[i] === '*' && entrySrc[i + 1] === '/')) i++; i += 2; continue; }
174
+ if (c === '{' || c === '[') { depth++; i++; continue; }
175
+ if (c === '}' || c === ']') { depth--; i++; continue; }
176
+ if (c !== '"') { i++; continue; }
177
+ // String literal: read it (honoring backslash escapes).
178
+ let j = i + 1;
179
+ let str = '';
180
+ while (j < n) {
181
+ const d = entrySrc[j];
182
+ if (d === '\\') { str += entrySrc.slice(j, j + 2); j += 2; continue; }
183
+ if (d === '"') break;
184
+ str += d; j++;
185
+ }
186
+ if (j >= n) return null;
187
+ const closingQuote = j;
188
+ if (depth !== 1 || str !== key) { i = closingQuote + 1; continue; }
189
+ // Candidate key at top level: expect `:` then a quoted value.
190
+ let k = closingQuote + 1;
191
+ while (k < n && isWs(entrySrc[k])) k++;
192
+ if (entrySrc[k] !== ':') { i = closingQuote + 1; continue; }
193
+ k++;
194
+ while (k < n && isWs(entrySrc[k])) k++;
195
+ if (entrySrc[k] !== '"') { i = closingQuote + 1; continue; }
196
+ let m = k + 1;
197
+ while (m < n) {
198
+ const e = entrySrc[m];
199
+ if (e === '\\') { m += 2; continue; }
200
+ if (e === '"') return { valueStart: k, valueEnd: m + 1 };
201
+ m++;
202
+ }
203
+ return null;
204
+ }
205
+ return null;
206
+ }
207
+
208
+ /**
209
+ * Byte-preserving accountId update: replaces the value in place, or inserts
210
+ * `"accountId"` right after `"preferredAccount"` keeping the line indent, so
211
+ * comments, key order and formatting elsewhere in the file survive. The
212
+ * patched document is re-parsed (comments allowed) and the value asserted
213
+ * before the atomic tmp+rename write. Returns false on any uncertainty —
214
+ * callers fall back to a full rewrite.
215
+ */
216
+ function patchAccountIdInRaw(filePath: string, entryIndex: number, accountId: string): boolean {
217
+ let raw: string;
218
+ try {
219
+ raw = fs.readFileSync(filePath, 'utf8');
220
+ } catch (_) {
221
+ return false;
222
+ }
223
+ const span = findBotsEntrySpan(raw, entryIndex);
224
+ if (!span) return false;
225
+ const entrySrc = raw.slice(span.start, span.end);
226
+ let patchedEntry: string | null = null;
227
+ const idSpan = findEntryKeyValueSpan(entrySrc, 'accountId');
228
+ if (idSpan) {
229
+ patchedEntry = entrySrc.slice(0, idSpan.valueStart)
230
+ + `"${accountId}"`
231
+ + entrySrc.slice(idSpan.valueEnd);
232
+ } else {
233
+ const prefSpan = findEntryKeyValueSpan(entrySrc, 'preferredAccount');
234
+ if (!prefSpan) return false;
235
+ const lineStart = entrySrc.lastIndexOf('\n', prefSpan.valueEnd) + 1;
236
+ const indentMatch = /^[ \t]*/.exec(entrySrc.slice(lineStart));
237
+ const indent = indentMatch ? indentMatch[0] : ' ';
238
+ patchedEntry = entrySrc.slice(0, prefSpan.valueEnd)
239
+ + `,\n${indent}"accountId": "${accountId}"`
240
+ + entrySrc.slice(prefSpan.valueEnd);
241
+ }
242
+ if (!patchedEntry || patchedEntry === entrySrc) return false;
243
+ const patched = raw.slice(0, span.start) + patchedEntry + raw.slice(span.end);
244
+ try {
245
+ const reparsed = parseJsonWithComments(patched);
246
+ const checkEntries = Array.isArray(reparsed?.bots) ? reparsed.bots : [];
247
+ if (checkEntries[entryIndex]?.accountId !== accountId) return false;
248
+ } catch (_) {
249
+ return false;
250
+ }
251
+ const tmpPath = `${filePath}.tmp-persist-${process.pid}-${Date.now()}`;
252
+ try {
253
+ fs.writeFileSync(tmpPath, patched, 'utf8');
254
+ fs.renameSync(tmpPath, filePath);
255
+ return true;
256
+ } catch (_) {
257
+ try { fs.unlinkSync(tmpPath); } catch (_) { /* best-effort cleanup */ }
258
+ return false;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Persist a resolved account name → ID mapping onto the bot entry in
264
+ * profiles/bots.json (`accountId` next to `preferredAccount`), so analysis
265
+ * tools can skip the chain lookup on subsequent runs. No-op when the stored
266
+ * value already matches. Prefers a byte-preserving textual patch (comments,
267
+ * key order and formatting survive); falls back to a full rewrite using the
268
+ * same atomic writeJSON serialization the bot editor uses. Returns true when
269
+ * the file was updated.
270
+ */
271
+ function persistBotAccountId(botKey: any, accountId: string, filePath = PATHS.PROFILES.BOTS_JSON): boolean {
272
+ if (!botKey || !/^1\.2\.\d+$/.test(String(accountId))) return false;
273
+ let settings: any = null;
274
+ try {
275
+ settings = loadBotSettings(filePath);
276
+ } catch (_) {
277
+ return false;
278
+ }
279
+ const entries = Array.isArray(settings?.bots) ? settings.bots : [];
280
+ const normalizedKey = String(botKey).toLowerCase();
281
+ const index = entries.findIndex((bot: any, i: number) => computeBotKey(bot, i) === normalizedKey);
282
+ if (index < 0 || entries[index]?.accountId === accountId) return false;
283
+ try {
284
+ if (patchAccountIdInRaw(filePath, index, String(accountId))) return true;
285
+ } catch (_) {
286
+ // Fall through to the full rewrite below.
287
+ }
288
+ entries[index].accountId = String(accountId);
289
+ try {
290
+ writeJSON(filePath, settings);
291
+ return true;
292
+ } catch (_) {
293
+ return false;
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Return the stored chain account ID for a bot key, or null when none is
299
+ * stamped yet. When expectedRef is given, the stored ID is only returned if
300
+ * the entry's current preferredAccount still matches it — a rename must
301
+ * never silently reuse a stale ID.
302
+ */
303
+ function getStoredBotAccountId(botKey: any, expectedRef?: string | null, filePath = PATHS.PROFILES.BOTS_JSON): string | null {
304
+ if (!botKey) return null;
305
+ let meta: any = null;
306
+ try {
307
+ meta = loadBotMeta(botKey, filePath);
308
+ } catch (_) {
309
+ return null;
310
+ }
311
+ const stored = meta && /^1\.2\.\d+$/.test(String(meta.accountId ?? '')) ? String(meta.accountId) : null;
312
+ if (!stored) return null;
313
+ if (expectedRef != null && String(meta.preferredAccount ?? '').toLowerCase() !== String(expectedRef).toLowerCase()) return null;
314
+ return stored;
315
+ }
316
+
317
+ /**
318
+ * Find the bot entry whose preferredAccount matches a raw account reference
319
+ * (case-insensitive). Used by tools that accept a bare account name to locate
320
+ * the stored accountId and the persist target. Returns null for 1.2.x input
321
+ * or when no bot claims the name.
322
+ */
323
+ function findBotKeyByAccountRef(ref: any, filePath = PATHS.PROFILES.BOTS_JSON): { botKey: string; meta: any } | null {
324
+ if (!ref || /^1\.2\.\d+$/.test(String(ref))) return null;
325
+ let settings: any = null;
326
+ try {
327
+ settings = loadBotSettings(filePath);
328
+ } catch (_) {
329
+ return null;
330
+ }
331
+ const entries = Array.isArray(settings?.bots) ? settings.bots : [];
332
+ const want = String(ref).toLowerCase();
333
+ for (let i = 0; i < entries.length; i++) {
334
+ const entry = entries[i];
335
+ if (entry && String(entry.preferredAccount ?? '').toLowerCase() === want) {
336
+ return { botKey: computeBotKey(entry, i), meta: entry };
337
+ }
338
+ }
339
+ return null;
340
+ }
341
+
73
342
  function resolveAmaConfig(botKey: any) {
74
343
  const botMeta = loadBotMeta(botKey);
75
344
  // Unknown bot key: fall back to the global erSmoothPeriod default (same
@@ -153,5 +422,5 @@ function resolveAmaKey(botKey: any) {
153
422
  return 'AMA3';
154
423
  }
155
424
 
156
- export { loadBotSettings, sanitizeKey, computeBotKey, resolveBotKey, candleFileForBot, resolveCandleFile, loadBotMeta, resolveAmaConfig, resolveAmaKey }
425
+ export { loadBotSettings, sanitizeKey, computeBotKey, resolveBotKey, candleFileForBot, resolveCandleFile, loadBotMeta, getStoredBotAccountId, findBotKeyByAccountRef, persistBotAccountId, resolveAmaConfig, resolveAmaKey }
157
426