dexbot 1.6.3 → 1.6.5
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 +78 -2
- package/README.md +23 -19
- package/analysis/README.md +26 -13
- package/analysis/account_resolver.ts +171 -0
- package/analysis/ama_fitting/analyze_lambda_vs_slow.ts +2 -5
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/analyze_dynamic_weight.ts +9 -3
- package/analysis/bot_usage/discover_bot_accounts.ts +20 -27
- package/analysis/chain_pool.ts +47 -0
- package/analysis/chart_utils.ts +33 -20
- package/analysis/derivative_chart_generator.ts +2 -3
- package/analysis/fills_source.ts +234 -0
- package/analysis/grid_correction_check.ts +38 -251
- package/analysis/trade_profitability.ts +169 -304
- package/analysis/tradingview/README.md +2 -2
- package/analysis/tradingview/analyze_tradingview.ts +5 -0
- package/analysis/tradingview/tradingview_uplot_chart_generator.ts +5 -6
- package/analysis/trend_detection/DYNAMIC_WEIGHT_RESEARCH.md +35 -2
- package/analysis/trend_detection/dynamic_weight_chart_generator.ts +84 -13
- package/analysis/trend_detection/kalman_chart_generator.ts +2 -3
- package/analysis/trend_detection/package.json +1 -1
- package/analysis/trend_detection/regime_chart_generator.ts +2 -3
- package/analysis/trend_detection/volatility_chart_generator.ts +2 -3
- package/claw/modules/dexbot_profiles.ts +4 -3
- package/claw/package.json +1 -1
- package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
- package/claw/runtimes/openclaw-plugin/package.json +1 -1
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/claw/tests/test_dexbot_profiles.ts +18 -1
- package/dist/analysis/account_resolver.d.ts +43 -0
- package/dist/analysis/account_resolver.d.ts.map +1 -0
- package/dist/analysis/account_resolver.js +137 -0
- package/dist/analysis/account_resolver.js.map +1 -0
- package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts.map +1 -1
- package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +2 -4
- package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -1
- package/dist/analysis/analyze_dynamic_weight.js +9 -3
- package/dist/analysis/analyze_dynamic_weight.js.map +1 -1
- package/dist/analysis/bot_usage/discover_bot_accounts.js +20 -28
- package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
- package/dist/analysis/chain_pool.d.ts +10 -0
- package/dist/analysis/chain_pool.d.ts.map +1 -0
- package/dist/analysis/chain_pool.js +47 -0
- package/dist/analysis/chain_pool.js.map +1 -0
- package/dist/analysis/chart_utils.d.ts +14 -1
- package/dist/analysis/chart_utils.d.ts.map +1 -1
- package/dist/analysis/chart_utils.js +32 -22
- package/dist/analysis/chart_utils.js.map +1 -1
- package/dist/analysis/derivative_chart_generator.d.ts.map +1 -1
- package/dist/analysis/derivative_chart_generator.js +2 -3
- package/dist/analysis/derivative_chart_generator.js.map +1 -1
- package/dist/analysis/fills_source.d.ts +79 -0
- package/dist/analysis/fills_source.d.ts.map +1 -0
- package/dist/analysis/fills_source.js +186 -0
- package/dist/analysis/fills_source.js.map +1 -0
- package/dist/analysis/grid_correction_check.d.ts +1 -21
- package/dist/analysis/grid_correction_check.d.ts.map +1 -1
- package/dist/analysis/grid_correction_check.js +30 -257
- package/dist/analysis/grid_correction_check.js.map +1 -1
- package/dist/analysis/trade_profitability.d.ts +18 -19
- package/dist/analysis/trade_profitability.d.ts.map +1 -1
- package/dist/analysis/trade_profitability.js +136 -280
- package/dist/analysis/trade_profitability.js.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -1
- package/dist/analysis/tradingview/analyze_tradingview.js +5 -0
- package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +5 -6
- package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
- package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -1
- package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +84 -13
- package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -1
- package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -1
- package/dist/analysis/trend_detection/kalman_chart_generator.js +2 -3
- package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -1
- package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -1
- package/dist/analysis/trend_detection/regime_chart_generator.js +2 -3
- package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -1
- package/dist/analysis/trend_detection/volatility_chart_generator.d.ts.map +1 -1
- package/dist/analysis/trend_detection/volatility_chart_generator.js +2 -3
- package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -1
- package/dist/dexbot.d.ts.map +1 -1
- package/dist/dexbot.js +27 -68
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/core/asymmetric_bounds.d.ts +2 -2
- package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -1
- package/dist/market_adapter/core/asymmetric_bounds.js +15 -12
- package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -1
- package/dist/market_adapter/interval_utils.d.ts +1 -1
- package/dist/market_adapter/interval_utils.js +1 -1
- package/dist/market_adapter/lp_chart_core.d.ts.map +1 -1
- package/dist/market_adapter/lp_chart_core.js +2 -3
- package/dist/market_adapter/lp_chart_core.js.map +1 -1
- package/dist/modules/account_bots.d.ts +51 -18
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +258 -117
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/bitshares-native/serial/operations.d.ts +1 -84
- package/dist/modules/bitshares-native/serial/operations.d.ts.map +1 -1
- package/dist/modules/bitshares-native/serial/operations.js +1 -38
- package/dist/modules/bitshares-native/serial/operations.js.map +1 -1
- package/dist/modules/bot_defaults.d.ts +98 -0
- package/dist/modules/bot_defaults.d.ts.map +1 -0
- package/dist/modules/bot_defaults.js +169 -0
- package/dist/modules/bot_defaults.js.map +1 -0
- package/dist/modules/bot_settings.d.ts.map +1 -1
- package/dist/modules/bot_settings.js +4 -1
- package/dist/modules/bot_settings.js.map +1 -1
- package/dist/modules/cli_colors.d.ts +0 -1
- package/dist/modules/cli_colors.d.ts.map +1 -1
- package/dist/modules/config.d.ts +1 -0
- package/dist/modules/config.d.ts.map +1 -1
- package/dist/modules/config.js +1 -0
- package/dist/modules/config.js.map +1 -1
- package/dist/modules/constants.d.ts +11 -2
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +45 -32
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/credential_policy.d.ts +1 -25
- package/dist/modules/credential_policy.d.ts.map +1 -1
- package/dist/modules/credential_policy.js +1 -1
- package/dist/modules/credential_policy.js.map +1 -1
- package/dist/modules/credential_runtime.d.ts +1 -6
- package/dist/modules/credential_runtime.d.ts.map +1 -1
- package/dist/modules/credential_runtime.js +1 -1
- package/dist/modules/credential_runtime.js.map +1 -1
- package/dist/modules/credential_session_cache.d.ts +1 -5
- package/dist/modules/credential_session_cache.d.ts.map +1 -1
- package/dist/modules/credential_session_cache.js +1 -1
- package/dist/modules/credential_session_cache.js.map +1 -1
- package/dist/modules/crypto/pure_secp256k1.d.ts +1 -2
- package/dist/modules/crypto/pure_secp256k1.d.ts.map +1 -1
- package/dist/modules/crypto/pure_secp256k1.js +1 -1
- package/dist/modules/crypto/pure_secp256k1.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +8 -7
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +9 -8
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +7 -6
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +8 -7
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_fill_runtime.d.ts +1 -17
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +1 -1
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +2 -2
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +24 -8
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/dexbot_state_recovery.d.ts +1 -7
- package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
- package/dist/modules/dexbot_state_recovery.js +1 -1
- package/dist/modules/dexbot_state_recovery.js.map +1 -1
- package/dist/modules/launcher/adapter_requirement.d.ts +1 -5
- package/dist/modules/launcher/adapter_requirement.d.ts.map +1 -1
- package/dist/modules/launcher/adapter_requirement.js +2 -2
- package/dist/modules/launcher/adapter_requirement.js.map +1 -1
- package/dist/modules/launcher/bot_supervisor.d.ts +1 -6
- package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
- package/dist/modules/launcher/bot_supervisor.js +1 -1
- package/dist/modules/launcher/bot_supervisor.js.map +1 -1
- package/dist/modules/launcher/foreign_cred_daemon.d.ts +1 -3
- package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -1
- package/dist/modules/launcher/foreign_cred_daemon.js +1 -1
- package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -1
- package/dist/modules/launcher/launch_modes.d.ts +17 -2
- package/dist/modules/launcher/launch_modes.d.ts.map +1 -1
- package/dist/modules/launcher/launch_modes.js +18 -1
- package/dist/modules/launcher/launch_modes.js.map +1 -1
- package/dist/modules/launcher/market_adapter_runtime.d.ts +1 -3
- package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/market_adapter_runtime.js +1 -1
- package/dist/modules/launcher/market_adapter_runtime.js.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.d.ts +1 -5
- package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.js +9 -2
- package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
- package/dist/modules/launcher/status_reporting.d.ts +1 -2
- package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
- package/dist/modules/launcher/status_reporting.js +1 -1
- package/dist/modules/launcher/status_reporting.js.map +1 -1
- package/dist/modules/market_adapter_whitelist.d.ts +40 -1
- package/dist/modules/market_adapter_whitelist.d.ts.map +1 -1
- package/dist/modules/market_adapter_whitelist.js +161 -11
- package/dist/modules/market_adapter_whitelist.js.map +1 -1
- package/dist/modules/order/export.d.ts +1 -8
- package/dist/modules/order/export.d.ts.map +1 -1
- package/dist/modules/order/export.js +1 -1
- package/dist/modules/order/export.js.map +1 -1
- package/dist/modules/order/grid.d.ts +16 -0
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +75 -103
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts +1 -10
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.js +2 -2
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/manager.d.ts +10 -2
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +20 -8
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +8 -15
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +1 -10
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +1 -1
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +5 -16
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +7 -4
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/process_discovery.d.ts +0 -3
- package/dist/modules/process_discovery.d.ts.map +1 -1
- package/dist/modules/process_discovery.js +1 -1
- package/dist/modules/process_discovery.js.map +1 -1
- package/dist/modules/settings_merge.d.ts +10 -1
- package/dist/modules/settings_merge.d.ts.map +1 -1
- package/dist/modules/settings_merge.js +27 -17
- package/dist/modules/settings_merge.js.map +1 -1
- package/dist/modules/utils/chain_logs.d.ts +1 -2
- package/dist/modules/utils/chain_logs.d.ts.map +1 -1
- package/dist/modules/utils/chain_logs.js +1 -1
- package/dist/modules/utils/chain_logs.js.map +1 -1
- package/dist/modules/validate_profiles.d.ts.map +1 -1
- package/dist/modules/validate_profiles.js +4 -3
- package/dist/modules/validate_profiles.js.map +1 -1
- package/dist/scripts/analyze-git.js +2 -2
- package/dist/scripts/analyze-git.js.map +1 -1
- package/dist/scripts/chart_command.d.ts +48 -0
- package/dist/scripts/chart_command.d.ts.map +1 -0
- package/dist/scripts/chart_command.js +504 -0
- package/dist/scripts/chart_command.js.map +1 -0
- package/dist/scripts/dw.d.ts +3 -0
- package/dist/scripts/dw.d.ts.map +1 -0
- package/dist/scripts/dw.js +30 -0
- package/dist/scripts/dw.js.map +1 -0
- package/dist/scripts/tv.d.ts +1 -31
- package/dist/scripts/tv.d.ts.map +1 -1
- package/dist/scripts/tv.js +10 -463
- package/dist/scripts/tv.js.map +1 -1
- package/dist/scripts/update.js +145 -93
- package/dist/scripts/update.js.map +1 -1
- package/dist/scripts/update_dist_freshness.d.ts +46 -0
- package/dist/scripts/update_dist_freshness.d.ts.map +1 -0
- package/dist/scripts/update_dist_freshness.js +115 -0
- package/dist/scripts/update_dist_freshness.js.map +1 -0
- package/dist/unlock.js +1 -1
- package/dist/unlock.js.map +1 -1
- package/docs/BITSHARES_ONBOARDING.md +117 -44
- package/docs/COPY_ON_WRITE_MASTER_PLAN.md +10 -207
- package/docs/COW_INVARIANTS.md +16 -10
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +76 -30
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +9 -9
- package/docs/GRID_RECALCULATION.md +34 -30
- package/docs/GRID_RECONCILE.md +30 -27
- package/docs/LIFECYCLE.md +12 -11
- package/docs/LOGGING.md +1 -0
- package/docs/README.md +58 -52
- package/docs/WORKFLOW.md +1 -2
- package/docs/architecture.md +21 -32
- package/docs/developer_guide.md +4 -3
- package/market_adapter/README.md +36 -60
- package/modules/README.md +2 -1
- package/package.json +1 -2
- package/scripts/README.md +18 -25
- package/scripts/git-viewer.sh +1 -1
- package/scripts/reset-settings.sh +1 -1
- package/tests/README.md +10 -1
- package/dist/scripts/generate_market_adapter_whitelist.d.ts +0 -15
- package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +0 -1
- package/dist/scripts/generate_market_adapter_whitelist.js +0 -191
- package/dist/scripts/generate_market_adapter_whitelist.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,82 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.6.5] - 2026-09-23 - Editor-Managed Whitelist Flags, GridPrice Normalization, Pool/Health Cues, Log-Symmetric Range Tilt, Centralized Bot Defaults, Dynamic-Weight CLI, Update Self-Heal
|
|
6
|
+
|
|
7
|
+
### 2026-09-23
|
|
8
|
+
|
|
9
|
+
- **Fix(bot-editor)**: treat `gridPrice` deactivating entries as `startPrice` and normalize them to `null` — typing `n`/`no`/`false`/`0`/`f`/`s`/`start`/`none`/blank, or a lone space, now commits `null` and the editor renders it as red `startPrice`, matching the live echo. Previously a whitespace-only entry previewed `startPrice` but a bare-Enter path silently kept the old value (`readInput` trimmed the resolved line, so a typed space was indistinguishable from Enter). `readInput` gains a `trimInput` option (default true, existing callers unchanged); `askGridPriceMode` opts out and tells a bare Enter (keep current) from whitespace (overwrite null). New `GRID_PRICE_UNSET_INPUTS`/`isUnsetGridPrice` in `modules/bot_defaults.ts` is the single spelling set, shared by the editor prompt, the display, and `seedBotDraft`, so a hand-edited `"gridPrice": false`/`"no"` normalizes to null at seed time instead of lingering as a value the runtime only silently degrades. Tests: unset → null (`tests/test_bot_defaults_characterization.ts`), red-startPrice rendering (`tests/test_account_bots_adapter.ts`, which now exports `colorGridPriceValue`), and `trimInput` default-vs-opt-out (`tests/test_read_input.ts`) (`modules/account_bots.ts`, `modules/bot_defaults.ts`, `modules/order/utils/system.ts`).
|
|
10
|
+
|
|
11
|
+
- **Feat(bot-editor)**: clearer `Pool` and `Adapter` status cues — the `3) Price` summary's `Pool:` field no longer reads `none` when the runtime auto-selects the pair's default pool: it shows a green `default` when `startPrice` is `pool`, a grey `none` otherwise, and a pinned `poolRef` still shows its ID (display-only; `formatPoolRefLabel` never touches the stored value; a `gridPrice` of `pool`/`book` is discouraged market-price anchoring and stays red in its own field). `6) Adapter` Weight/Range `false` now render bright yellow (`COLORS.yellowBold`) as optional-rider warnings instead of red; `Price` false stays red as the gate. New `testFormatPoolRefLabel` and a red-`book` assertion in `tests/test_account_bots_adapter.ts` (which now exports `formatPoolRefLabel`).
|
|
12
|
+
|
|
13
|
+
- **Feat(settings)**: expose the AMA-slope reset trigger in General settings — `1) Grid Health` now also edits `MARKET_ADAPTER.AMA_SLOPE_DELTA_THRESHOLD_PERCENT` (default `8%`, range 0.1-100; the slope-delta trigger as a percentage of max AMA slope), and the health line renders both thresholds with the Δ symbol and `%` (`AMA Δ: 1%, AMA-Slope Δ: 8%`); the input prompts are labelled `AMA Δ` / `AMA-Slope Δ` (the unit stays on the health line, not repeated at the prompt). `loadGeneralSettings` validates the new value with the same `>0` guard as the center-delta threshold, so a hand-edited `0`/negative/NaN cannot silently disable the slope reset. Also relabels the first Grid Health value `Ratio` → `Funds` (the trigger is available-funds/allocated-capital, so the old name was vague; config key `GRID_REGENERATION_PERCENTAGE` unchanged) and its prompt `Grid Ratio Regeneration %` → `Grid Funds Regeneration %`. Docs: `README.md` global-settings reference and the `docs/GRID_RECALCULATION.md` range-scaling trigger note updated (`modules/account_bots.ts`, `README.md`, `docs/GRID_RECALCULATION.md`).
|
|
14
|
+
|
|
15
|
+
- **Refactor(whitelist)**: remove the legacy `dexbot whitelist`/`white` command and its bulk generator — `scripts/generate_market_adapter_whitelist.ts` and the `market-adapter:whitelist` npm script are fully superseded by the per-bot editor (`dexbot bot` -> `2) Modify bot` -> `6) Adapter`), which reads/writes `profiles/market_adapter_whitelist.json` directly. The command, script, CLI help/alias/docstring entries, and all docs/tests that referenced it are gone; deleting a bot in the editor now prunes its whitelist entry via a new `removeWhitelistEntry(botKey)` helper (`modules/market_adapter_whitelist.ts`), run only after the `bots.json` save succeeds (`modules/account_bots.ts`), so a deleted bot cannot leave stale flags for a future same-name bot to inherit (a malformed whitelist file aborts the removal and is never silently replaced). Behavioral impact: `dexbot whitelist`/`white` is an unknown command; bots removed by hand-editing `bots.json` still need manual pruning (documented in `market_adapter/README.md`). New `tests/test_account_bots_adapter.ts` case (`dexbot.ts`, `package.json`, `scripts/README.md`, `scripts/reset-settings.sh`, `docs/README.md`, `docs/GRID_RECALCULATION.md`, `docs/WORKFLOW.md`, `market_adapter/README.md`).
|
|
16
|
+
|
|
17
|
+
- **Feat(bot-editor)**: default `gridPrice` to `ama3` and color AMA values as healthy — `DEFAULT_CONFIG.gridPrice` was null (delegating to `startPrice`), so a freshly created bot priced its bounds off `startPrice` while every other AMA-capable path expects a preset; the editor also greened pool/book/numeric exactly like valid AMA values. `DEFAULT_CONFIG.gridPrice` is now `"ama3"` (`modules/constants.ts`), the prompt label reads `gridPrice (ama1/ama2/ama3/ama4)` (bare `ama` still accepted), and `colorGridPriceValue()` greens only AMA values and reds pool/book/numeric/null across the Enter default, live input echo, and `3) Price` menu line. Section `6) Adapter` now renders Price/Weight/Range through `colorBooleanFlag(v, true)` instead of a local gray-for-false lambda, so each flag reads as a health state (green on, red off) like `Active` — the optional Weight/Range off-state was later softened to bright yellow (see the `Fix(bot-editor)` entry above). Input acceptance is unchanged — pool, book, bare ama, ama1..ama4, positive numbers, none/null/startprice, and Enter keeping the current value all still work; red flags "not an AMA value" rather than blocking it. New drafts and configs missing the key seed `ama3`; explicit values in existing bots entries are untouched (seeding fills only undefined), and whitelist flags stay false for a bot with no entry, so an ama3 bot starts in adapter dry-run and falls back to startPrice with a warning until Adapter -> Price is enabled. `modules/bot_defaults.ts` and `tests/test_bot_defaults_characterization.ts` follow the new default.
|
|
18
|
+
|
|
19
|
+
- **Refactor(launcher)**: spawn the supervised bot worker as `worker`, not `test` — `dexbot start` launched the bot as `dist/dexbot.js test`, which read like a test job in htop/ps even though it is the live runtime. `buildDexbotStartArgs` now emits `['worker', ...(dryrun ? ['--dryrun'] : []), botName?]` (`modules/launcher/monolithic_runtime.ts`); `unlock.ts` sets `DEXBOT_LAUNCHER_WORKER=1` on the child env (`modules/config.ts` exposes the flag) and `dexbot.ts` intercepts the internal `worker` command when the marker is set, before command validation, so it never re-enters the supervisor. Public `dexbot start` still delegates to `unlock`; the `test` help wording was clarified. Launcher/unlock tests updated, including an assertion that the child carries the worker marker.
|
|
20
|
+
|
|
21
|
+
- **Fix(update)**: rebuild when source is current but `dist/` is stale — the git update flow exited before `npm run build` when there were no incoming commits, and tsc's incremental cache will not re-emit an output it believes is current (it will not even recreate a deleted file), so a `dist/` that lagged its source stayed stale while every later run reported "already up to date"; the old guard only checked the `dist/modules/dexbot_class.js` mtime, so a stale `dist/dexbot.js` (holding the CLI alias table) went unnoticed. New `scripts/update_dist_freshness.ts` detects missing/stale outputs without invoking the compiler over exactly the root tsconfig include roots; `scripts/update.ts` self-heals on the no-op path (force a full rebuild, then restart), re-checks and force-emits on the normal path, and `assertDistBundleFresh()` replaces the single-marker guard with a full-bundle check. New `tests/test_update_dist_freshness.ts` covers missing entries, missing/stale counterparts, root-level entry points, and non-compiled files.
|
|
22
|
+
|
|
23
|
+
- **Feat(defaults)**: centralize bot defaults, settings-doc building, and whitelist flags — seven scattered producers applied `DEFAULT_CONFIG` and whitelist defaults independently and had already drifted. New `modules/bot_defaults.ts` (`seedBotDraft`/`seedBotEntry`/`seedBotRuntimeConfig` with classified key sets, `normalizeBotDraft`/`normalizeBotEntry`) now backs the editor draft, `bot_settings` + claw, and the `OrderManager` constructor. `buildDefaultGeneralSettings()` (`modules/constants.ts`) and `buildNodesView()` (`modules/settings_merge.ts`) give the first-run generator, the editor fallback (`account_bots.loadGeneralSettings`), and the local-overrides merge one shared document (`loadGeneralSettings`/`saveGeneralSettings` now exported). `DEFAULT_WHITELIST_FLAGS`/`AMA_ONLY`/`ALL_ENABLED` constants replace every inline flag literal. Behavioral impact: a draft's `gridPrice` follows a `DEFAULT_CONFIG.gridPrice` override (default stays null here); a missing `bot.active` sources `DEFAULT_CONFIG.active` (was hardcoded true) and claw no longer coerces present null/0, unifying on `active !== false`; `OrderManager` clones absent defaults so config mutations cannot leak into the global default; brand-new first-run settings files gain `NODE_MANAGEMENT`, drop `ANCHOR:{}`, and match the editor key order. Existing `bots.json`/`general.settings.json` are not rewritten (merge output verified byte-identical to a pre-refactor baseline). Tests: 295/295 files passed; new `tests/test_bot_defaults_characterization.ts`, `tests/test_bot_defaults_parity.ts`, and a claw delegation test.
|
|
24
|
+
|
|
25
|
+
- **Feat(bot-editor)**: manage market-adapter whitelist flags from the editor — the per-bot Price/Weight/Range flags (`ama`/`dynamicWeight`/`asymmetricBounds`) previously required running the `dexbot white` script with the correct `botKey`; a wrong key silently wrote a useless entry and the script remained the only way to flip a single flag. New `6) Adapter` section in `modules/account_bots.ts` stages the three flags and commits them only after `bots.json` saved (migrating the entry on rename); `parseBooleanInput` fixes `askBoolean` parsing `'true'` as false and accepting any garbage (`y`/`yes`/`true`, `n`/`no`/`false`, Enter keeps current). `modules/market_adapter_whitelist.ts` gains `setWhitelistFlags()`/`renameWhitelistEntry()` read-modify-write helpers — an occupied key refuses the rename, a malformed file aborts instead of clobbering, legacy array-form entries are preserved, and keys are written sorted. Docs now point to `dexbot bot` -> `6) Adapter`. New `tests/test_account_bots_adapter.ts` (boolean parsing, flag writes, rename/collision, legacy and malformed-file coverage); `npm test` 293 passed / 0 failed, `verify:browser-bundle` 39/39, `tsc --noEmit` clean. Risk: writes touch the same file `dexbot white` owned; unchanged flags are never rewritten.
|
|
26
|
+
|
|
27
|
+
- **Feat(market-adapter)**: log-symmetric range-scaling tilt — range scaling previously widened the trend side by `(1+a)` but tightened the opposite side by `(1-a)`, which in log space made the tighten ~40% more aggressive than the widen at the default cap of 0.333x, collapsing the non-trend side toward center. Both bounds now scale by the same factor (down `1/(1+a)`, up `(1+a)`), so `delta log = +/-ln(1+a)` exactly and total log-width (slot count) is preserved while only the band's geometric center translates toward the trend; the widened side is unchanged and the tightened side is now the reciprocal rather than the linear complement. The geometric safe-clamp is mirrored to `base* - 1` so the shifted tightened bound still contains the fixed AMA center (still required: the reciprocal prevents reaching zero, not crossing center). `market_adapter/core/asymmetric_bounds.ts`, `modules/constants.ts`, `market_adapter/README.md`, and tests updated; `tests/test_market_adapter_service.ts` now sets `process.exitCode` on failure so its catch no longer masks a real failure. Behavioral change: with range scaling enabled the non-trend bound sits further from center (a 1.55x base at full tilt lands at +16.5% instead of +3.9%). The live grid build, adapter metrics, analyze-orders display, and the TradingView chart (embedded canonical source) all share this function.
|
|
28
|
+
|
|
29
|
+
- **Docs**: sync documentation index and linked docs with runtime behavior — `docs/README.md` bullets still described pre-1.6.4 behavior (batch cap is `gapSlots+1`, log rotation is 1.1GB total budget / 10 files, the `maxPages` warning is debug-level, duplicate detection is exact slot-price); `docs/architecture.md`'s batcher diagram/table said default cap 4 after 1f2fd7ae raised it to `gapSlots+1`; `docs/GRID_RECONCILE.md` Phase-1 duplicate detection described the removed 5x fuzzy matcher (code uses `priceSlotEqual` exact equality); `docs/FUND_MOVEMENT_AND_ACCOUNTING.md` dust partials are cancelled immediately on detection, not marked for consolidation; `docs/EVOLUTION.md` footer/stats refreshed to repo HEAD. Docs-only.
|
|
30
|
+
|
|
31
|
+
- **Feat(analysis)**: add an `ema` knob to the dynamic-weight chart for AMA input smoothing — the AMA slope channel had no post-filter besides the nz% dead-band, so there was no way to research how much additional slope smoothing (peak trimming, fewer sign flips) improves weight stability before touching live code. The dynamic-weight chart gains an `ema` slider (0-32 bars, 0 = off = previous behavior) that EMA-filters the AMA input before the slope; the clip-percentile pool and canonical `computeDynamicWeightSeries` offsets derive from it, so one source feeds display, clipping, and pipeline. Panel 2 gains a gray dashed Raw fraction-per-mille reference line and legend entry; the knob participates in `SLIDER_RANGES` clamps, copy/paste (`amaEmaSpan`), and init form-restore. `analyze_dynamic_weight` gains `--ema` (absent/NaN/out-of-range all resolve to the clamped default). `DYNAMIC_WEIGHT_RESEARCH` documents the flag row, knob table row, span value table, tuning interactions, and the measured effect on ~8.7k hourly bars (std -3% / peaks -22% / crossings 86 -> 74 at span 9). Research only; default ema = 0 reproduces prior output, the live market adapter and shared `dynamic_weight_series` module are untouched.
|
|
32
|
+
|
|
33
|
+
- **Feat(cli)**: add `dexbot dw` and centralize the chart-command pipeline — `dexbot dw` renders the dynamic-weight research chart through the same fetch pipeline as `dexbot tv`, but the pipeline lived in a tv-branded module with the exporter hardcoded. New `scripts/chart_command.ts` holds the shared pipeline (bot/pool-id/pair target resolution, `--feed`/`--pool`/`--book` routing with pool-first orderbook fallback, MPA/prediction-market guards, cached 1-month candle chunks, temp-JSON handoff) plus a `RENDERERS` registry (analyzer path, exporter label, title kind, usage blurb) so both commands (and future chart commands) are a table row apart; `scripts/tv.ts` and new `scripts/dw.ts` are thin entries with per-command error labels (loading them never triggers a run, test-asserted). Explicit `--ama-er/fast/slow-period` forwarding to the analyzer was dropped — both renderers resolve `resolveAmaConfig()` from `--bot-key` themselves, which also stops silently dropping the 4th field (`erSmoothPeriod`); verified output-identical for tv, and tv stays intentionally 3-param AMA. `analysis/analyze_dynamic_weight.ts` gains `--title` and a clickable `file://` save link; `dexbot.ts` registers `dw` in `CLI_COMMANDS`/`HELP_OWNING_COMMANDS` and the shared tv/dw dispatch (advanced-only, root help/README unchanged; documented in `scripts/README.md`, `analysis/README.md`, `analysis/trend_detection/DYNAMIC_WEIGHT_RESEARCH.md`). Month flags: `--month` canonical (default 3), `--months` a pure alias, invalid values always report `--month`. New `tests/test_dw_cli.ts`; `test_tv_feed_routing` now imports `scripts/chart_command`.
|
|
34
|
+
|
|
35
|
+
### 2026-09-22
|
|
36
|
+
|
|
37
|
+
- **Feat(log)**: tag RMS structural-divergence logs with `[RMS]` and show the threshold — the RMS reset line logged raw decimal metrics via `formatPrice6` (`buy=0.162000`) with no threshold context, so operators could not tell which level fired or which side breached. `resolveRmsThresholdPct()` is extracted as the single source of truth for the `GRID_COMPARISON.RMS_PERCENTAGE` override chain (previously duplicated inline in `compareGrids`) and surfaced as `thresholdPct` through `compareGrids`/`monitorDivergence`: `modules/order/grid.ts` emits a per-side `[RMS]` debug line (metric vs threshold -> TRIGGER-RESYNC/no trigger) each tick plus a "checks disabled" variant when the threshold is 0, and `modules/dexbot_maintenance_runtime.ts` now prefixes the reset line with `[RMS]` and formats it as 4 significant digits (`buy=16.20%`) with threshold, breaching sides, and the resync reason; the failure warn is also tagged. The threshold check itself is unchanged (same override chain, same percent->decimal scaling), but log line format changes, so log scrapers keyed on the old literals must be updated (no legacy format retained). `GRID_RECALCULATION.md` signatures/examples and the `LOGGING.md` `[RMS]` prefix row updated (including the threshold=0 debug variant). Also fixes a red test left by f859941e (`tests/test_market_adapter_log_format.ts` now expects `asymCap=33%`).
|
|
38
|
+
|
|
39
|
+
- **Fix(config)**: lower the default asymmetric bounds max factor to 0.333 — `ASYMMETRIC_BOUNDS_MAX_ASYMMETRY_FACTOR` moves 0.35 -> 0.333 so the slope-derived grid bound tilt is less aggressive. At the tight end of the supported span range (1.55x) full tilt previously pinched the tightened side to ~0.75% from the AMA center; 0.333 keeps ~3.3%, leaving the narrowing-side slot guard (`minScaleSlots`) more room before it overrides the tilt. Saturated band width moves from 87.75% to 88.91% of the symmetric base; wide 2x spans are effectively unchanged. The TradingView chart fallback and README defaults are synced to `modules/constants.ts`; local overrides in `profiles/general.settings.json` still take precedence at runtime.
|
|
40
|
+
|
|
41
|
+
- **Style(ui)**: normalize bot editor separators — group related fields with `|` in the `dexbot bot` editor summary and keep within-group fields comma-separated, matching the Funding line style: Identity (Name/Account grouped, Active/DryRun comma), Price (Range/Start+Pool/GridPrice grouped with `|`), Grid (Weights group `|`, Incr/Spread comma), Funding (Sell/Buy comma, Orders/Reserve grouped with `|`). Presentational only (`modules/account_bots.ts`).
|
|
42
|
+
|
|
43
|
+
- **Feat(tradingview)**: step the AMA ER stepper by 10 per click — the ER period defaults to 781, so the +/-1 per-click stepper (and its held-repeat) made any meaningful adjustment painfully slow. The step is now 10 (floor stays 1, integer rounding); other steppers (`sma-period`, `vwap-bars`, `fast`, `slow`) are unchanged (`analysis/tradingview/tradingview_uplot_chart_generator.ts`).
|
|
44
|
+
|
|
45
|
+
## [1.6.4] - 2026-09-22 - Fund-Driven Spread Correction, GapSlots+1 Batch Cap, Analysis Shared Modules
|
|
46
|
+
|
|
47
|
+
### 2026-09-21
|
|
48
|
+
|
|
49
|
+
- **Fix(grid)**: include VIRTUAL orders in RMS structural divergence — `compareGrids()` filtered each side's RMS metric to ACTIVE orders only, so persisted-vs-ideal drift in VIRTUAL slots was invisible. VIRTUAL slots carry the planned reservation for unplaced rail slots (`funds.virtual`), and Available = ChainFree − Virtual − fees, so an over-reserved or stale virtual pins spending power without ever tripping a structural check. `filterForRms` now covers ACTIVE + VIRTUAL (PARTIAL/SPREAD stay excluded); ideal sizing already covers every slot of the side, so no other logic changes. A side whose only divergence is in VIRTUAL reservations now yields an RMS metric and can trigger the `rms_structural_grid_resync` full resync; the 14.3% threshold is unchanged. Docs: divergence-filter wording corrected in `docs/architecture.md` and `docs/GRID_RECALCULATION.md` (`modules/order/grid.ts`, `tests/test_grid_comparison.ts`; full suite 291 pass / 0 fail).
|
|
50
|
+
|
|
51
|
+
- **Feat(spread)**: make spread correction purely fund-driven with a balance refresh fallback — `prepareSpreadCorrectionOrders` / `determineOrderSideByFunds` shrank resting orders to manufacture budget (self-funded tail recycling plus a generic redistribution donor loop). Those paths moved inventory within a rail and were gamed by stale-size snapshots: a donor's "current" size could lag a fill, so the recovered budget was phantom and the grid churned instead of healing. All shrink paths removed: corrections are funded only by free available/chainFree; when no side has funds the check returns `fundsExhausted` and the maintenance runtime refreshes account totals + open orders on the next tick via `getTargetedSyncReason` (a stale zero balance is re-read, not met with inventory recycling). New `tests/test_spread_pure_fund_driven.ts` (zero free → no side/create/shrink; funded → spend ≤ free and top-up only); SGP-6 now expects a skip (`modules/order/grid.ts`, `modules/dexbot_maintenance_runtime.ts`, `tests/test_targeted_drift_reconcile.ts`).
|
|
52
|
+
|
|
53
|
+
- **Fix(sync)**: stop false sync-lock "disappeared" logs and correct shadow-lock metric — `_doSyncFromOpenOrders` collected slot ids and chain order ids into `orderIdsToLock`, then re-verified each with `mgr.orders.has(id)`. `mgr.orders` is keyed by slot id only, so every placed order's chain id failed the check and was logged as "disappeared between collection and locking" (~40 lines per sync, 3,461 in one production log) — phantom lines once mistaken for a COW race during a fill-storm investigation, and chain ids were silently dropped from the lock set. Collection and locking are synchronous, so the re-verification guarded an impossible window; the full collected set is now locked. `shadowOrderIds` aliases one order under two keys, so `size` double-counted live orders; new `OrderManager.getActiveShadowLockCount()` feeds the `shadowLocks` / `shadowLocksActive` metrics. New `tests/test_sync_lock_id_verification.ts` (`modules/order/sync_engine.ts`, `modules/order/manager.ts`, `modules/dexbot_maintenance_runtime.ts`).
|
|
54
|
+
|
|
55
|
+
- **Feat(batch)**: raise fill/broadcast batch cap to gapSlots + 1 — `_getGapSlotBatchSize()`, the single source of truth for the fill-batch chunk size and the per-broadcast op cap, now returns the resolved gap-slot count + 1 instead of the count itself, giving one slot of headroom so a queue or operation set that previously spilled into an extra sequential broadcast chunk completes in a single cycle/transaction (1..gapSlots+1 fills → one unified batch; the non-finite/non-positive fallback still returns 1). Docs, constants/cow-runtime comments, and the batch-sizing tests updated to the new contract (`modules/dexbot_class.ts`, `modules/constants.ts`, `modules/dexbot_cow_runtime.ts`, `tests/test_cow_ops_per_broadcast.ts`, `tests/test_fill_batch_chunking.ts`, `tests/sim_batching.ts`).
|
|
56
|
+
|
|
57
|
+
### 2026-09-20
|
|
58
|
+
|
|
59
|
+
- **Docs(invariant)**: make grid-price invariant doc a fully present-tense contract — drops the mixed-state "What is still open" section (the blocking check has seen live traffic: 75 judgeable checks, 0 violations across four live bot logs), folds each item into its owning section, documents `runFinalPivotGate` as the seventh gate, adds a key-constants table, and cross-links the doc from `LIFECYCLE.md`, `COW_INVARIANTS.md` (INV-GRID-004), `GRID_RECONCILE.md`, `tests/README.md`, and `docs/README.md`; repoints dead `CONSOLIDATED_ORPHAN_FIX_SUMMARY.md` references in `CHANGELOG.md` to the retrospective. Docs-only, no code or test behavior (`docs/GRID_PRICE_INVARIANT.md`, `docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md`).
|
|
60
|
+
|
|
61
|
+
- **Refactor(code)**: purge dead code and internal-only exports across modules — drops the unreachable `processed_transaction` serializer chain in `bitshares-native/serial/operations.ts` (export list 98 → 17 names), the unused `CliColors` type, the never-read `_recentlyRotatedOrderIds` field, plus 56 internal-only exports across account/launcher/order/credential/crypto modules. Symbol visibility only, no behavioral impact (`npx tsc --noEmit` clean; `npm test` 290 pass, 0 fail).
|
|
62
|
+
|
|
63
|
+
- **Docs(engine)**: consolidate order-engine docs and move legacy narrative to the history hubs — replaces `docs/CONSOLIDATED_ORPHAN_FIX_SUMMARY.md` with the synthesis hub `docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md` (§0–§8 numbering preserved: code comments and tests cite it), folds the grid-price-invariant history into retrospective Appendix A, and refreshes drifted refs/symbols across `COW_INVARIANTS.md`, `GRID_RECONCILE.md`, `LIFECYCLE.md`, `FUND_MOVEMENT_AND_ACCOUNTING.md`, and `architecture.md`. History (COW three-era/build-step narrative, memory-only tracking) moves into `docs/EVOLUTION.md`; docs/comment-only change, no runtime behavior.
|
|
64
|
+
|
|
65
|
+
### 2026-09-18
|
|
66
|
+
|
|
67
|
+
- **Fix(range)**: restore suizidal range threshold to 1.45x — reverts the 1.40 widening so sub-1.45x ranges flag red (suizidal) again and the tight orange zone is 1.45x–1.55x; single-sourced via `RANGE_QUALITY`, bot-editor legend and live range coloring only (`modules/constants.ts`). Trivial: TradingView toolbar tag shortened to `Offset` (tooltip keeps full name).
|
|
68
|
+
|
|
69
|
+
### 2026-09-16
|
|
70
|
+
|
|
71
|
+
- **Docs(onboarding)**: first-run troubleshooting — npm 12 install-script blocking + manual-build fallback, `dexbot` unknown-command diagnosis (failed link vs stale shell cache), `npm link` EACCES recovery, and pacman `-Syu` for Arch-derivative installs (`docs/BITSHARES_ONBOARDING.md`, `README.md`, `scripts/git-viewer.sh`). Docs-only.
|
|
72
|
+
|
|
73
|
+
- **Refactor(analysis)**: centralize account/node handling, drop `--node` flags — new `analysis/chain_pool.ts` (single read-only chain entry point), `analysis/account_resolver.ts` (single preferredAccount/override decision tree), and `analysis/fills_source.ts` (shared asset-precision table + paginated fill fetch); `trade_profitability.ts` and `grid_correction_check.ts` use the shared modules. Name → accountId resolution always goes through the full built-in pool; breaks anyone passing the undocumented `--node` flag; no production runtime path touched. New `tests/test_analysis_account_resolver.ts`; full suite 290 pass, 0 fail, 15 live skipped.
|
|
74
|
+
|
|
75
|
+
- **Feat(analysis)**: window-aware annualisation of profitability metrics — Sharpe/Sortino now bin the queried window into whole periods (daily for ≥3-day windows, else hourly), zero-filled so flat periods count as 0 PnL, with sample (n−1) variance and √periods-per-year scaling; Sharpe prints with Lo-2002 estimation error, Sortino returns Infinity with no losing periods; new "Projected net PnL" (scored net / scored days × 365); trailing partial periods excluded from ratios, projection, and activity rates via one shared predicate (`analysis/trade_profitability.ts`, `tests/test_trade_profitability_fees.ts`, `analysis/README.md`).
|
|
76
|
+
|
|
77
|
+
- **Feat(charts)**: inline vendored uPlot into generated chart HTML — every generated chart embeds the vendored uPlot JS+CSS via `uplotInlineTags()`, so each export is a single self-contained document rendering anywhere with no CDN, install, or sibling-dir dependency (~53KB per chart; tests assert the banner is present and no `../uplot/` reference remains).
|
|
78
|
+
|
|
79
|
+
- **Fix(update)**: don't fake pm2 process list or claim restarts that failed — when `pm2 jlist` fails the selective restart step no longer substitutes config-active bots for the process list, and `restarted` is set only when at least one `pm2 restart` succeeds, so the monolithic auto-start fallback and the manual-start notice fire correctly on installs without pm2 (`scripts/update.ts`).
|
|
80
|
+
|
|
5
81
|
## [1.6.3] - 2026-09-14 - Correction-Queue Staleness Guard, Grid-Checker Price Epochs, Final Pivot Gate
|
|
6
82
|
|
|
7
83
|
### 2026-09-15
|
|
@@ -18,7 +94,7 @@ All notable changes to this project will be documented in this file.
|
|
|
18
94
|
|
|
19
95
|
### 2026-09-14
|
|
20
96
|
|
|
21
|
-
- **Fix(grid)**: enforce grid-price invariant at emission sites + escalate persistent corruption and stranded holds — the engine had two sources of truth for a slot's price — the genesis ladder (`priceForSlot(idx, genesis)`) and the mutable `slot.price` field — and every violation was the second winning over the first. Five mechanisms fed it: orphan adoption overwrote the slot's identity price (S1), a pre-broadcast "freshness" step re-substituted that corrupted price at debug level (S2), guards checked range membership but never grid membership (S3), the fill-guard pivot was written from unvalidated fill prices (S4), and guard bypasses skipped even that (S5). New `checkGridPriceInvariant` / `reportGridPriceInvariant` (`modules/order/utils/order.ts`) require the emitted price for a slot to equal its genesis level (fail-open on everything unjudgeable: no genesis, unparseable id, out-of-ladder index, non-finite price, checker error), enforced blocking at all six emission sites: CREATE, UPDATE (rotation), CREATE-FALLBACK (`modules/dexbot_cow_runtime.ts`); RECONCILE-CREATE, RECONCILE-UPDATE, STARTUP-CREATE (`modules/order/grid_reconcile_internal.ts`). Rotation UPDATE derives the emitted price from the destination slot's genesis level (`deriveRotationPrice`) instead of trusting the planner's carried `newPrice`; both pre-broadcast price substitutions removed (drift reported at warn, never adopted); legacy orphan adoption no longer writes `chainOrder.price` into `slot.price` and gains a rail guard; materialize path derives slot price (and side) from the ladder; `loadGrid` repairs a mismatched slot price from the genesis ladder in both validation modes; fill-guard pivot validated onto the ladder (`resolveOnGridPivot`: near-ladder pivots snap, far off-ladder fail open and are counted as `pivotOffGrid`). Persistent invariant rejection escalates after `GRID_PRICE_INVARIANT_RESYNC_THRESHOLD` consecutive rejecting batches per slot (bot-scoped streak, reset on clean check, 15m cooldown) to `requestStructuralGridResync('grid-price-invariant-violation')`. Out-of-bounds policy: hold and surface — grid geometry is not invalidated by the market leaving it (the removed 5% placement gate is documented as a design deadlock, not a tuning problem). New `docs/GRID_PRICE_INVARIANT.md` plus `docs/README.md` index entry and `CONSOLIDATED_ORPHAN_FIX_SUMMARY.md` status refresh. Tests: new `tests/test_grid_price_invariant_guard.ts` (GPI-001..015) and `tests/test_grid_price_invariant_wiring.ts` (GPI-WIRE-001..008) plus `tests/test_startup_and_guard_log_hygiene.ts` (GPI-LOG-001); every behavioral fix mutation-tested (11 escalation/guard mutations verified); audited over 1,153 slots across five geometries with zero rejections; full suite 282/282 pass (`modules/order/utils/order.ts`, `modules/dexbot_cow_runtime.ts`, `modules/order/grid_reconcile_internal.ts`, `modules/order/grid.ts`).
|
|
97
|
+
- **Fix(grid)**: enforce grid-price invariant at emission sites + escalate persistent corruption and stranded holds — the engine had two sources of truth for a slot's price — the genesis ladder (`priceForSlot(idx, genesis)`) and the mutable `slot.price` field — and every violation was the second winning over the first. Five mechanisms fed it: orphan adoption overwrote the slot's identity price (S1), a pre-broadcast "freshness" step re-substituted that corrupted price at debug level (S2), guards checked range membership but never grid membership (S3), the fill-guard pivot was written from unvalidated fill prices (S4), and guard bypasses skipped even that (S5). New `checkGridPriceInvariant` / `reportGridPriceInvariant` (`modules/order/utils/order.ts`) require the emitted price for a slot to equal its genesis level (fail-open on everything unjudgeable: no genesis, unparseable id, out-of-ladder index, non-finite price, checker error), enforced blocking at all six emission sites: CREATE, UPDATE (rotation), CREATE-FALLBACK (`modules/dexbot_cow_runtime.ts`); RECONCILE-CREATE, RECONCILE-UPDATE, STARTUP-CREATE (`modules/order/grid_reconcile_internal.ts`). Rotation UPDATE derives the emitted price from the destination slot's genesis level (`deriveRotationPrice`) instead of trusting the planner's carried `newPrice`; both pre-broadcast price substitutions removed (drift reported at warn, never adopted); legacy orphan adoption no longer writes `chainOrder.price` into `slot.price` and gains a rail guard; materialize path derives slot price (and side) from the ladder; `loadGrid` repairs a mismatched slot price from the genesis ladder in both validation modes; fill-guard pivot validated onto the ladder (`resolveOnGridPivot`: near-ladder pivots snap, far off-ladder fail open and are counted as `pivotOffGrid`). Persistent invariant rejection escalates after `GRID_PRICE_INVARIANT_RESYNC_THRESHOLD` consecutive rejecting batches per slot (bot-scoped streak, reset on clean check, 15m cooldown) to `requestStructuralGridResync('grid-price-invariant-violation')`. Out-of-bounds policy: hold and surface — grid geometry is not invalidated by the market leaving it (the removed 5% placement gate is documented as a design deadlock, not a tuning problem). New `docs/GRID_PRICE_INVARIANT.md` plus `docs/README.md` index entry and a `CONSOLIDATED_ORPHAN_FIX_SUMMARY.md` status refresh (now absorbed into `docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md`). Tests: new `tests/test_grid_price_invariant_guard.ts` (GPI-001..015) and `tests/test_grid_price_invariant_wiring.ts` (GPI-WIRE-001..008) plus `tests/test_startup_and_guard_log_hygiene.ts` (GPI-LOG-001); every behavioral fix mutation-tested (11 escalation/guard mutations verified); audited over 1,153 slots across five geometries with zero rejections; full suite 282/282 pass (`modules/order/utils/order.ts`, `modules/dexbot_cow_runtime.ts`, `modules/order/grid_reconcile_internal.ts`, `modules/order/grid.ts`).
|
|
22
98
|
|
|
23
99
|
- **Fix(fill)**: retire deferred-retry counter on queue settle + pin COW finally drain — the fill-consumer deferral counter (`_deferredFillRetryWaits`) only retired when a pending retry timer fired against an already-empty queue, so a queue drained by any other path left the counter elevated and the deferral log cadence stuck at warn (every 12th defer) after the backlog cleared. The counter now retires in `consumeFillQueue` at both settle points (top-of-run empty early-return and tail-after-drain); `_deferredFillRetryTimer` declared/initialized on the bot class and cleared on shutdown so the unref'd timer cannot leak across lifecycle restarts. New RETRY-007 pins the counter reset on a clean drain (7 → 0 on settle); RETRY-006 pins the COW batch finally (injected `_ensureCredentialDaemonWritable` throw after `_batchInFlight++` / `_cowBroadcastInFlight` asserts the finally releases both and reschedules the fill consumer exactly once; the COW layer converts the throw into a handled `{executed:false}` abort via `_handleBatchHardAbort`) (`modules/dexbot_fill_runtime.ts`, `modules/dexbot_class.ts`, `tests/test_fill_defer_retry.ts`; neighbors green, `tsc --noEmit` clean).
|
|
24
100
|
|
|
@@ -38,7 +114,7 @@ All notable changes to this project will be documented in this file.
|
|
|
38
114
|
|
|
39
115
|
### 2026-09-13
|
|
40
116
|
|
|
41
|
-
- **Fix(recovery)**: give the GRID-PRICE-INVARIANT guard and the deferred-hold policy a self-healing exit — both had the same shape (state the guard refuses to emit, but nothing repairs it) and both now escalate to the existing structural resync rather than running forever. A slot rejected as off-grid is skipped and warned, which is correct for a one-off, but the recurring planner carries the slot's price straight from `manager.orders`, so an in-process corruption is re-planned, re-rejected and re-warned every cycle with nothing able to heal it short of a restart — the slot is dead while the bot looks healthy, and the repeated warns train operators to ignore them. The guard now counts **consecutive** rejecting batches per slot (bot-scoped — the monolithic runtime (`dexbot.ts`, the `dexbot` bin) builds EVERY active bot in one process, so a module-level streak would let one bot's rejections push another to the threshold on its FIRST rejection and fire a spurious resync on a healthy bot, and the count also must not outlive a repair; GPI-WIRE-009 pins the cross-bot case) and at `TIMING.GRID_PRICE_INVARIANT_RESYNC_THRESHOLD` (3) fires `requestStructuralGridResync('grid-price-invariant-violation', {slotId, expected, actual, site, streak})`; a clean check clears the streak so escalation means "rejected N consecutive batches", not "N times ever", and `GRID_PRICE_INVARIANT_RESYNC_COOLDOWN_MS` (15m) bounds repeats. Healing in place at rejection time is deliberately NOT done — silently rewriting `slot.price` would erase the diagnostic signal distinguishing the four corruption sources. Separately, out-of-rail orphans hold locked funds and are never auto-cancelled per cycle (correct: cancelling on ambiguous evidence is irreversible), but "held indefinitely" had no exit; a hold whose signature is unchanged for `DEFERRED_HOLD_ESCALATE_MS` (24h) now escalates at `error` to `requestStructuralGridResync('deferred-hold-stale')`, which is safe because the full reset's reconcile is update-first (unmatched orders are price-updated onto rail slots, only true surplus cancelled), so funds are released without inventing a new cancellation policy. Hold age is tracked **per stranded order** (bot-scoped map keyed `id@price/size:reason`), because two simpler clocks were wrong: `manager._lastUnmatchedChainOrdersAt` is unconditionally refreshed on every sync observing any unmatched order, so it records "when we last looked" and an age gate on it could never fire (HOLD-007 pins this); and the whole-held-set signature clock looked correct but was **reset by unrelated churn** — the signature includes every entry's reason, so an unrelated hold flapping in and out restarted the clock every cycle and starved a genuinely stranded order of escalation forever (HOLD-010 pins this; a 6-hourly flap was simulated reporting a clock reset on every tick). A third defect sat in the same path: the signature-change branch `return`ed before the escalation call, so any churn skipped escalation entirely — escalation now runs on both branches, since a signature change is a reason to re-log, not to stop evaluating age. Escalation triggers only on genuinely **stranded** reasons via a narrow allow-list (`isStrandedHoldOrder`: `out-of-rail-deferred`, `out-of-grid-deferred`) rather than the broad `-deferred` non-blocking filter, because a resync cannot end a broadcast region or re-evaluate an uncommitted boundary; `broadcast-active-deferred`, `boundary-hold-trailing-market`, `boundary-unknown-deferred` and `held-plan-unchanged-deferred` are excluded (HOLD-011 pins this), and the allow-list fails closed so a future transient reason is excluded by default. Both escalations reuse the existing debounced, batch-in-flight-aware resync path; a second repair mechanism would duplicate tested machinery. New GPI-WIRE-006..009 (`tests/test_grid_price_invariant_wiring.ts`) and HOLD-006..011 (`tests/test_hold_and_center_guards.ts`); mutation-verified that removing the escalation call, removing the streak reset, disabling the cooldown, removing the hold escalation, re-gating escalation behind the signature early return, reading the hold age from the whole-set clock, and replacing the stranded allow-list with the broad `-deferred` filter are each caught (`modules/constants.ts`, `modules/dexbot_cow_runtime.ts`, `modules/dexbot_maintenance_runtime.ts`, `docs/GRID_PRICE_INVARIANT.md`, `docs/CONSOLIDATED_ORPHAN_FIX_SUMMARY.md`).
|
|
117
|
+
- **Fix(recovery)**: give the GRID-PRICE-INVARIANT guard and the deferred-hold policy a self-healing exit — both had the same shape (state the guard refuses to emit, but nothing repairs it) and both now escalate to the existing structural resync rather than running forever. A slot rejected as off-grid is skipped and warned, which is correct for a one-off, but the recurring planner carries the slot's price straight from `manager.orders`, so an in-process corruption is re-planned, re-rejected and re-warned every cycle with nothing able to heal it short of a restart — the slot is dead while the bot looks healthy, and the repeated warns train operators to ignore them. The guard now counts **consecutive** rejecting batches per slot (bot-scoped — the monolithic runtime (`dexbot.ts`, the `dexbot` bin) builds EVERY active bot in one process, so a module-level streak would let one bot's rejections push another to the threshold on its FIRST rejection and fire a spurious resync on a healthy bot, and the count also must not outlive a repair; GPI-WIRE-009 pins the cross-bot case) and at `TIMING.GRID_PRICE_INVARIANT_RESYNC_THRESHOLD` (3) fires `requestStructuralGridResync('grid-price-invariant-violation', {slotId, expected, actual, site, streak})`; a clean check clears the streak so escalation means "rejected N consecutive batches", not "N times ever", and `GRID_PRICE_INVARIANT_RESYNC_COOLDOWN_MS` (15m) bounds repeats. Healing in place at rejection time is deliberately NOT done — silently rewriting `slot.price` would erase the diagnostic signal distinguishing the four corruption sources. Separately, out-of-rail orphans hold locked funds and are never auto-cancelled per cycle (correct: cancelling on ambiguous evidence is irreversible), but "held indefinitely" had no exit; a hold whose signature is unchanged for `DEFERRED_HOLD_ESCALATE_MS` (24h) now escalates at `error` to `requestStructuralGridResync('deferred-hold-stale')`, which is safe because the full reset's reconcile is update-first (unmatched orders are price-updated onto rail slots, only true surplus cancelled), so funds are released without inventing a new cancellation policy. Hold age is tracked **per stranded order** (bot-scoped map keyed `id@price/size:reason`), because two simpler clocks were wrong: `manager._lastUnmatchedChainOrdersAt` is unconditionally refreshed on every sync observing any unmatched order, so it records "when we last looked" and an age gate on it could never fire (HOLD-007 pins this); and the whole-held-set signature clock looked correct but was **reset by unrelated churn** — the signature includes every entry's reason, so an unrelated hold flapping in and out restarted the clock every cycle and starved a genuinely stranded order of escalation forever (HOLD-010 pins this; a 6-hourly flap was simulated reporting a clock reset on every tick). A third defect sat in the same path: the signature-change branch `return`ed before the escalation call, so any churn skipped escalation entirely — escalation now runs on both branches, since a signature change is a reason to re-log, not to stop evaluating age. Escalation triggers only on genuinely **stranded** reasons via a narrow allow-list (`isStrandedHoldOrder`: `out-of-rail-deferred`, `out-of-grid-deferred`) rather than the broad `-deferred` non-blocking filter, because a resync cannot end a broadcast region or re-evaluate an uncommitted boundary; `broadcast-active-deferred`, `boundary-hold-trailing-market`, `boundary-unknown-deferred` and `held-plan-unchanged-deferred` are excluded (HOLD-011 pins this), and the allow-list fails closed so a future transient reason is excluded by default. Both escalations reuse the existing debounced, batch-in-flight-aware resync path; a second repair mechanism would duplicate tested machinery. New GPI-WIRE-006..009 (`tests/test_grid_price_invariant_wiring.ts`) and HOLD-006..011 (`tests/test_hold_and_center_guards.ts`); mutation-verified that removing the escalation call, removing the streak reset, disabling the cooldown, removing the hold escalation, re-gating escalation behind the signature early return, reading the hold age from the whole-set clock, and replacing the stranded allow-list with the broad `-deferred` filter are each caught (`modules/constants.ts`, `modules/dexbot_cow_runtime.ts`, `modules/dexbot_maintenance_runtime.ts`, `docs/GRID_PRICE_INVARIANT.md`, `docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md` — successor of the then-cited `docs/CONSOLIDATED_ORPHAN_FIX_SUMMARY.md`).
|
|
42
118
|
|
|
43
119
|
- **Fix(shelf)**: exclude shelf orders from every grid count (issue #27 follow-up) — the four shelf follow-ups gated `reserveEdgeIdSet`, `matchedExcess`, and the geometric size recalc to `parseSlotIndex(id) !== null`, but three counters feeding the same decisions never got the gate, so a live fork-kept shelf (non-slot-N id, `ACTIVE` + `orderId`) still poisoned them via the fail-open `isSlotInRail` geometry. `_countActiveOnGrid` inflated `matchedOnGrid` (suppressing `neededSlots`/creates), `chainCount` in `_reconcileStartupSide` counted shelf-bound chain orders (fabricating a `chain - target` surplus that cancelled real window orders), `countLiveGridOrders` masked window shortfalls in targeted sync, and `_getOnChainOrders` plus the spread `buyCount`/`sellCount` masked `oneSideEmpty`. All now apply the same slot-N gate (no-op on grids that only mint slot-N ids); shelf stays in the geometric-recalc denominator by design (conservative sizing direction). `_pickVirtualSlotsToActivate` and the `getInitialOrdersToActivate` window picks are gated too so a `VIRTUAL` shelf can never consume window activation budget (`modules/order/grid_reconcile_internal.ts`, `modules/dexbot_maintenance_runtime.ts`, `modules/order/grid.ts`, `modules/order/manager.ts`; `tests/test_reserve_orders.ts` shelf expectations corrected to the fixed behavior — chain 12 grid vs target 8 plans 4 rail cancels, grid count converges to 8 with 3 shelf surviving alongside — plus a new block pinning `_countActiveOnGrid`, startup create non-suppression, targeted-sync `buy 5/8` firing through shelf, and slot-N-only activation picks; `tests/test_startup_decision.ts`, `tests/test_resync_balance_fix.ts`, `tests/test_resync_duplicate_race.ts` fixtures migrated from ad-hoc ids to `slot-N` ids per the production invariant; full suite exit 0, zero failures).
|
|
44
120
|
|
package/README.md
CHANGED
|
@@ -65,8 +65,8 @@ Install Git:
|
|
|
65
65
|
sudo apt-get update
|
|
66
66
|
sudo apt-get install git
|
|
67
67
|
|
|
68
|
-
# Arch/Manjaro
|
|
69
|
-
sudo pacman -
|
|
68
|
+
# Arch/Manjaro/CachyOS
|
|
69
|
+
sudo pacman -Syu git
|
|
70
70
|
|
|
71
71
|
# Fedora/RHEL
|
|
72
72
|
sudo dnf install git
|
|
@@ -122,6 +122,10 @@ npm install
|
|
|
122
122
|
npm link
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
+
If `npm link` fails with `EACCES` or `dexbot` isn't found afterwards, see the
|
|
126
|
+
[first-run troubleshooting](docs/BITSHARES_ONBOARDING.md#troubleshooting-first-run-mistakes)
|
|
127
|
+
section in the onboarding guide.
|
|
128
|
+
|
|
125
129
|
### Where your data lives
|
|
126
130
|
|
|
127
131
|
Both installs use the same CLI and store all user state — keys, `bots.json`, logs — in `~/.config/dexbot2/profiles` (Windows: `%USERPROFILE%\.config\dexbot2\profiles`). State lives outside the repo/package tree, so it survives reinstalls and `npm update -g`. A source checkout that already contains a populated `profiles/` directory keeps using it. Override the location with `DEXBOT_PROFILE_ROOT` (see the [developer guide](docs/developer_guide.md)).
|
|
@@ -169,17 +173,18 @@ Keep the default settings first, and tune these:
|
|
|
169
173
|
on AMA. Pick a specific preset if desired: `"ama1"` is the fastest,
|
|
170
174
|
`"ama4"` the slowest, and `"ama"` uses the pair's default preset.
|
|
171
175
|
|
|
172
|
-
5. **
|
|
176
|
+
5. **Set the adapter flags** — `dexbot bot` → `2) Modify bot` → pick the bot
|
|
177
|
+
→ `6) Adapter`:
|
|
173
178
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
179
|
+
- `Price` — AMA pricing and live adapter writes (turn **on** for AMA bots;
|
|
180
|
+
without it the adapter only dry-runs)
|
|
181
|
+
- `Weight` — dynamic buy/sell weights (opt-in)
|
|
182
|
+
- `Range` — AMA-slope range scaling (opt-in)
|
|
177
183
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
To overwrite an existing bot, include `--bot <botKey>` with the desired flags.
|
|
184
|
+
The flags are stored per bot in `market_adapter_whitelist.json` in the
|
|
185
|
+
profiles directory and can be changed any time from the same editor
|
|
186
|
+
section. Boolean prompts accept `y`/`yes`/`true` and `n`/`no`/`false`;
|
|
187
|
+
Enter keeps the current value.
|
|
183
188
|
|
|
184
189
|
6. **Start DEXBot2** with `dexbot start`.
|
|
185
190
|
|
|
@@ -223,7 +228,7 @@ Global settings via `dexbot bot`, stored in `general.settings.json` in the profi
|
|
|
223
228
|
|
|
224
229
|
<details><summary><mark>Global settings reference (click to expand)</mark></summary>
|
|
225
230
|
|
|
226
|
-
- **Grid Health**: Grid
|
|
231
|
+
- **Grid Health**: Grid Funds Regeneration % (default `3%`), RMS Divergence Threshold % (default `14.3%`), AMA Δ Threshold % (default `1%`, AMA center move that triggers a grid reset), AMA-Slope Δ Threshold % (default `8%`, slope-delta trigger as a percentage of max AMA slope)
|
|
227
232
|
- **Order Recovery**: Partial Dust Threshold % (default `5%`), Dust Cancel Delay (default `30s`, `-1` = off, `0` = instant)
|
|
228
233
|
- **Node Configuration**: Node List (10 default public BitShares nodes), Health Check Interval (default `240 min`), Preferred Node (default `none`)
|
|
229
234
|
- **Log Level**: `debug`, `info`, `warn`, `error`, `critical`. Fine-grained category control via `LOGGING_CONFIG` (see [Logging](docs/LOGGING.md))
|
|
@@ -253,8 +258,7 @@ First-run details and common mistakes are covered in the [BitShares Onboarding T
|
|
|
253
258
|
|
|
254
259
|
```bash
|
|
255
260
|
dexbot key # Master password/keyring
|
|
256
|
-
dexbot bot # Interactive bot configurator
|
|
257
|
-
dexbot white # AMA whitelist; dynamic weights and range scaling off by default
|
|
261
|
+
dexbot bot # Interactive bot configurator (adapter Price/Weight/Range flags: 6) Adapter)
|
|
258
262
|
|
|
259
263
|
dexbot reset {all|<bot>} # Regenerate grid
|
|
260
264
|
dexbot disable {all|<bot>} # Disable bot in config
|
|
@@ -271,7 +275,7 @@ dexbot clear # Clear log files
|
|
|
271
275
|
dexbot default # Reset settings to defaults
|
|
272
276
|
```
|
|
273
277
|
|
|
274
|
-
##
|
|
278
|
+
## ⚙️ PM2 Process Management
|
|
275
279
|
|
|
276
280
|
PM2 is optional — `dexbot start` is the native solution.
|
|
277
281
|
|
|
@@ -313,12 +317,12 @@ Logs are written to `logs/` in the profiles directory in all modes: the monolith
|
|
|
313
317
|
- **[Docs Index](docs/README.md)** - Main documentation hub
|
|
314
318
|
- **[Architecture](docs/architecture.md)** - System design, fill processing pipeline, and testing strategy
|
|
315
319
|
- **[Lifecycle](docs/LIFECYCLE.md)** - End-to-end walkthrough: startup, fill-driven, and AMA-driven flows with diagrams
|
|
316
|
-
- **[Evolution Report](docs/EVOLUTION.md)** - Project timeline, architecture phases, and release history
|
|
317
|
-
- **[Developer Guide](docs/developer_guide.md)** - Development guide, environment variables, examples, and glossary
|
|
318
|
-
- **[Workflow](docs/WORKFLOW.md)** - Project workflow and contribution guide
|
|
319
|
-
- **[Fund Movement & Accounting](docs/FUND_MOVEMENT_AND_ACCOUNTING.md)** - Fund accounting, grid topology, and rotation mechanics
|
|
320
320
|
- **[Copy-on-Write Plan](docs/COPY_ON_WRITE_MASTER_PLAN.md)** - Copy-on-Write grid architecture
|
|
321
|
+
- **[Fund Movement & Accounting](docs/FUND_MOVEMENT_AND_ACCOUNTING.md)** - Fund accounting, grid topology, and rotation mechanics
|
|
321
322
|
- **[Claw API Boundary](claw/docs/AI_BOT_LIBRARY_API.md)** - Responsibility split between the AI layer and the DEXBot2 execution layer
|
|
323
|
+
- **[Developer Guide](docs/developer_guide.md)** - Development guide, environment variables, examples, and glossary
|
|
324
|
+
- **[Workflow](docs/WORKFLOW.md)** - Project workflow and contribution guide
|
|
325
|
+
- **[Evolution Report](docs/EVOLUTION.md)** - Project timeline, architecture phases, and release history
|
|
322
326
|
|
|
323
327
|
## 🤝 Contributing
|
|
324
328
|
|
package/analysis/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Tools that inspect DEXBot trading behavior and the market data it operates on. O
|
|
|
25
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>` |
|
|
26
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>` |
|
|
27
27
|
| [`tradingview/analyze_tradingview.ts`](#tradingview-chart-tradingviewanalyze_tradingviewts) | "Just give me a candle chart" | `dexbot tv <bot-key>` |
|
|
28
|
-
| [`analyze_dynamic_weight.ts`](#dynamic-weight-research-analyze_dynamic_weightts) | "Are buy/sell weights tuned for this regime?" | `
|
|
28
|
+
| [`analyze_dynamic_weight.ts`](#dynamic-weight-research-analyze_dynamic_weightts) | "Are buy/sell weights tuned for this regime?" | `dexbot dw <bot-key>` |
|
|
29
29
|
| [`analyze_volatility.ts`](#volatility-analyze_volatilityts) | "Both weights clipped too hard / not enough?" | `node dist/analysis/analyze_volatility.js --bot-key <bot-key>` |
|
|
30
30
|
| [`analyze_regime.ts`](#supporting-sub-signals) | "Is the trend/chaos gate too aggressive?" | `node dist/analysis/analyze_regime.js --bot-key <bot-key>` |
|
|
31
31
|
| [`analyze_kalman.ts`](#supporting-sub-signals) | "Is Kalman's contribution to the blend right?" | `node dist/analysis/analyze_kalman.js --bot-key <bot-key>` |
|
|
@@ -134,6 +134,8 @@ Fetches `fill_order` operations for a BitShares account from Kibana within a spe
|
|
|
134
134
|
|
|
135
135
|
**Pipeline:** Kibana fill query → on-chain asset precision resolution → buy/sell classification → chronological matching (sequential LIFO by default) → per-pair summary + optional per-match detail.
|
|
136
136
|
|
|
137
|
+
**Account resolution:** an account name (or a bare `1.2.x` ID) is resolved through the shared `account_resolver.ts` helper. When the name matches a bot in `profiles/bots.json`, the resolved ID is stamped onto that entry as `accountId`, so later runs resolve offline; `--refresh-account` forces a fresh lookup.
|
|
138
|
+
|
|
137
139
|
```bash
|
|
138
140
|
# Account by ID, last 7 days (default)
|
|
139
141
|
node dist/analysis/trade_profitability.js 1.2.123456
|
|
@@ -164,7 +166,6 @@ node dist/analysis/trade_profitability.js 1.2.123456 \
|
|
|
164
166
|
| `--asset <id>` | all | Filter to one base asset ID |
|
|
165
167
|
| `--lookup` | off | Legacy (no-op): account names always resolve automatically |
|
|
166
168
|
| `--refresh-account` | off | Force re-resolution and update the stored `accountId` |
|
|
167
|
-
| `--node <url>` | first healthy from built-in pool (10 nodes) | BitShares node for account + asset resolution |
|
|
168
169
|
| `--csv <file>` | — | Export chronologically sorted trade list |
|
|
169
170
|
| `--json <file>` | — | Export full analysis with per-pair PnL data |
|
|
170
171
|
| `--match-mode <mode>` | `sequential` | Matching mode: `sequential` (LIFO, default) or `fifo` |
|
|
@@ -177,8 +178,8 @@ node dist/analysis/trade_profitability.js 1.2.123456 \
|
|
|
177
178
|
**Asset precision handling:**
|
|
178
179
|
|
|
179
180
|
1. Assets listed in the static `ASSETS` table (BTS, TWENTIX, XBTSX.*, HONEST.*, IOB.*, etc.) resolve instantly.
|
|
180
|
-
2. Unknown assets are resolved on-chain via `get_assets`
|
|
181
|
-
3. If
|
|
181
|
+
2. Unknown assets are resolved on-chain via `get_assets` against the built-in node pool, with results cached at runtime.
|
|
182
|
+
3. If resolution fails or an asset is not found on chain, the affected fills are **skipped** with a warning (no abort).
|
|
182
183
|
|
|
183
184
|
**PnL methodology:**
|
|
184
185
|
|
|
@@ -206,9 +207,10 @@ node dist/analysis/trade_profitability.js 1.2.123456 \
|
|
|
206
207
|
| `Expectancy (gross)` | How much one trade is expected to earn before fees. Positive = edge exists. The `R` version normalises this by the average loss size (reports in R-multiples instead of BTS). The `net` version subtracts fees. |
|
|
207
208
|
| `Median R` | The middle R-multiple value (half of trades are above, half below). `>1R` / `>2R` = % of trades that earned more than 1× or 2× the average loss. `<-1R` = % that lost more than 1× the average loss. |
|
|
208
209
|
| `PnL distribution` | Median, P25, P75, Best, Worst — the centre, spread, and extremes of per-trade return %. Not annualised, just per cycle. |
|
|
209
|
-
| `Sharpe (ann)` |
|
|
210
|
-
| `Sortino (ann)` |
|
|
211
|
-
| `
|
|
210
|
+
| `Sharpe (ann)` | The window's net PnL per unit of volatility, annualised (`mean/std × √periods-per-year`). Binned daily for ≥ 3-day windows, hourly below; every period counts, flat ones as 0 PnL. Shown as `value ± estimation error [bin, n, confidence]` — short windows are low confidence, and only same-bin runs are comparable. Dimensionful (absolute PnL, not % returns). |
|
|
211
|
+
| `Sortino (ann)` | As Sharpe, but only losing periods feed the downside deviation. `∞` means the window had no losing periods. |
|
|
212
|
+
| `Projected net PnL` | Scored-window net PnL scaled linearly to a year (`÷ scored days × 365`) — same whole-period basis as Sharpe/Sortino, so a trailing partial period is excluded from both. A projection, not a forecast. |
|
|
213
|
+
| `Max Drawdown` | Largest peak-to-trough decline of the realised-PnL curve, in quote units (with the same decline as a % of peak cumulative profit). Realised only — open inventory isn't marked. |
|
|
212
214
|
| `Max Recovery Time` | Longest time (in days) from the deepest point of a drawdown back to a new equity high. |
|
|
213
215
|
| `Max Consecutive W/L` | Longest streak of winning or losing round-trips. Grouped by sell order, so one order covering multiple buy lots counts as one result. Grid bots naturally cluster wins during trends — streaks of 100-200 are not alarming. |
|
|
214
216
|
| `Avg hold time` | Average time (hours) between buying an asset and selling it. |
|
|
@@ -216,14 +218,14 @@ node dist/analysis/trade_profitability.js 1.2.123456 \
|
|
|
216
218
|
| `Sell orders filled` | Number of distinct sell orders that were filled in the period. |
|
|
217
219
|
| `Partial fills/order` | How many buy lots each sell order consumed (mean, median, max). For a grid bot: 2.0 median means half the orders clear 2 grid levels; 18 max means one big sweep. |
|
|
218
220
|
| `One-shot orders` | % of orders that matched exactly 1 buy lot. Low % = your grid is thick enough that orders routinely cover multiple levels. |
|
|
219
|
-
| `Fills/day` | Average matched lots per
|
|
220
|
-
| `Avg vol/day` | Average daily trading volume in the quote asset. |
|
|
221
|
+
| `Fills/day` | Average matched lots per scored day, on the same whole-period basis as the ratios above. Raw activity speed. |
|
|
222
|
+
| `Avg vol/day` | Average daily trading volume in the quote asset over that same scored window. |
|
|
221
223
|
|
|
222
224
|
</details>
|
|
223
225
|
|
|
224
226
|
### Grid Correction Check (`grid_correction_check.ts`)
|
|
225
227
|
|
|
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/
|
|
228
|
+
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/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md`.
|
|
227
229
|
|
|
228
230
|
**Pipeline:** Kibana `fill_order` query (paginated `search_after`) → on-chain asset precision resolution → buy/sell classification → chronological sort → per-order/price-epoch aggregation (partial fills at one price collapsed to weighted-average; repriced order lifetimes kept separate) → consecutive same-direction pair comparison → violation report with daily histogram.
|
|
229
231
|
|
|
@@ -253,7 +255,6 @@ Exit code `0` = pass, `2` = violations found, `1` = fatal error. Bot keys resolv
|
|
|
253
255
|
| `--account <id>` | bot `preferredAccount` | Override account ID or name |
|
|
254
256
|
| `--lookup` | off | Legacy (no-op): account names always resolve via BitShares node when no stored ID exists |
|
|
255
257
|
| `--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
258
|
| `--per-fill` | off | Check at fill granularity instead of per-order aggregated |
|
|
258
259
|
| `--include-cross-pair` | off | Also check consecutive fills across different pairs |
|
|
259
260
|
| `--tolerance <pct>` | `0` | Adverse price move (%) forgiven before flagging |
|
|
@@ -336,6 +337,13 @@ Two weight-tuning paths feed into the market adapter:
|
|
|
336
337
|
Interactive 4-panel chart for the asymmetric path: AMA slope plus Kalman confirmation, gated by Hurst Exponent and Permutation Entropy. Use this when tuning buy/sell weight bias, AMA slope offset behavior, and regime damping.
|
|
337
338
|
|
|
338
339
|
```bash
|
|
340
|
+
# Recommended one-step (advanced): bot, pool, or pair — fetches candles and renders,
|
|
341
|
+
# same pipeline as `dexbot tv`, default 3 months
|
|
342
|
+
dexbot dw <bot-key>
|
|
343
|
+
dexbot dw 133 --month 6
|
|
344
|
+
dexbot dw TOKENA/TOKENB --month 1
|
|
345
|
+
|
|
346
|
+
# Direct analyzer (own candle files, research knobs --alpha/--gain/--dw/--lb/--clip)
|
|
339
347
|
node dist/analysis/analyze_dynamic_weight.js --bot-key <bot-key>
|
|
340
348
|
|
|
341
349
|
# From LP candle file with custom parameters
|
|
@@ -485,7 +493,12 @@ Details: [bot_fitting/README.md](bot_fitting/README.md)
|
|
|
485
493
|
| `price_sources.ts` | Unified candle source abstraction (`json`, `market_adapter`) |
|
|
486
494
|
| `chart_utils.ts` | Shared chart rendering utilities |
|
|
487
495
|
| `math_utils.ts` | Shared math utilities |
|
|
488
|
-
| `bot_key_utils.ts` | Bot-key resolution
|
|
496
|
+
| `bot_key_utils.ts` | Bot-key resolution, candle file lookup, `accountId` persistence (`persistBotAccountId`) |
|
|
497
|
+
| `account_resolver.ts` | Account resolution for all tools: `preferredAccount` / `--account` → `1.2.x`, stamping the result into `profiles/bots.json` |
|
|
498
|
+
| `chain_pool.ts` | Ephemeral read-only chain client over the built-in node pool (account + asset lookups) |
|
|
499
|
+
| `fills_source.ts` | Shared `fill_order` Kibana fetch/query and the static asset-precision table + on-chain cache |
|
|
500
|
+
|
|
501
|
+
On-chain account and asset lookups in the fill-based tools go through `account_resolver.ts` / `fills_source.ts` (both built on `chain_pool.ts`): tool scripts must not open their own read-only clients or carry their own node list. The batch backfill `resolve_bot_accounts.ts` is the exception — it reuses the production chain client over one connection.
|
|
489
502
|
|
|
490
503
|
## npm Script Shortcuts
|
|
491
504
|
|
|
@@ -519,5 +532,5 @@ npm run ama:chart:lp-local -- --data market_adapter/data/lp/<pair>/lp_pool_<id>_
|
|
|
519
532
|
## Related Docs
|
|
520
533
|
|
|
521
534
|
- [Market Adapter](../market_adapter/README.md) — live AMA pricing, grid triggers, dynamic weights, and recalc triggers
|
|
522
|
-
- [
|
|
535
|
+
- [Order Engine Retrospective](../docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md) — orphan/gap-band root-cause plans; `grid_correction_check` is their regression gate
|
|
523
536
|
- [DEXBot2 Tuning Cheat Sheet](../claw/docs/DEXBOT2_TUNING_CHEAT_SHEET.md) — grid tuning reference for live bots
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SHARED ACCOUNT RESOLUTION (analysis)
|
|
5
|
+
*
|
|
6
|
+
* Central implementation of "preferredAccount / bare account reference →
|
|
7
|
+
* 1.2.x accountId" for every analysis tool, including stamping the resolved ID
|
|
8
|
+
* back into profiles/bots.json so later runs skip the chain lookup.
|
|
9
|
+
*
|
|
10
|
+
* trade_profitability.ts and grid_correction_check.ts used to each carry a
|
|
11
|
+
* private copy of this decision tree, of the chain lookup and of the node
|
|
12
|
+
* handling — all of it lives here now.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { findBotKeyByAccountRef, loadBotMeta, persistBotAccountId } from './bot_key_utils.js';
|
|
16
|
+
import { withReadOnlyClient } from './chain_pool.js';
|
|
17
|
+
|
|
18
|
+
const ACCOUNT_ID_RE = /^1\.2\.\d+$/;
|
|
19
|
+
|
|
20
|
+
/** Chain lookup seam: batch tools can reuse one connection across lookups. */
|
|
21
|
+
type NameLookup = (name: string) => Promise<string | null>;
|
|
22
|
+
|
|
23
|
+
/** Default lookup: ephemeral read-only client over the built-in node pool. */
|
|
24
|
+
async function lookupNameOnChain(name: string): Promise<string | null> {
|
|
25
|
+
try {
|
|
26
|
+
return await withReadOnlyClient(async (client) => {
|
|
27
|
+
const accounts = await client.db('lookup_account_names', [[name]]);
|
|
28
|
+
return Array.isArray(accounts) && accounts[0]?.id ? String(accounts[0].id) : null;
|
|
29
|
+
});
|
|
30
|
+
} catch (e: any) {
|
|
31
|
+
console.warn(` [warn] Account resolution failed: ${e.message}`);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type ResolveSource = 'typed-id' | 'override-id' | 'override-resolved' | 'preferred-id' | 'stored' | 'resolved';
|
|
37
|
+
type ResolveFailure = 'bot-not-found' | 'no-preferred-account' | 'override-unresolved' | 'name-unresolved';
|
|
38
|
+
|
|
39
|
+
interface ResolveOptions {
|
|
40
|
+
/** Explicit override (--account). Wins over the bot entry and is never persisted. */
|
|
41
|
+
overrideAccount?: string | null;
|
|
42
|
+
/** Ignore a stored accountId and re-resolve on chain (--refresh-account). */
|
|
43
|
+
refresh?: boolean;
|
|
44
|
+
/** Suppress informational output; failures are reported via `reason`. */
|
|
45
|
+
quiet?: boolean;
|
|
46
|
+
/** Chain lookup override (batch tools reuse one connection, tests stub it). */
|
|
47
|
+
lookup?: NameLookup;
|
|
48
|
+
/** Override profiles/bots.json (tests use a temp file). */
|
|
49
|
+
botsFile?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface ResolvedAccount {
|
|
53
|
+
accountId: string | null;
|
|
54
|
+
botKey: string | null;
|
|
55
|
+
botMeta: any | null;
|
|
56
|
+
source: ResolveSource | null;
|
|
57
|
+
reason: ResolveFailure | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Persist a resolved ID onto the bot entry (no-op when it already matches) and
|
|
62
|
+
* report the outcome. `previous` is the stored value the write replaces, if any.
|
|
63
|
+
*/
|
|
64
|
+
function persistResolvedAccountId(botKey: string, accountId: string, previous: string | null, quiet: boolean, botsFile?: string): boolean {
|
|
65
|
+
const wrote = persistBotAccountId(botKey, accountId, botsFile);
|
|
66
|
+
if (quiet) return wrote;
|
|
67
|
+
if (wrote) {
|
|
68
|
+
console.log(previous && previous !== accountId
|
|
69
|
+
? ` Updated stored accountId ${previous} → ${accountId} in profiles/bots.json for '${botKey}'`
|
|
70
|
+
: ` Stored accountId ${accountId} in profiles/bots.json for '${botKey}'`);
|
|
71
|
+
} else if (previous === accountId) {
|
|
72
|
+
console.log(` Stored accountId ${accountId} confirmed up to date`);
|
|
73
|
+
} else {
|
|
74
|
+
console.warn(` [warn] Could not store accountId ${accountId} in profiles/bots.json for '${botKey}'`);
|
|
75
|
+
}
|
|
76
|
+
return wrote;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the chain account for a bot entry:
|
|
81
|
+
* explicit override > typed 1.2.x preferredAccount > stored accountId >
|
|
82
|
+
* fresh chain lookup of the name.
|
|
83
|
+
* The resolved ID is persisted onto the entry whenever it differs from the
|
|
84
|
+
* stored one (an explicit override is never persisted — it is not the bot's
|
|
85
|
+
* preferredAccount).
|
|
86
|
+
*/
|
|
87
|
+
async function resolveBotAccount(botKey: string, options: ResolveOptions = {}): Promise<ResolvedAccount> {
|
|
88
|
+
const { overrideAccount = null, refresh = false, quiet = false, lookup = lookupNameOnChain, botsFile } = options;
|
|
89
|
+
const botMeta = botKey ? loadBotMeta(botKey, botsFile) : null;
|
|
90
|
+
|
|
91
|
+
// An explicit override always wins and is never persisted.
|
|
92
|
+
const override = overrideAccount != null ? String(overrideAccount).trim() : '';
|
|
93
|
+
if (override) {
|
|
94
|
+
if (ACCOUNT_ID_RE.test(override)) {
|
|
95
|
+
return { accountId: override, botKey, botMeta, source: 'override-id', reason: null };
|
|
96
|
+
}
|
|
97
|
+
if (!quiet) console.log(` Resolving account name '${override}'...`);
|
|
98
|
+
const id = await lookup(override);
|
|
99
|
+
if (!id) return { accountId: null, botKey, botMeta, source: null, reason: 'override-unresolved' };
|
|
100
|
+
if (!quiet) console.log(` → ${id}`);
|
|
101
|
+
return { accountId: String(id), botKey, botMeta, source: 'override-resolved', reason: null };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!botMeta) return { accountId: null, botKey, botMeta: null, source: null, reason: 'bot-not-found' };
|
|
105
|
+
|
|
106
|
+
const prefRaw = botMeta.preferredAccount != null ? String(botMeta.preferredAccount).trim() : '';
|
|
107
|
+
if (!prefRaw) return { accountId: null, botKey, botMeta, source: null, reason: 'no-preferred-account' };
|
|
108
|
+
|
|
109
|
+
const stored = ACCOUNT_ID_RE.test(String(botMeta.accountId ?? '')) ? String(botMeta.accountId) : null;
|
|
110
|
+
|
|
111
|
+
// A typed 1.2.x preferredAccount is authoritative — the stored accountId is
|
|
112
|
+
// a derived cache and must never override it. Self-heal the cache.
|
|
113
|
+
if (ACCOUNT_ID_RE.test(prefRaw)) {
|
|
114
|
+
if (stored !== prefRaw) persistResolvedAccountId(botKey, prefRaw, stored, quiet, botsFile);
|
|
115
|
+
return { accountId: prefRaw, botKey, botMeta, source: 'preferred-id', reason: null };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Name + fresh-enough cache + no refresh requested: offline-friendly fast path.
|
|
119
|
+
if (stored && !refresh) {
|
|
120
|
+
if (!quiet) console.log(` Using stored accountId ${stored} from profiles/bots.json (no lookup needed; pass --refresh-account to re-verify)`);
|
|
121
|
+
return { accountId: stored, botKey, botMeta, source: 'stored', reason: null };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!quiet) console.log(` Resolving account name '${prefRaw}'...`);
|
|
125
|
+
const id = await lookup(prefRaw);
|
|
126
|
+
if (!id) return { accountId: null, botKey, botMeta, source: null, reason: 'name-unresolved' };
|
|
127
|
+
if (!quiet) console.log(` → ${id}`);
|
|
128
|
+
persistResolvedAccountId(botKey, String(id), stored, quiet, botsFile);
|
|
129
|
+
return { accountId: String(id), botKey, botMeta, source: 'resolved', reason: null };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Resolve a bare account reference (1.2.x ID or name) to a chain account ID.
|
|
134
|
+
* When a bot in profiles/bots.json claims the name, the resolved ID is
|
|
135
|
+
* persisted onto that entry via resolveBotAccount.
|
|
136
|
+
*/
|
|
137
|
+
async function resolveAccountRef(accountRef: string, options: ResolveOptions = {}): Promise<ResolvedAccount> {
|
|
138
|
+
const ref = String(accountRef ?? '').trim();
|
|
139
|
+
if (ACCOUNT_ID_RE.test(ref)) {
|
|
140
|
+
return { accountId: ref, botKey: null, botMeta: null, source: 'typed-id', reason: null };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// A bot claiming this name is the persist target; its preferredAccount is
|
|
144
|
+
// the same name, so resolveBotAccount owns the whole decision tree.
|
|
145
|
+
let match: { botKey: string; meta: any } | null = null;
|
|
146
|
+
try {
|
|
147
|
+
match = findBotKeyByAccountRef(ref, options.botsFile);
|
|
148
|
+
} catch (_) {
|
|
149
|
+
// bots.json issues must never break resolution; fall through to chain.
|
|
150
|
+
}
|
|
151
|
+
if (match) return resolveBotAccount(match.botKey, options);
|
|
152
|
+
|
|
153
|
+
const lookup = options.lookup ?? lookupNameOnChain;
|
|
154
|
+
if (!options.quiet) console.log(` Resolving account name '${ref}'...`);
|
|
155
|
+
const id = await lookup(ref);
|
|
156
|
+
if (!id) return { accountId: null, botKey: null, botMeta: null, source: null, reason: 'name-unresolved' };
|
|
157
|
+
if (!options.quiet) console.log(` → ${id}`);
|
|
158
|
+
return { accountId: String(id), botKey: null, botMeta: null, source: 'resolved', reason: null };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
ACCOUNT_ID_RE,
|
|
163
|
+
lookupNameOnChain,
|
|
164
|
+
resolveBotAccount,
|
|
165
|
+
resolveAccountRef,
|
|
166
|
+
ResolveOptions,
|
|
167
|
+
ResolvedAccount,
|
|
168
|
+
ResolveSource,
|
|
169
|
+
ResolveFailure,
|
|
170
|
+
NameLookup,
|
|
171
|
+
};
|
|
@@ -10,6 +10,7 @@ import { ensureDir } from '../../modules/order/utils/system.js';
|
|
|
10
10
|
import { PATHS } from '../../modules/paths.js';
|
|
11
11
|
import { MARKET_ADAPTER } from '../../modules/constants.js';
|
|
12
12
|
import { getErrorMessage } from '../../modules/utils/errors.js';
|
|
13
|
+
import { uplotInlineTags } from '../chart_utils.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* LAMBDA vs SLOW ANALYSIS
|
|
@@ -186,9 +187,6 @@ function generateChartHtml(results: any, metricCache: any, fixEr: any, fixFast:
|
|
|
186
187
|
const cacheSlow = metricCache.map((m: any) => m.slow);
|
|
187
188
|
const cacheMove = metricCache.map((m: any) => m.movement);
|
|
188
189
|
|
|
189
|
-
const uplotCode = fs.readFileSync(path.join(PATHS.ANALYSIS.ASSETS_DIR, 'uPlot.iife.min.js'), 'utf8');
|
|
190
|
-
const uplotCSS = fs.readFileSync(path.join(PATHS.ANALYSIS.ASSETS_DIR, 'uPlot.min.css'), 'utf8');
|
|
191
|
-
|
|
192
190
|
const amaAnnotations = [
|
|
193
191
|
{ label: 'AMA1', lambda: 0.0031, slow: 62.1, color: '#ef5350' },
|
|
194
192
|
{ label: 'AMA2', lambda: 0.0025, slow: 72.0, color: '#fb8c00' },
|
|
@@ -203,7 +201,6 @@ function generateChartHtml(results: any, metricCache: any, fixEr: any, fixFast:
|
|
|
203
201
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
204
202
|
<title>λ → Slow — ER=${fixEr} Fast=${fixFast}</title>
|
|
205
203
|
<style>
|
|
206
|
-
${uplotCSS}
|
|
207
204
|
body { font-family: system-ui, sans-serif; margin: 20px; background: #1e1e2e; color: #cdd6f4; }
|
|
208
205
|
h1 { font-size: 16px; margin: 0 0 2px 0; }
|
|
209
206
|
.subtitle { color: #a6adc8; font-size: 12px; margin-bottom: 12px; }
|
|
@@ -231,7 +228,7 @@ function generateChartHtml(results: any, metricCache: any, fixEr: any, fixFast:
|
|
|
231
228
|
${amaAnnotations.map(a => `<span class="legend-item"><span class="legend-dot" style="background:${a.color}"></span> ${a.label}</span>`).join('\n')}
|
|
232
229
|
</div>
|
|
233
230
|
</div>
|
|
234
|
-
|
|
231
|
+
${uplotInlineTags()}
|
|
235
232
|
<script>
|
|
236
233
|
(function() {
|
|
237
234
|
const xs = ${JSON.stringify(xs)};
|