dexbot 1.4.18 → 1.4.20

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 (131) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -0
  3. package/analysis/ama_fitting/analyze_lambda_vs_slow.ts +2 -2
  4. package/analysis/ama_fitting/optimizer_high_resolution.ts +2 -1
  5. package/analysis/ama_fitting/package.json +1 -1
  6. package/analysis/analyze_derivatives.ts +4 -2
  7. package/analysis/analyze_dynamic_weight.ts +3 -1
  8. package/analysis/analyze_kalman.ts +3 -1
  9. package/analysis/analyze_regime.ts +3 -1
  10. package/analysis/analyze_regime_windows.ts +3 -1
  11. package/analysis/analyze_volatility.ts +2 -2
  12. package/analysis/bot_fitting/backtest_ama_sweep.ts +2 -1
  13. package/analysis/bot_fitting/backtest_bot_fitting.ts +17 -12
  14. package/analysis/chart_utils.ts +24 -0
  15. package/analysis/derivative_chart_generator.ts +3 -2
  16. package/analysis/trend_detection/DYNAMIC_WEIGHT_RESEARCH.md +1 -1
  17. package/analysis/trend_detection/dynamic_weight_chart_generator.ts +91 -55
  18. package/analysis/trend_detection/package.json +1 -1
  19. package/claw/examples/claw_profiles_example.ts +4 -1
  20. package/claw/modules/claw_catalog.ts +1 -1
  21. package/claw/modules/claw_skill_md.ts +6 -5
  22. package/claw/modules/dexbot_profiles.ts +3 -1
  23. package/claw/modules/launcher_paths.ts +4 -2
  24. package/claw/package.json +1 -1
  25. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  26. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  27. package/claw/tests/test_claw_mcp_transport.ts +2 -2
  28. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +2 -2
  29. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -1
  30. package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts.map +1 -1
  31. package/dist/analysis/ama_fitting/optimizer_high_resolution.js +2 -1
  32. package/dist/analysis/ama_fitting/optimizer_high_resolution.js.map +1 -1
  33. package/dist/analysis/analyze_derivatives.d.ts.map +1 -1
  34. package/dist/analysis/analyze_derivatives.js +4 -2
  35. package/dist/analysis/analyze_derivatives.js.map +1 -1
  36. package/dist/analysis/analyze_dynamic_weight.js +3 -1
  37. package/dist/analysis/analyze_dynamic_weight.js.map +1 -1
  38. package/dist/analysis/analyze_kalman.js +3 -1
  39. package/dist/analysis/analyze_kalman.js.map +1 -1
  40. package/dist/analysis/analyze_regime.js +3 -1
  41. package/dist/analysis/analyze_regime.js.map +1 -1
  42. package/dist/analysis/analyze_regime_windows.js +3 -1
  43. package/dist/analysis/analyze_regime_windows.js.map +1 -1
  44. package/dist/analysis/analyze_volatility.js +2 -2
  45. package/dist/analysis/analyze_volatility.js.map +1 -1
  46. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts.map +1 -1
  47. package/dist/analysis/bot_fitting/backtest_ama_sweep.js +2 -1
  48. package/dist/analysis/bot_fitting/backtest_ama_sweep.js.map +1 -1
  49. package/dist/analysis/bot_fitting/backtest_bot_fitting.js +18 -13
  50. package/dist/analysis/bot_fitting/backtest_bot_fitting.js.map +1 -1
  51. package/dist/analysis/chart_utils.d.ts +0 -3
  52. package/dist/analysis/chart_utils.d.ts.map +1 -1
  53. package/dist/analysis/chart_utils.js +25 -0
  54. package/dist/analysis/chart_utils.js.map +1 -1
  55. package/dist/analysis/derivative_chart_generator.d.ts.map +1 -1
  56. package/dist/analysis/derivative_chart_generator.js +3 -2
  57. package/dist/analysis/derivative_chart_generator.js.map +1 -1
  58. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -1
  59. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +82 -50
  60. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -1
  61. package/dist/dexbot.d.ts.map +1 -1
  62. package/dist/dexbot.js +2 -3
  63. package/dist/dexbot.js.map +1 -1
  64. package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -1
  65. package/dist/market_adapter/core/market_adapter_service.js +8 -3
  66. package/dist/market_adapter/core/market_adapter_service.js.map +1 -1
  67. package/dist/modules/account_bots.d.ts +1 -0
  68. package/dist/modules/account_bots.d.ts.map +1 -1
  69. package/dist/modules/account_bots.js +27 -5
  70. package/dist/modules/account_bots.js.map +1 -1
  71. package/dist/modules/config.d.ts +2 -0
  72. package/dist/modules/config.d.ts.map +1 -1
  73. package/dist/modules/config.js +2 -0
  74. package/dist/modules/config.js.map +1 -1
  75. package/dist/modules/constants.d.ts +1 -0
  76. package/dist/modules/constants.d.ts.map +1 -1
  77. package/dist/modules/constants.js +13 -4
  78. package/dist/modules/constants.js.map +1 -1
  79. package/dist/modules/dexbot_class.d.ts +22 -0
  80. package/dist/modules/dexbot_class.d.ts.map +1 -1
  81. package/dist/modules/dexbot_class.js +27 -1
  82. package/dist/modules/dexbot_class.js.map +1 -1
  83. package/dist/modules/dexbot_cow_runtime.d.ts +48 -8
  84. package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
  85. package/dist/modules/dexbot_cow_runtime.js +263 -4
  86. package/dist/modules/dexbot_cow_runtime.js.map +1 -1
  87. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  88. package/dist/modules/dexbot_state_recovery.js +20 -1
  89. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  90. package/dist/modules/order/grid.d.ts.map +1 -1
  91. package/dist/modules/order/grid.js +146 -32
  92. package/dist/modules/order/grid.js.map +1 -1
  93. package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
  94. package/dist/modules/order/grid_reconcile_internal.js +6 -27
  95. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  96. package/dist/modules/order/manager.d.ts +13 -0
  97. package/dist/modules/order/manager.d.ts.map +1 -1
  98. package/dist/modules/order/manager.js +75 -2
  99. package/dist/modules/order/manager.js.map +1 -1
  100. package/dist/modules/order/strategy.d.ts.map +1 -1
  101. package/dist/modules/order/strategy.js +6 -15
  102. package/dist/modules/order/strategy.js.map +1 -1
  103. package/dist/modules/order/utils/math.d.ts +102 -1
  104. package/dist/modules/order/utils/math.d.ts.map +1 -1
  105. package/dist/modules/order/utils/math.js +165 -2
  106. package/dist/modules/order/utils/math.js.map +1 -1
  107. package/dist/modules/order/utils/system.d.ts +2 -0
  108. package/dist/modules/order/utils/system.d.ts.map +1 -1
  109. package/dist/modules/order/utils/system.js +30 -2
  110. package/dist/modules/order/utils/system.js.map +1 -1
  111. package/dist/modules/paths.d.ts +26 -1
  112. package/dist/modules/paths.d.ts.map +1 -1
  113. package/dist/modules/paths.js +39 -6
  114. package/dist/modules/paths.js.map +1 -1
  115. package/dist/modules/runtime_settings.d.ts +1 -0
  116. package/dist/modules/runtime_settings.d.ts.map +1 -1
  117. package/dist/modules/runtime_settings.js +13 -2
  118. package/dist/modules/runtime_settings.js.map +1 -1
  119. package/dist/scripts/analyze-git.js +2 -1
  120. package/dist/scripts/analyze-git.js.map +1 -1
  121. package/dist/scripts/analyze-orders.js +2 -2
  122. package/dist/scripts/analyze-orders.js.map +1 -1
  123. package/docs/DEXBOT_COMPARISON.md +3 -3
  124. package/docs/EVOLUTION.md +27 -96
  125. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
  126. package/docs/LIFECYCLE.md +207 -0
  127. package/docs/README.md +9 -1
  128. package/docs/developer_guide.md +4 -3
  129. package/package.json +3 -3
  130. package/scripts/lib/dexbot-paths.sh +20 -11
  131. package/scripts/repo-stats.html +0 -480
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.4.20] - 2026-08-23 - Grid Boundary Hardening, Recovery Poison Gate, Analysis Path Centralization
6
+
7
+ ### 2026-08-22
8
+
9
+ - **Fix(grid)**: harden boundary promotion against gap-floor violations — the spread-correction promotion path could consume every gap slot in a single call (`maxPromotable` computed the opposite rail's span and constrained nothing; `resolveGapBand` re-derived `sellStartIdx` unconditionally from the mutable committed boundary), so any one-time overrun became permanent geometry with no spread floor. Promotion is now capped at band size − MIN_SPREAD_ORDERS (disabled entirely when band ≤ reserve) and the walk hard-stops reserve slots short of the opposite rail edge independent of quota; upstream depth caps (BUY P_s−G−1−B, SELL B−P_b) prevent the slide from stranding placed opposite-rail orders; new `validateBoundaryCommit()` gates commit-time proposals in `_commitWorkingGrid` (rejected proposals keep the last valid boundary); defense-in-depth detectors add pre-broadcast crossed-book refusal (`detectCrossedBookPlan`) and post-commit gap-band intrusion detection → structural resync; new shared `resolveGapSlots()` dedups the `_gapSlots ?? calculateGapSlots(config)` pattern (`modules/order/grid.ts`, `modules/order/utils/math.ts`, `modules/order/manager.ts`, `modules/dexbot_cow_runtime.ts`, `tests/test_spread_boundary_promotion.ts`, `tests/test_spread_check_orchestration.ts`).
10
+ - **Fix(recovery)**: gate persisted boundary restore against overrun poison — a boundary committed and persisted by the pre-fix promotion overrun would legalize itself on every restart: `resolveGapBand` re-derives `sellStartIdx` from whatever value is restored and no runtime gate ran before restore, so structural resync "recovered" straight back into corrupted geometry. New `validatePersistedBoundary()` (stricter than commit-time validation — also rejects in-band placed orders, the poison signature only position-based geometry detects) is applied in `loadGrid` before `_restoreBoundary`; on rejection the structural center is re-derived from slot prices via `calculateIdealBoundary` (unmappable anchors degrade to a boundary-less load the next sync reconciles), and `recoverFromPersistedGrid` refuses poisoned snapshots BEFORE `loadGrid` so resync falls through to a clean `requestGridReset(refreshCenterPrice)` rebuild using config-derived `calculateGapSlots` (`modules/order/utils/math.ts`, `modules/order/grid.ts`, `modules/dexbot_state_recovery.ts`, new `tests/test_boundary_restore_validation.ts`, `tests/test_uncertain_broadcast.ts` fixtures updated).
11
+ - **Feat(analysis)**: centralize dynamic weight chart slider ranges, kalS% default 1.0 — the interactive research chart hardcoded its 16 knob ranges independently in four places each (HTML range attrs, tooltip text, server-side clamps, paste-clamp code), and the amaS% paste clamp had already drifted from its slider span. New `SLIDER_RANGES` constant is the single source of truth feeding HTML attrs, tooltips, payload transport, and every paste/init/latch clamp; kalS% range 0.15–1.5 → 0.5–1.5 with `DYNAMIC_WEIGHT_KALMAN_MAX_SLOPE_PCT` raised 0.8 → 1.0; fixes a TDZ crash (`const SR` declared after first use threw ReferenceError on chart load, leaving all panels blank) (`analysis/trend_detection/dynamic_weight_chart_generator.ts`, `analysis/trend_detection/DYNAMIC_WEIGHT_RESEARCH.md`, `modules/constants.ts`).
12
+ - **Fix(paths)**: centralize analysis output dirs and npm-install path resolution — research/chart tooling wrote generated artifacts into the package tree via `__dirname`/`import.meta.url`/cwd-relative defaults (wiped by `npm update -g`, blocked on read-only prefixes). New `PATHS.ANALYSIS.{DIR,CHARTS_DIR,RESULTS_DIR,ASSETS_DIR}` resolver keeps repo-local outputs only for repo-layout source checkouts and follows the resolved profiles dir otherwise, with a `DEXBOT_ANALYSIS_DIR` env override and `XDG_CONFIG_HOME` support; all chart generators and optimizer/backtest result writers rerouted (auto-discovery scans RESULTS_DIR first with legacy-dir fallback); `writeChartFile` materializes vendored uPlot assets next to relocated charts so relative refs keep resolving; claw profile/skill-doc resolution aligned with `modules/paths.ts` ordering; shell shim npm-package detection switched to exact-parent basename matching; generated charts/repo-stats excluded from the shipped tarball (`modules/paths.ts`, `modules/config.ts`, `analysis/*`, `scripts/lib/dexbot-paths.sh`, `claw/modules/*`, `package.json`).
13
+
14
+ ### 2026-08-23
15
+
16
+ - **Fix(market-adapter)**: align dynamic weight clip logic between live service and research chart — the chart dropped zero-slope bars from the AMA clip pool, kept Kalman warmup bars that the live service sliced away, and fed different precision (rounded vs raw) into the velocity smoothing pipeline, so parameter research could mispredict live clipping behavior. The live service now feeds the smoothing pipeline unrounded velocity/displacement percentages and computes the Kalman clip percentile over the full series (no warmup slice); the chart keeps every finite slope including zeros via `Number.isFinite` and builds the payload-level initial smoothed series from raw fields to match the interactive recompute path; CLIP_PCT_MAX knob bound lowered 55 → 20. Note: at clip=10% totals shift ~−0.12% since warmup bars now count toward the percentile pool (`market_adapter/core/market_adapter_service.ts`, `analysis/trend_detection/dynamic_weight_chart_generator.ts`, `tests/test_market_adapter_service.ts`).
17
+
18
+ ## [1.4.19] - 2026-08-21 - COW Broadcast Op Cap, Grid Divergence Rail Fix, UI Price Feedback
19
+
20
+ ### 2026-08-21
21
+
22
+ - **Feat(cow)**: enforce a per-broadcast operation cap — a single COW rebalance can expand one fill batch into many more order operations (4 fills → 12 creates + 4 updates = 16 ops), so `FILL_PROCESSING.MAX_FILL_BATCH_SIZE` is a weak proxy for on-chain transaction size. `COW_PERFORMANCE.MAX_OPS_PER_BROADCAST` (default 4) now caps operations per broadcast; oversized batches are split into sequential transactions via `executeChunkedWithRetryOnUncertain`, restoring the original "N fills per broadcast" intent at the op level. An uncertain chunk failure continues broadcasting the remaining chunks (no order dropped); a definitively rejected chunk aborts the rest; the first failure is rethrown enriched with `partialOnChainState`, `chunksTotal`/`chunksFailed`/`chunksAborted`, and `broadcastedOperationCount`. Configurable via the runtime settings override chain (`runtimeCowPerformance`/`marketCowPerformance`/`botCowPerformance`). Covered by the new 463-line `test_cow_ops_per_broadcast.ts` (`modules/dexbot_cow_runtime.ts`, `modules/constants.ts`, `modules/dexbot_class.ts`, `modules/runtime_settings.ts`).
23
+ - **Fix(grid)**: exclude gap-band strays from divergence desired-window selection — a fund-driven boundary shift during `applyGridDivergenceCorrections` re-types the working grid, but the SPREAD GUARD keeps live on-chain orders inside the new spread band typed BUY/SELL, so the Phase-2 window selected "closest to market" slots purely by stored type+price and a stray SELL inside the new gap looked like the bottom of the sell rail and was never relocated, collapsing the real spread (boundary 107→110 left sells at 111–113 inside the new gap, real spread 0.5% vs the 2.0% target). New shared `MathUtils.isSlotInRail` (pure geometric rail-membership test) now filters desired-window candidates using the working boundary; the strategy window and virtual-slot selection delegate to the same helper, and gap-band strays become surplus → cancelled and relocated back onto the rail (`modules/order/utils/math.ts`, `modules/order/utils/system.ts`, `modules/order/strategy.ts`, `modules/order/grid_reconcile_internal.ts`, `tests/test_is_slot_in_rail.ts`, `tests/test_cow_divergence_correction.ts`, `tests/test_lp_chart.ts`).
24
+ - **Tune**: lower the AMA slope grid-reset threshold from 10 to 8 — the AMA slope delta trigger resets the grid when the slope swing exceeds (`AMA_SLOPE_DELTA_THRESHOLD_PERCENT`/100) × maxSlopePct. At 10 the XRP-BTS trend reversal (DOWN baseline → UP current, delta 0.0074%/bar) stayed below the 0.0085 threshold, leaving the grid scaled for the wrong direction. Lowering to 8 (threshold 0.0068%/bar) reacts to the reversal while still ignoring small slope noise (`modules/constants.ts`).
25
+ - **Feat(ui)**: green/red relative-scaling feedback for price inputs — the bot editor's Price Range displayed min/max as plain white, hiding whether relative scaling (x multipliers) is active. Range values now render green for "1.55x" multipliers and red for fixed prices, and `readInput` gains a colorize option so the typed input switches red → green live the moment the "x" is entered (`modules/account_bots.ts`, `modules/order/utils/system.ts`).
26
+ - **Fix(ui)**: clarify the GridPrice label in the bot editor — the Price section displayed the gridPrice reference as bare "Grid:", which was ambiguous next to "Start:"/"Pool:" and collided with the section "4) Grid". Renamed to "GridPrice:" (one word, matching the `gridPrice` config field name) to reflect the values it accepts (pool/book/ama/number/none) (`modules/account_bots.ts`).
27
+ - **Docs**: add an end-to-end lifecycle walkthrough — new `docs/LIFECYCLE.md` consolidates the startup, fill-driven (reactive), and maintenance/AMA-driven (periodic) flows into one newcomer-facing map with mermaid system-context and sequence diagrams (fill → `_incomingFillQueue` → `processFilledOrders` → Accounting SSOT → WorkingGrid COW → single atomic broadcast → persist; `_performPeriodicGridChecks` → `performPeriodicGridChecks` → `runGridMaintenance` → `executeMaintenanceLogic`), plus a cross-cutting invariants table and file map. Surfaced as the first read from README and the docs index, and added to the npm `files` array (`docs/LIFECYCLE.md`, `docs/developer_guide.md`, `docs/README.md`, `README.md`, `package.json`).
28
+
5
29
  ## [1.4.18] - 2026-08-19 - Compile-First Runtime Migration (tsx to dist)
6
30
 
7
31
  ### 2026-08-19
package/README.md CHANGED
@@ -293,6 +293,7 @@ Logs are written to `logs/` in the profiles directory in all modes: the monolith
293
293
  - **[Docs Index](docs/README.md)** - Main documentation hub
294
294
  - **[Claw API Boundary](claw/docs/AI_BOT_LIBRARY_API.md)** - Responsibility split between the AI layer and the DEXBot2 execution layer
295
295
  - **[Architecture](docs/architecture.md)** - System design, fill processing pipeline, and testing strategy
296
+ - **[Lifecycle](docs/LIFECYCLE.md)** - End-to-end walkthrough: startup, fill-driven, and AMA-driven flows with diagrams
296
297
  - **[Developer Guide](docs/developer_guide.md)** - Development guide, environment variables, examples, and glossary
297
298
  - **[Copy-on-Write Plan](docs/COPY_ON_WRITE_MASTER_PLAN.md)** - Copy-on-Write grid architecture
298
299
  - **[Fund Movement & Accounting](docs/FUND_MOVEMENT_AND_ACCOUNTING.md)** - Fund accounting, grid topology, and rotation mechanics
@@ -186,8 +186,8 @@ function generateChartHtml(results: any, metricCache: any, fixEr: any, fixFast:
186
186
  const cacheSlow = metricCache.map((m: any) => m.slow);
187
187
  const cacheMove = metricCache.map((m: any) => m.movement);
188
188
 
189
- const uplotCode = fs.readFileSync(path.resolve(__dirname, '..', 'uplot', 'uPlot.iife.min.js'), 'utf8');
190
- const uplotCSS = fs.readFileSync(path.resolve(__dirname, '..', 'uplot', 'uPlot.min.css'), 'utf8');
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
191
 
192
192
  const amaAnnotations = [
193
193
  { label: 'AMA1', lambda: 0.0031, slow: 62.1, color: '#ef5350' },
@@ -719,7 +719,8 @@ async function run() {
719
719
  const outName = dataFile
720
720
  ? `optimization_results_${path.basename(dataFile, '.json')}${lambdaSuffix}.json`
721
721
  : `optimization_results_high_resolution${lambdaSuffix}.json`;
722
- const outPath = path.join(__dirname, outName);
722
+ const outPath = path.join(PATHS.ANALYSIS.RESULTS_DIR, outName);
723
+ ensureDir(path.dirname(outPath));
723
724
  writeJSON(outPath, {
724
725
  meta: {
725
726
  dataLabel,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ama_fitting",
3
- "version": "1.4.18",
3
+ "version": "1.4.20",
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": {
@@ -20,10 +20,12 @@
20
20
  */
21
21
 
22
22
 
23
+ import path from 'node:path';
23
24
  import { pathToFileURL } from 'node:url';
24
25
  import { DerivativeAnalyzer } from './trend_detection/derivative_analyzer.js';
25
26
  import { generateHTML } from './derivative_chart_generator.js';
26
27
  import { writeChartFile } from './chart_utils.js';
28
+ import { PATHS } from '../modules/paths.js';
27
29
  import { resolveSource, listAvailableBots, type SourceConfig } from './resolve_source.js';
28
30
  'use strict';
29
31
 
@@ -85,7 +87,7 @@ function parseArgs(): CliConfig {
85
87
  interpHoldBars: 3,
86
88
  rsiZone: 10,
87
89
  rsiExtreme: 90,
88
- chartFile: 'analysis/charts/derivative_chart.html',
90
+ chartFile: path.join(PATHS.ANALYSIS.CHARTS_DIR, 'derivative_chart.html'),
89
91
  quiet: false,
90
92
  listBots: false,
91
93
  };
@@ -173,7 +175,7 @@ Analyzer options:
173
175
  --file PATH JSON candle input
174
176
 
175
177
  Output:
176
- --chart FILE Chart output path (default: analysis/charts/derivative_chart.html)
178
+ --chart FILE Chart output path (default: <analysis charts dir>/derivative_chart.html)
177
179
  --list-bots List available bot keys from bots.json
178
180
  --quiet Suppress log output
179
181
  `);
@@ -12,6 +12,7 @@
12
12
  * --file market_adapter/data/lp/<path>/<to>/<lp-candles>.json
13
13
  */
14
14
 
15
+ import path from 'node:path';
15
16
  import { KalmanTrendAnalyzer } from './trend_detection/kalman_trend_analyzer.js';
16
17
  import { HurstAnalyzer } from './trend_detection/hurst_analyzer.js';
17
18
  import { PermutationEntropyAnalyzer } from './trend_detection/permutation_entropy_analyzer.js';
@@ -19,6 +20,7 @@ import { generateHTML } from './trend_detection/dynamic_weight_chart_generator.j
19
20
  import { calculateAMA } from '../market_adapter/core/strategies/ama.js';
20
21
  import { computeAmaSlopeWeights } from '../market_adapter/core/strategies/ama_slope_model.js';
21
22
  import { MARKET_ADAPTER } from '../modules/constants.js';
23
+ import { PATHS } from '../modules/paths.js';
22
24
  import { writeChartFile } from './chart_utils.js';
23
25
  import { getCandleClose } from './math_utils.js';
24
26
  import { resolveSource, listAvailableBots, type SourceConfig } from './resolve_source.js';
@@ -64,7 +66,7 @@ function parseArgs() {
64
66
  dispScaleMinPct?: number;
65
67
  } = {
66
68
  source: { type: 'market_adapter', config: { botKey: '' } },
67
- chartFile: 'analysis/charts/dynamic_weight_chart.html',
69
+ chartFile: path.join(PATHS.ANALYSIS.CHARTS_DIR, 'dynamic_weight_chart.html'),
68
70
  alpha: MARKET_ADAPTER.DYNAMIC_WEIGHT_ALPHA,
69
71
  gain: MARKET_ADAPTER.DYNAMIC_WEIGHT_GAIN,
70
72
  dispWeight: MARKET_ADAPTER.DYNAMIC_WEIGHT_DW,
@@ -11,12 +11,14 @@
11
11
  * --file market_adapter/data/lp/<path>/<to>/<lp-candles>.json
12
12
  */
13
13
 
14
+ import path from 'node:path';
14
15
  import { KalmanTrendAnalyzer } from './trend_detection/kalman_trend_analyzer.js';
15
16
  import { generateHTML } from './trend_detection/kalman_chart_generator.js';
16
17
  import { calculateAMA } from '../market_adapter/core/strategies/ama.js';
17
18
  import { computeAverageAmaSlopePct } from '../market_adapter/core/strategies/dynamic_weight_series.js';
18
19
  import { getCandleClose } from './math_utils.js';
19
20
  import { writeChartFile } from './chart_utils.js';
21
+ import { PATHS } from '../modules/paths.js';
20
22
  import { resolveSource, listAvailableBots, type SourceConfig } from './resolve_source.js';
21
23
 
22
24
  'use strict';
@@ -39,7 +41,7 @@ function parseArgs() {
39
41
  source: { type: 'market_adapter', config: { botKey: '' } },
40
42
  rNoise: 0.05,
41
43
  qNoise: 0.005,
42
- chartFile: 'analysis/charts/kalman_chart.html',
44
+ chartFile: path.join(PATHS.ANALYSIS.CHARTS_DIR, 'kalman_chart.html'),
43
45
  quiet: false,
44
46
  listBots: false,
45
47
  };
@@ -18,7 +18,9 @@
18
18
  * --file market_adapter/data/lp/<path>/<to>/<lp-candles>.json
19
19
  */
20
20
 
21
+ import path from 'node:path';
21
22
  import { MARKET_ADAPTER } from '../modules/constants.js';
23
+ import { PATHS } from '../modules/paths.js';
22
24
  import { HurstAnalyzer } from './trend_detection/hurst_analyzer.js';
23
25
  import { PermutationEntropyAnalyzer } from './trend_detection/permutation_entropy_analyzer.js';
24
26
  import { generateRegimeHTML } from './trend_detection/regime_chart_generator.js';
@@ -44,7 +46,7 @@ function parseArgs() {
44
46
  listBots: boolean;
45
47
  } = {
46
48
  source: { type: 'market_adapter', config: { botKey: '' } },
47
- chartFile: 'analysis/charts/regime_chart.html',
49
+ chartFile: path.join(PATHS.ANALYSIS.CHARTS_DIR, 'regime_chart.html'),
48
50
  hurstWindow: HURST_CONFIG.window,
49
51
  peWindow: PE_CONFIG.window,
50
52
  peM: PE_CONFIG.m,
@@ -17,9 +17,11 @@
17
17
  * --file market_adapter/data/lp/<path>/<to>/<lp-candles>.json
18
18
  */
19
19
 
20
+ import path from 'node:path';
20
21
  import { HurstAnalyzer } from './trend_detection/hurst_analyzer.js';
21
22
  import { PermutationEntropyAnalyzer } from './trend_detection/permutation_entropy_analyzer.js';
22
23
  import { MARKET_ADAPTER } from '../modules/constants.js';
24
+ import { PATHS } from '../modules/paths.js';
23
25
  import { writeChartFile } from './chart_utils.js';
24
26
  import { getCandleClose } from './math_utils.js';
25
27
  import { roundTo } from '../modules/order/utils/math.js';
@@ -59,7 +61,7 @@ function parseArgs() {
59
61
  listBots: boolean;
60
62
  } = {
61
63
  source: { type: 'market_adapter', config: { botKey: '' } },
62
- chartFile: 'analysis/charts/regime_windows_heatmap.html',
64
+ chartFile: path.join(PATHS.ANALYSIS.CHARTS_DIR, 'regime_windows_heatmap.html'),
63
65
  quiet: false,
64
66
  listBots: false,
65
67
  };
@@ -21,7 +21,6 @@
21
21
  */
22
22
 
23
23
  import path from 'node:path';
24
- import { fileURLToPath } from 'node:url';
25
24
 
26
25
  import { calculateAMA } from '../market_adapter/core/strategies/ama.js';
27
26
  import { computeATRSeries } from '../market_adapter/core/strategies/atr/calculator.js';
@@ -29,6 +28,7 @@ import { normalizeAtrPeriod } from '../market_adapter/core/config_normalizers.js
29
28
  import { computeVolatilityShift } from '../market_adapter/core/strategies/volatility_shift.js';
30
29
  import { generateHTML } from './trend_detection/volatility_chart_generator.js';
31
30
  import { MARKET_ADAPTER } from '../modules/constants.js';
31
+ import { PATHS } from '../modules/paths.js';
32
32
  import { getCandleClose } from './math_utils.js';
33
33
  import { writeChartFile } from './chart_utils.js';
34
34
  import { resolveSource, listAvailableBots, type SourceConfig } from './resolve_source.js';
@@ -40,7 +40,7 @@ const MIN_WEIGHT = MARKET_ADAPTER.DYNAMIC_WEIGHT_MIN_WEIGHT;
40
40
  const MAX_WEIGHT = MARKET_ADAPTER.DYNAMIC_WEIGHT_MAX_WEIGHT;
41
41
  const DEFAULT_THRESHOLD = MARKET_ADAPTER.DYNAMIC_WEIGHT_SYMMETRIC_SHIFT_THRESHOLD;
42
42
  const DEFAULT_CLAMP = MARKET_ADAPTER.DYNAMIC_WEIGHT_SYMMETRIC_SHIFT_CLAMP;
43
- const DEFAULT_CHART_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), 'charts');
43
+ const DEFAULT_CHART_DIR = PATHS.ANALYSIS.CHARTS_DIR;
44
44
  const DEFAULT_CHART_FILE = path.join(DEFAULT_CHART_DIR, 'volatility_chart.html');
45
45
 
46
46
  function computeATRSeriesNormalized(candles: any[], period = DEFAULT_ATR_PERIOD) {
@@ -7,6 +7,7 @@ import { calculateAMA } from '../../market_adapter/core/strategies/ama.js';
7
7
  import { range } from '../math_utils.js';
8
8
  import { parseListOrRange, loadLpData, fmt } from './shared_utils.js';
9
9
  import { getStorage } from '../../modules/storage/index.js';
10
+ import { PATHS } from '../../modules/paths.js';
10
11
  const { readJSON, writeJSON } = getStorage();
11
12
  'use strict';
12
13
 
@@ -759,7 +760,7 @@ async function run() {
759
760
 
760
761
  // ── Save JSON ───────────────────────────────────────────────────────────
761
762
  const outName = `ama_sweep_results_${path.basename(cfg.dataPath!, '.json')}.json`;
762
- const outPath = path.join(__dirname, outName);
763
+ const outPath = path.join(PATHS.ANALYSIS.RESULTS_DIR, outName);
763
764
  writeJSON(outPath, {
764
765
  meta: {
765
766
  generatedAt: new Date().toISOString(),
@@ -4,6 +4,7 @@ import { calculateAMA } from '../../market_adapter/core/strategies/ama.js';
4
4
  import { range } from '../math_utils.js';
5
5
  import { parseListOrRange, loadLpData, fmt } from './shared_utils.js';
6
6
  import { getStorage } from '../../modules/storage/index.js';
7
+ import { PATHS } from '../../modules/paths.js';
7
8
  const { ensureDir, readJSON, writeJSON } = getStorage();
8
9
 
9
10
  'use strict';
@@ -125,28 +126,32 @@ function parseArgs() {
125
126
  * The optimizer names results `optimization_results_<base>_w<λ1>_<λ2>_<λ3>_<λ4>.json`
126
127
  * (per-AMA distance weights appended as a `_w...` suffix). To stay robust against
127
128
  * weight overrides, pick the most recently written `optimization_results_<base>_w*.json`
128
- * in `ama_fitting/`; fall back to the legacy `optimization_results_<base>.json` name
129
- * if no suffixed file exists.
129
+ * in the analysis results dir (legacy ama_fitting location still scanned); fall
130
+ * back to the legacy `optimization_results_<base>.json` name if no suffixed file exists.
130
131
  */
131
132
  function resolveAutoResultsPath(dataPath: string): string {
132
133
  const base = path.basename(dataPath, '.json');
133
- const dir = path.join(__dirname, '..', 'ama_fitting');
134
+ // Canonical results dir first, then the legacy ama_fitting location so
135
+ // previously generated files are still discovered.
136
+ const dirs = [PATHS.ANALYSIS.RESULTS_DIR, path.join(PATHS.PROJECT_ROOT, 'analysis', 'ama_fitting')];
134
137
  const prefix = `optimization_results_${base}_w`;
135
138
  let newest: { file: string; mtimeMs: number } | null = null;
136
139
  try {
137
- for (const entry of fs.readdirSync(dir)) {
138
- if (!entry.startsWith(prefix) || !entry.endsWith('.json')) continue;
139
- const full = path.join(dir, entry);
140
- const stat = fs.statSync(full);
141
- if (!newest || stat.mtimeMs > newest.mtimeMs) {
142
- newest = { file: full, mtimeMs: stat.mtimeMs };
140
+ for (const dir of dirs) {
141
+ for (const entry of fs.readdirSync(dir)) {
142
+ if (!entry.startsWith(prefix) || !entry.endsWith('.json')) continue;
143
+ const full = path.join(dir, entry);
144
+ const stat = fs.statSync(full);
145
+ if (!newest || stat.mtimeMs > newest.mtimeMs) {
146
+ newest = { file: full, mtimeMs: stat.mtimeMs };
147
+ }
143
148
  }
144
149
  }
145
150
  } catch {
146
151
  // dir unreadable — fall through to legacy name check
147
152
  }
148
153
  if (newest) return newest.file;
149
- return path.join(dir, `optimization_results_${base}.json`);
154
+ return path.join(PATHS.ANALYSIS.RESULTS_DIR, `optimization_results_${base}.json`);
150
155
  }
151
156
 
152
157
  function loadAmaStrategies(resultsPath: string) {
@@ -388,8 +393,8 @@ function run() {
388
393
  console.log();
389
394
 
390
395
  const outName = `bot_fitting_results_${path.basename(cfg.dataPath!, '.json')}.json`;
391
- const outPath = path.join(__dirname, outName);
392
- ensureDir(__dirname);
396
+ const outPath = path.join(PATHS.ANALYSIS.RESULTS_DIR, outName);
397
+ ensureDir(PATHS.ANALYSIS.RESULTS_DIR);
393
398
  writeJSON(outPath, {
394
399
  meta: {
395
400
  generatedAt: new Date().toISOString(),
@@ -2,6 +2,7 @@
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { getStorage } from '../modules/storage/index.js';
5
+ import { PATHS } from '../modules/paths.js';
5
6
  const { ensureDir } = getStorage();
6
7
  'use strict';
7
8
 
@@ -9,6 +10,28 @@ const { ensureDir } = getStorage();
9
10
  * Chart utilities for analysis HTML generators.
10
11
  */
11
12
 
13
+ // Runtime assets every generated chart references as ../uplot/* relative to
14
+ // its own directory (charts live in <root>/charts, assets in <root>/uplot).
15
+ const UPLOT_RUNTIME_FILES = ['uPlot.iife.min.js', 'uPlot.min.css'];
16
+
17
+ /**
18
+ * Generated HTML loads uPlot from a sibling `uplot/` dir next to the charts
19
+ * dir. In a source checkout that dir is the vendored original; anywhere else
20
+ * (relocated analysis root under npm/home profiles, or a custom --chart path)
21
+ * materialize a copy so the relative refs keep resolving.
22
+ */
23
+ function ensureSiblingUplotAssets(chartDir: any) {
24
+ const targetDir = path.join(path.dirname(path.resolve(chartDir)), 'uplot');
25
+ const sourceDir = PATHS.ANALYSIS.ASSETS_DIR;
26
+ if (targetDir === path.resolve(sourceDir)) return;
27
+ if (UPLOT_RUNTIME_FILES.every((f) => fs.existsSync(path.join(targetDir, f)))) return;
28
+ if (!fs.existsSync(sourceDir)) return;
29
+ fs.mkdirSync(targetDir, { recursive: true });
30
+ for (const f of UPLOT_RUNTIME_FILES) {
31
+ fs.copyFileSync(path.join(sourceDir, f), path.join(targetDir, f));
32
+ }
33
+ }
34
+
12
35
 
13
36
  function escapeHtml(str: string) {
14
37
  const map: Record<string, string> = {
@@ -34,6 +57,7 @@ function toEpochSeconds(ts: any, fallbackIdx: any) {
34
57
  function writeChartFile(filePath: any, html: any) {
35
58
  const chartDir = path.dirname(filePath);
36
59
  if (!fs.existsSync(chartDir)) ensureDir(chartDir);
60
+ ensureSiblingUplotAssets(chartDir);
37
61
  fs.writeFileSync(filePath, html, 'utf8');
38
62
  }
39
63
 
@@ -8,11 +8,12 @@ import { getStorage } from '../modules/storage/index.js';
8
8
  const { ensureDir, readJSON } = getStorage();
9
9
  import { fixedTo } from '../modules/order/utils/math.js';
10
10
  import { bindHoverStateFn, zoomResetScript } from './chart_ui.js';
11
+ import { PATHS } from '../modules/paths.js';
11
12
  'use strict';
12
13
  function parseArgs(argv = process.argv.slice(2)) {
13
14
  const cfg: { inputFile: string | null; outputFile: string; title: string; quiet: boolean } = {
14
15
  inputFile: null,
15
- outputFile: 'analysis/charts/derivative_chart.html',
16
+ outputFile: path.join(PATHS.ANALYSIS.CHARTS_DIR, 'derivative_chart.html'),
16
17
  title: 'Derivative Trend Analysis',
17
18
  quiet: false,
18
19
  };
@@ -40,7 +41,7 @@ Derivative Chart Generator (uPlot)
40
41
  Usage:
41
42
  tsx analysis/derivative_chart_generator.ts --input <file.json> [options]
42
43
  Options:
43
- --output FILE Output HTML (default: analysis/charts/derivative_chart.html)
44
+ --output FILE Output HTML (default: <analysis charts dir>/derivative_chart.html)
44
45
  --title TEXT Chart title
45
46
  --quiet Suppress output
46
47
  `);
@@ -160,7 +160,7 @@ All panels share aligned vertical time grid lines, and the bottom output panel s
160
160
  | **nz%** | 0–1 | 0.00 | Neutral zone: dead-band below which offset is forced to 0 |
161
161
  | **lb** | 1–32 | 9 | Logarithmic. Lookback bars for AMA slope calculation |
162
162
  | **amaS%** | 0.04–0.12 | 0.085 | Logarithmic. Gear ratio for average per-bar AMA slope saturation |
163
- | **kalS%** | 0.15–1.5 | 0.8 | Logarithmic. Gear ratio for Kalman composite saturation |
163
+ | **kalS%** | 0.5–1.5 | 1.0 | Logarithmic. Gear ratio for Kalman composite saturation |
164
164
  | **clip%** | 0–55 | 10 | Percentile clip: filters extreme inputs (0 = off) |
165
165
 
166
166
  ### Output Controls