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.
Files changed (275) hide show
  1. package/CHANGELOG.md +78 -2
  2. package/README.md +23 -19
  3. package/analysis/README.md +26 -13
  4. package/analysis/account_resolver.ts +171 -0
  5. package/analysis/ama_fitting/analyze_lambda_vs_slow.ts +2 -5
  6. package/analysis/ama_fitting/package.json +1 -1
  7. package/analysis/analyze_dynamic_weight.ts +9 -3
  8. package/analysis/bot_usage/discover_bot_accounts.ts +20 -27
  9. package/analysis/chain_pool.ts +47 -0
  10. package/analysis/chart_utils.ts +33 -20
  11. package/analysis/derivative_chart_generator.ts +2 -3
  12. package/analysis/fills_source.ts +234 -0
  13. package/analysis/grid_correction_check.ts +38 -251
  14. package/analysis/trade_profitability.ts +169 -304
  15. package/analysis/tradingview/README.md +2 -2
  16. package/analysis/tradingview/analyze_tradingview.ts +5 -0
  17. package/analysis/tradingview/tradingview_uplot_chart_generator.ts +5 -6
  18. package/analysis/trend_detection/DYNAMIC_WEIGHT_RESEARCH.md +35 -2
  19. package/analysis/trend_detection/dynamic_weight_chart_generator.ts +84 -13
  20. package/analysis/trend_detection/kalman_chart_generator.ts +2 -3
  21. package/analysis/trend_detection/package.json +1 -1
  22. package/analysis/trend_detection/regime_chart_generator.ts +2 -3
  23. package/analysis/trend_detection/volatility_chart_generator.ts +2 -3
  24. package/claw/modules/dexbot_profiles.ts +4 -3
  25. package/claw/package.json +1 -1
  26. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  27. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  28. package/claw/tests/test_claw_mcp_transport.ts +2 -2
  29. package/claw/tests/test_dexbot_profiles.ts +18 -1
  30. package/dist/analysis/account_resolver.d.ts +43 -0
  31. package/dist/analysis/account_resolver.d.ts.map +1 -0
  32. package/dist/analysis/account_resolver.js +137 -0
  33. package/dist/analysis/account_resolver.js.map +1 -0
  34. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts.map +1 -1
  35. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +2 -4
  36. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -1
  37. package/dist/analysis/analyze_dynamic_weight.js +9 -3
  38. package/dist/analysis/analyze_dynamic_weight.js.map +1 -1
  39. package/dist/analysis/bot_usage/discover_bot_accounts.js +20 -28
  40. package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
  41. package/dist/analysis/chain_pool.d.ts +10 -0
  42. package/dist/analysis/chain_pool.d.ts.map +1 -0
  43. package/dist/analysis/chain_pool.js +47 -0
  44. package/dist/analysis/chain_pool.js.map +1 -0
  45. package/dist/analysis/chart_utils.d.ts +14 -1
  46. package/dist/analysis/chart_utils.d.ts.map +1 -1
  47. package/dist/analysis/chart_utils.js +32 -22
  48. package/dist/analysis/chart_utils.js.map +1 -1
  49. package/dist/analysis/derivative_chart_generator.d.ts.map +1 -1
  50. package/dist/analysis/derivative_chart_generator.js +2 -3
  51. package/dist/analysis/derivative_chart_generator.js.map +1 -1
  52. package/dist/analysis/fills_source.d.ts +79 -0
  53. package/dist/analysis/fills_source.d.ts.map +1 -0
  54. package/dist/analysis/fills_source.js +186 -0
  55. package/dist/analysis/fills_source.js.map +1 -0
  56. package/dist/analysis/grid_correction_check.d.ts +1 -21
  57. package/dist/analysis/grid_correction_check.d.ts.map +1 -1
  58. package/dist/analysis/grid_correction_check.js +30 -257
  59. package/dist/analysis/grid_correction_check.js.map +1 -1
  60. package/dist/analysis/trade_profitability.d.ts +18 -19
  61. package/dist/analysis/trade_profitability.d.ts.map +1 -1
  62. package/dist/analysis/trade_profitability.js +136 -280
  63. package/dist/analysis/trade_profitability.js.map +1 -1
  64. package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -1
  65. package/dist/analysis/tradingview/analyze_tradingview.js +5 -0
  66. package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
  67. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
  68. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +5 -6
  69. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
  70. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -1
  71. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +84 -13
  72. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -1
  73. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -1
  74. package/dist/analysis/trend_detection/kalman_chart_generator.js +2 -3
  75. package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -1
  76. package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -1
  77. package/dist/analysis/trend_detection/regime_chart_generator.js +2 -3
  78. package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -1
  79. package/dist/analysis/trend_detection/volatility_chart_generator.d.ts.map +1 -1
  80. package/dist/analysis/trend_detection/volatility_chart_generator.js +2 -3
  81. package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -1
  82. package/dist/dexbot.d.ts.map +1 -1
  83. package/dist/dexbot.js +27 -68
  84. package/dist/dexbot.js.map +1 -1
  85. package/dist/market_adapter/core/asymmetric_bounds.d.ts +2 -2
  86. package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -1
  87. package/dist/market_adapter/core/asymmetric_bounds.js +15 -12
  88. package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -1
  89. package/dist/market_adapter/interval_utils.d.ts +1 -1
  90. package/dist/market_adapter/interval_utils.js +1 -1
  91. package/dist/market_adapter/lp_chart_core.d.ts.map +1 -1
  92. package/dist/market_adapter/lp_chart_core.js +2 -3
  93. package/dist/market_adapter/lp_chart_core.js.map +1 -1
  94. package/dist/modules/account_bots.d.ts +51 -18
  95. package/dist/modules/account_bots.d.ts.map +1 -1
  96. package/dist/modules/account_bots.js +258 -117
  97. package/dist/modules/account_bots.js.map +1 -1
  98. package/dist/modules/bitshares-native/serial/operations.d.ts +1 -84
  99. package/dist/modules/bitshares-native/serial/operations.d.ts.map +1 -1
  100. package/dist/modules/bitshares-native/serial/operations.js +1 -38
  101. package/dist/modules/bitshares-native/serial/operations.js.map +1 -1
  102. package/dist/modules/bot_defaults.d.ts +98 -0
  103. package/dist/modules/bot_defaults.d.ts.map +1 -0
  104. package/dist/modules/bot_defaults.js +169 -0
  105. package/dist/modules/bot_defaults.js.map +1 -0
  106. package/dist/modules/bot_settings.d.ts.map +1 -1
  107. package/dist/modules/bot_settings.js +4 -1
  108. package/dist/modules/bot_settings.js.map +1 -1
  109. package/dist/modules/cli_colors.d.ts +0 -1
  110. package/dist/modules/cli_colors.d.ts.map +1 -1
  111. package/dist/modules/config.d.ts +1 -0
  112. package/dist/modules/config.d.ts.map +1 -1
  113. package/dist/modules/config.js +1 -0
  114. package/dist/modules/config.js.map +1 -1
  115. package/dist/modules/constants.d.ts +11 -2
  116. package/dist/modules/constants.d.ts.map +1 -1
  117. package/dist/modules/constants.js +45 -32
  118. package/dist/modules/constants.js.map +1 -1
  119. package/dist/modules/credential_policy.d.ts +1 -25
  120. package/dist/modules/credential_policy.d.ts.map +1 -1
  121. package/dist/modules/credential_policy.js +1 -1
  122. package/dist/modules/credential_policy.js.map +1 -1
  123. package/dist/modules/credential_runtime.d.ts +1 -6
  124. package/dist/modules/credential_runtime.d.ts.map +1 -1
  125. package/dist/modules/credential_runtime.js +1 -1
  126. package/dist/modules/credential_runtime.js.map +1 -1
  127. package/dist/modules/credential_session_cache.d.ts +1 -5
  128. package/dist/modules/credential_session_cache.d.ts.map +1 -1
  129. package/dist/modules/credential_session_cache.js +1 -1
  130. package/dist/modules/credential_session_cache.js.map +1 -1
  131. package/dist/modules/crypto/pure_secp256k1.d.ts +1 -2
  132. package/dist/modules/crypto/pure_secp256k1.d.ts.map +1 -1
  133. package/dist/modules/crypto/pure_secp256k1.js +1 -1
  134. package/dist/modules/crypto/pure_secp256k1.js.map +1 -1
  135. package/dist/modules/dexbot_class.d.ts +8 -7
  136. package/dist/modules/dexbot_class.d.ts.map +1 -1
  137. package/dist/modules/dexbot_class.js +9 -8
  138. package/dist/modules/dexbot_class.js.map +1 -1
  139. package/dist/modules/dexbot_cow_runtime.d.ts +7 -6
  140. package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
  141. package/dist/modules/dexbot_cow_runtime.js +8 -7
  142. package/dist/modules/dexbot_cow_runtime.js.map +1 -1
  143. package/dist/modules/dexbot_fill_runtime.d.ts +1 -17
  144. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
  145. package/dist/modules/dexbot_fill_runtime.js +1 -1
  146. package/dist/modules/dexbot_fill_runtime.js.map +1 -1
  147. package/dist/modules/dexbot_maintenance_runtime.d.ts +2 -2
  148. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  149. package/dist/modules/dexbot_maintenance_runtime.js +24 -8
  150. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  151. package/dist/modules/dexbot_state_recovery.d.ts +1 -7
  152. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  153. package/dist/modules/dexbot_state_recovery.js +1 -1
  154. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  155. package/dist/modules/launcher/adapter_requirement.d.ts +1 -5
  156. package/dist/modules/launcher/adapter_requirement.d.ts.map +1 -1
  157. package/dist/modules/launcher/adapter_requirement.js +2 -2
  158. package/dist/modules/launcher/adapter_requirement.js.map +1 -1
  159. package/dist/modules/launcher/bot_supervisor.d.ts +1 -6
  160. package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
  161. package/dist/modules/launcher/bot_supervisor.js +1 -1
  162. package/dist/modules/launcher/bot_supervisor.js.map +1 -1
  163. package/dist/modules/launcher/foreign_cred_daemon.d.ts +1 -3
  164. package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -1
  165. package/dist/modules/launcher/foreign_cred_daemon.js +1 -1
  166. package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -1
  167. package/dist/modules/launcher/launch_modes.d.ts +17 -2
  168. package/dist/modules/launcher/launch_modes.d.ts.map +1 -1
  169. package/dist/modules/launcher/launch_modes.js +18 -1
  170. package/dist/modules/launcher/launch_modes.js.map +1 -1
  171. package/dist/modules/launcher/market_adapter_runtime.d.ts +1 -3
  172. package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -1
  173. package/dist/modules/launcher/market_adapter_runtime.js +1 -1
  174. package/dist/modules/launcher/market_adapter_runtime.js.map +1 -1
  175. package/dist/modules/launcher/monolithic_runtime.d.ts +1 -5
  176. package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
  177. package/dist/modules/launcher/monolithic_runtime.js +9 -2
  178. package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
  179. package/dist/modules/launcher/status_reporting.d.ts +1 -2
  180. package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
  181. package/dist/modules/launcher/status_reporting.js +1 -1
  182. package/dist/modules/launcher/status_reporting.js.map +1 -1
  183. package/dist/modules/market_adapter_whitelist.d.ts +40 -1
  184. package/dist/modules/market_adapter_whitelist.d.ts.map +1 -1
  185. package/dist/modules/market_adapter_whitelist.js +161 -11
  186. package/dist/modules/market_adapter_whitelist.js.map +1 -1
  187. package/dist/modules/order/export.d.ts +1 -8
  188. package/dist/modules/order/export.d.ts.map +1 -1
  189. package/dist/modules/order/export.js +1 -1
  190. package/dist/modules/order/export.js.map +1 -1
  191. package/dist/modules/order/grid.d.ts +16 -0
  192. package/dist/modules/order/grid.d.ts.map +1 -1
  193. package/dist/modules/order/grid.js +75 -103
  194. package/dist/modules/order/grid.js.map +1 -1
  195. package/dist/modules/order/grid_reconcile_internal.d.ts +1 -10
  196. package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
  197. package/dist/modules/order/grid_reconcile_internal.js +2 -2
  198. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  199. package/dist/modules/order/manager.d.ts +10 -2
  200. package/dist/modules/order/manager.d.ts.map +1 -1
  201. package/dist/modules/order/manager.js +20 -8
  202. package/dist/modules/order/manager.js.map +1 -1
  203. package/dist/modules/order/sync_engine.d.ts.map +1 -1
  204. package/dist/modules/order/sync_engine.js +8 -15
  205. package/dist/modules/order/sync_engine.js.map +1 -1
  206. package/dist/modules/order/utils/math.d.ts +1 -10
  207. package/dist/modules/order/utils/math.d.ts.map +1 -1
  208. package/dist/modules/order/utils/math.js +1 -1
  209. package/dist/modules/order/utils/math.js.map +1 -1
  210. package/dist/modules/order/utils/system.d.ts +5 -16
  211. package/dist/modules/order/utils/system.d.ts.map +1 -1
  212. package/dist/modules/order/utils/system.js +7 -4
  213. package/dist/modules/order/utils/system.js.map +1 -1
  214. package/dist/modules/process_discovery.d.ts +0 -3
  215. package/dist/modules/process_discovery.d.ts.map +1 -1
  216. package/dist/modules/process_discovery.js +1 -1
  217. package/dist/modules/process_discovery.js.map +1 -1
  218. package/dist/modules/settings_merge.d.ts +10 -1
  219. package/dist/modules/settings_merge.d.ts.map +1 -1
  220. package/dist/modules/settings_merge.js +27 -17
  221. package/dist/modules/settings_merge.js.map +1 -1
  222. package/dist/modules/utils/chain_logs.d.ts +1 -2
  223. package/dist/modules/utils/chain_logs.d.ts.map +1 -1
  224. package/dist/modules/utils/chain_logs.js +1 -1
  225. package/dist/modules/utils/chain_logs.js.map +1 -1
  226. package/dist/modules/validate_profiles.d.ts.map +1 -1
  227. package/dist/modules/validate_profiles.js +4 -3
  228. package/dist/modules/validate_profiles.js.map +1 -1
  229. package/dist/scripts/analyze-git.js +2 -2
  230. package/dist/scripts/analyze-git.js.map +1 -1
  231. package/dist/scripts/chart_command.d.ts +48 -0
  232. package/dist/scripts/chart_command.d.ts.map +1 -0
  233. package/dist/scripts/chart_command.js +504 -0
  234. package/dist/scripts/chart_command.js.map +1 -0
  235. package/dist/scripts/dw.d.ts +3 -0
  236. package/dist/scripts/dw.d.ts.map +1 -0
  237. package/dist/scripts/dw.js +30 -0
  238. package/dist/scripts/dw.js.map +1 -0
  239. package/dist/scripts/tv.d.ts +1 -31
  240. package/dist/scripts/tv.d.ts.map +1 -1
  241. package/dist/scripts/tv.js +10 -463
  242. package/dist/scripts/tv.js.map +1 -1
  243. package/dist/scripts/update.js +145 -93
  244. package/dist/scripts/update.js.map +1 -1
  245. package/dist/scripts/update_dist_freshness.d.ts +46 -0
  246. package/dist/scripts/update_dist_freshness.d.ts.map +1 -0
  247. package/dist/scripts/update_dist_freshness.js +115 -0
  248. package/dist/scripts/update_dist_freshness.js.map +1 -0
  249. package/dist/unlock.js +1 -1
  250. package/dist/unlock.js.map +1 -1
  251. package/docs/BITSHARES_ONBOARDING.md +117 -44
  252. package/docs/COPY_ON_WRITE_MASTER_PLAN.md +10 -207
  253. package/docs/COW_INVARIANTS.md +16 -10
  254. package/docs/DEXBOT_COMPARISON.md +3 -3
  255. package/docs/EVOLUTION.md +76 -30
  256. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +9 -9
  257. package/docs/GRID_RECALCULATION.md +34 -30
  258. package/docs/GRID_RECONCILE.md +30 -27
  259. package/docs/LIFECYCLE.md +12 -11
  260. package/docs/LOGGING.md +1 -0
  261. package/docs/README.md +58 -52
  262. package/docs/WORKFLOW.md +1 -2
  263. package/docs/architecture.md +21 -32
  264. package/docs/developer_guide.md +4 -3
  265. package/market_adapter/README.md +36 -60
  266. package/modules/README.md +2 -1
  267. package/package.json +1 -2
  268. package/scripts/README.md +18 -25
  269. package/scripts/git-viewer.sh +1 -1
  270. package/scripts/reset-settings.sh +1 -1
  271. package/tests/README.md +10 -1
  272. package/dist/scripts/generate_market_adapter_whitelist.d.ts +0 -15
  273. package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +0 -1
  274. package/dist/scripts/generate_market_adapter_whitelist.js +0 -191
  275. package/dist/scripts/generate_market_adapter_whitelist.js.map +0 -1
@@ -236,6 +236,11 @@ async function main() {
236
236
  },
237
237
  smaPeriod: config.smaPeriod,
238
238
  amaDefaults: {
239
+ // Intentionally the 3-param AMA (er/fast/slow) only: the in-page
240
+ // recomputation has no erSmoothPeriod input by decision — `dexbot tv`
241
+ // stays a plain candle chart. The dw research chart and the live
242
+ // adapter are the mirrors of the full bot AMA config (incl.
243
+ // ama.erSmoothPeriod); the knob is inert by default (global 0).
239
244
  erPeriod: config.amaErPeriod ?? amaConfig.erPeriod,
240
245
  fastPeriod: config.amaFastPeriod ?? amaConfig.fastPeriod,
241
246
  slowPeriod: config.amaSlowPeriod ?? amaConfig.slowPeriod,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import fs from 'node:fs';
4
4
  import { MARKET_ADAPTER } from '../../modules/constants.js';
5
- import { escapeHtml, serializeJsonForScript, UPLOT_SHARED_SCRIPT, embedFunctionSources } from '../chart_utils.js';
5
+ import { escapeHtml, serializeJsonForScript, UPLOT_SHARED_SCRIPT, embedFunctionSources, uplotInlineTags } from '../chart_utils.js';
6
6
  import { cursorCSS, uplotBgCSS } from '../chart_css.js';
7
7
  import { zoomResetScript } from '../chart_ui.js';
8
8
  import { normalizeCandle } from '../math_utils.js';
@@ -240,8 +240,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
240
240
  <meta name="darkreader-lock">
241
241
  <meta name="viewport" content="width=device-width, initial-scale=1">
242
242
  <title>${escapeHtml(title)}</title>
243
- <link rel="stylesheet" href="../uplot/uPlot.min.css">
244
- <script src="../uplot/uPlot.iife.min.js"></script>
243
+ ${uplotInlineTags()}
245
244
  <style>
246
245
  * { box-sizing: border-box; }
247
246
  :root { color-scheme: dark; }
@@ -584,7 +583,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
584
583
  </span>
585
584
  </div>
586
585
  <div class="indicator">
587
- <label><input type="checkbox" id="ama-init-offset-toggle"> Init Offset</label>
586
+ <label title="Init Offset"><input type="checkbox" id="ama-init-offset-toggle"> Offset</label>
588
587
  <input type="range" id="ama-init-offset" min="-50" max="50" value="0" step="1" style="width:90px;vertical-align:middle" disabled>
589
588
  <span id="ama-init-offset-val" style="font-size:11px;color:#8b949e;width:32px;display:inline-block;text-align:right">0%</span>
590
589
  </div>
@@ -1470,7 +1469,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1470
1469
  const maxSlopeOffset = Number(slopeCfg.maxSlopeOffset) > 0 ? Number(slopeCfg.maxSlopeOffset) : 0.5;
1471
1470
  const maxAsym = gridCfg && Number(gridCfg.maxAsymmetryFactor) > 0
1472
1471
  ? Number(gridCfg.maxAsymmetryFactor)
1473
- : (Number(slopeCfg.maxAsymmetryFactor) > 0 ? Number(slopeCfg.maxAsymmetryFactor) : 0.35);
1472
+ : (Number(slopeCfg.maxAsymmetryFactor) > 0 ? Number(slopeCfg.maxAsymmetryFactor) : 0.333);
1474
1473
  const inc = gridCfg && Number(gridCfg.incrementPercent) > 0 ? Number(gridCfg.incrementPercent) : null;
1475
1474
  const minSlots = Math.floor(Number(gridCfg && gridCfg.minScaleSlots) > 0 ? Number(gridCfg.minScaleSlots) : (Number(slopeCfg.minScaleSlots) || 0));
1476
1475
  // User x-range span (slider 1.2x–2.0x, default 1.55x): the only
@@ -3067,7 +3066,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
3067
3066
  }
3068
3067
  makeStepper('sma-period', 1, 1, 0);
3069
3068
  makeStepper('vwap-bars', 1, 24, 0);
3070
- makeStepper('ama-er', 1, 1, 0);
3069
+ makeStepper('ama-er', 10, 1, 0);
3071
3070
  makeStepper('ama-fast', 0.1, 0.1, 1);
3072
3071
  makeStepper('ama-slow', 1, 0.1, 1);
3073
3072
  document.getElementById('ama-init-offset').addEventListener('input', () => {
@@ -63,6 +63,11 @@ On top of this blend, **Hurst Exponent** and **Permutation Entropy** act as a re
63
63
  ## Quick Start
64
64
 
65
65
  ```bash
66
+ # One-step (recommended): same fetch pipeline as `dexbot tv` — bot, pool, or pair
67
+ dexbot dw <bot-key>
68
+ dexbot dw 133 --month 6
69
+ dexbot dw TOKENA/TOKENB --month 1
70
+
66
71
  # From JSON candle file
67
72
  node dist/analysis/analyze_dynamic_weight.js \
68
73
  --file market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
@@ -77,6 +82,8 @@ node dist/analysis/analyze_dynamic_weight.js \
77
82
 
78
83
  Output: `analysis/charts/dynamic_weight_chart.html` (open in browser)
79
84
 
85
+ `dexbot dw` writes an auto-named chart instead: `analysis/charts/dw_<bot|pool_<id>|<a>_<b>>_1h_<N>m.html`.
86
+
80
87
  **Note**: Hurst requires 256 bars and PE requires 58 bars before their regime signals become active. The first portion of the chart will show the full weight without regime gating.
81
88
 
82
89
  ## CLI Flags
@@ -90,6 +97,7 @@ Output: `analysis/charts/dynamic_weight_chart.html` (open in browser)
90
97
  | `--alpha` | `0.5` | Initial α blend (0 = pure Kalman, 1 = pure AMA) |
91
98
  | `--dw` | `0.50` | Initial displacement weight (0 = pure velocity, 1 = full displacement) |
92
99
  | `--lb` | `9` | Initial lookback bars (1-32) for AMA slope calculation |
100
+ | `--ema` | `0` | Initial AMA input EMA span in bars (0 = off, 0-32) |
93
101
  | `--gain` | `1.0` | Initial gain multiplier |
94
102
  | `--clip` | `10` | Initial clip percentile |
95
103
  | `--quiet` | `false` | Suppress console output |
@@ -107,6 +115,7 @@ All panels share aligned vertical time grid lines, and the bottom output panel s
107
115
 
108
116
  ### Panel 2 — AMA Slope Input (14%)
109
117
  - **Orange line**: AMA3 slope percentage
118
+ - **Gray dashed line**: raw (unfiltered) slope — the reference when the `ema` knob > 0
110
119
  - Shows the directional strength of the slow KAMA's trend movement
111
120
  - Values outside the clip threshold are flattened before entering the offset formula
112
121
  - Background shading mirrors the output signal direction
@@ -159,6 +168,7 @@ All panels share aligned vertical time grid lines, and the bottom output panel s
159
168
  |------|-------|---------|---------|
160
169
  | **nz%** | 0–1 | 0.00 | Neutral zone: dead-band below which offset is forced to 0 |
161
170
  | **lb** | 1–32 | 9 | Logarithmic. Lookback bars for AMA slope calculation |
171
+ | **ema** | 0–32 | 0 | AMA input EMA span in bars (0 = off). Low-pass filters the AMA before the slope is taken; the gray dashed Raw‰ line shows the unfiltered slope for comparison |
162
172
  | **amaS%** | 0.06–0.12 | 0.09 | Logarithmic. Gear ratio for average per-bar AMA slope saturation |
163
173
  | **kalS%** | 0.5–1.5 | 1.0 | Logarithmic. Gear ratio for Kalman composite saturation |
164
174
  | **clip%** | 0–55 | 10 | Percentile clip: filters extreme inputs (0 = off) |
@@ -172,7 +182,7 @@ All panels share aligned vertical time grid lines, and the bottom output panel s
172
182
 
173
183
  ## Copy / Paste Parameters
174
184
 
175
- The **copy** button serializes all knob values (α, dw, kf, kfd, dsp, kdt, kfs, cf, lb, amaS%, kalS%, th%, gain, clip%, nz%, regi) to JSON and writes them to both the clipboard and `localStorage`.
185
+ The **copy** button serializes all knob values (α, dw, kf, kfd, dsp, kdt, kfs, cf, lb, ema, amaS%, kalS%, th%, gain, clip%, nz%, regi) to JSON and writes them to both the clipboard and `localStorage`.
176
186
 
177
187
  The **paste** button first checks `localStorage` for parameters from a previous copy in the same browser session. If none found, it prompts for Ctrl+V input. A confirmation popup shows the parsed values before applying them. Click **Apply** to set the knobs, **Cancel** or press **Escape** to dismiss.
178
188
 
@@ -182,7 +192,7 @@ The **paste** button first checks `localStorage` for parameters from a previous
182
192
 
183
193
  ### AMA slope
184
194
 
185
- Taking the slope of the AMA produces a signal that is already noise-filtered at the source — a near-zero slope genuinely means sideways, not oscillation that happened to average out. The neutral zone (`nz%` knob) dead-bands any remaining micro-slope residuals, which is all the additional filtering the slope channel needs.
195
+ Taking the slope of the AMA produces a signal that is already noise-filtered at the source — a near-zero slope genuinely means sideways, not oscillation that happened to average out. The neutral zone (`nz%` knob) dead-bands any remaining micro-slope residuals. When that is still too jumpy, the optional `ema` knob low-pass filters the AMA input itself before the slope is taken (0 = off keeps the raw input).
186
196
 
187
197
  ### Kalman filter
188
198
 
@@ -277,6 +287,7 @@ The lookup table can be customized per-market or per-bot (see [Custom Configurat
277
287
  ## Formulas
278
288
 
279
289
  ### AMA Offset
290
+ With `ema` > 0, `AMA_now` / `AMA_lb_bars_ago` below are read from the EMA-filtered AMA input instead of the raw AMA series (everything else identical):
280
291
  ```
281
292
  amaSlope% = ((AMA_now − AMA_lb_bars_ago) / AMA_lb_bars_ago × 100) / lb
282
293
  amaClip = clamp(amaSlope%, ±clipThreshold) // percentile-based clip
@@ -354,6 +365,27 @@ Number of bars to look back when computing AMA slope:
354
365
 
355
366
  AMA slope is normalized to an average percent per bar, not the cumulative move across the full lookback. Lower values = more noise, faster reaction. Higher values = smoother signals, more lag without adding gain just because the measurement window is longer.
356
367
 
368
+ ### ema (AMA input EMA span)
369
+ Optional low-pass EMA over the AMA values before the slope is taken (span in bars, α = 2/(span+1), `0` = off). Because differencing and an EMA are both linear filters, they commute — smoothing the AMA input produces exactly the smoothed slope series, modulo the seeding transient.
370
+
371
+ | `ema` | noise reduction* | added lag | verdict |
372
+ |---|---|---|---|
373
+ | 0 | 1× (off) | 0 bars | default, current behavior |
374
+ | 4 | ~2× | ~+1.5 bars | light |
375
+ | 9 (= lb) | ~3× | ~+4 bars | sweet spot |
376
+ | 16 | ~4× | ~+7.5 bars | heavy but usable |
377
+ | 32 | ~5.7× | ~+15.5 bars | upper-bound demo, too laggy to trade |
378
+
379
+ \* standard-deviation reduction for white-ish noise ≈ `sqrt(span)`; lag ≈ `(span-1)/2` bars (EMA group delay) on top of the slope window's `(lb-1)/2`.
380
+
381
+ Measured on ~8.7k hourly bars with the default AMA profile (`lb = 9`): the slow AMA's slope is dominated by low-frequency moves rather than white noise, so the `sqrt(span)` rule overstates the standard-deviation gain — `ema = 9` cut slope std only ~3% but trimmed the largest peaks ~22% and zero-crossings 86 → 74; `ema = 32` cut std ~11%, peaks ~48%, crossings 86 → 44, with a measured lag of ~3 bars at span 9 (theory 4). On this input the knob acts mainly as a peak-trimmer and sign-flip reducer, not as a general noise killer.
382
+
383
+ Effects while tuning:
384
+ - Zero-crossings get rarer, so the `cf` latch matters less and the `nz%` dead-band sits on a smooth curve instead of on noise.
385
+ - |slope| peaks shrink, so the same `amaS%` reaches full offset strength less often — expect to lower `amaS%` (or raise `gain`) to restore output amplitude. The `clip%` percentile pool is rebuilt from the filtered input, which partially self-compensates.
386
+ - Keep total lag in mind: ~`lb/2 + ema/2` bars before the slope reflects a move; at defaults that is ~8 bars (~8 h on the 1 h chart).
387
+ - Panel 2 plots the smoothed slope (orange) over the raw slope (gray dashed) so the noise/lag trade-off is directly visible.
388
+
357
389
  ### regi (regime sensitivity)
358
390
  - 0 = regime multiplier is always 1.0 (Hurst+PE ignored)
359
391
  - 1 = default table values used as-is
@@ -383,6 +415,7 @@ The 3×3 regime multiplier table can be customized per-market or per-bot (defaul
383
415
  Candle Data
384
416
  ├── AMA3 (slow KAMA, erPeriod=781)
385
417
  │ ├── calculateAMA() → AMA3 values per bar [price panel overlay]
418
+ │ ├── `ema` knob (0 = off) → EMA-filtered AMA input [research chart only]
386
419
  │ ├── computeAmaSlopeWeights() → slope%
387
420
  │ └── Percentile clip → amaClip → amaOff
388
421
  │
@@ -10,7 +10,7 @@ import {
10
10
  resolveKalmanVelocitySmoothingConfig,
11
11
  smoothKalmanVelocityPoint,
12
12
  } from '../../market_adapter/core/signals/kalman_velocity_smoothing.js';
13
- import { embedFunctionSources, escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT } from '../chart_utils.js';
13
+ import { embedFunctionSources, escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT, uplotInlineTags } from '../chart_utils.js';
14
14
  import { sharedChartCSS } from '../chart_css.js';
15
15
  import { Y_AXIS_SIZE, makeCursorConfig, bindHoverStateFn, wireChartEvents, zoomResetScript, sizeChartsFn } from '../chart_ui.js';
16
16
 
@@ -40,6 +40,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
40
40
  const AMA_MS_MIN = 0.06, AMA_MS_MAX = 0.12;
41
41
  const KAL_MS_MIN = 0.5, KAL_MS_MAX = 1.5;
42
42
  const CLIP_PCT_MAX = 20;
43
+ const EMA_MIN = 0, EMA_MAX = 32;
43
44
  const TH_MIN = 0, TH_MAX = 0.5;
44
45
  const KF_MIN = 0, KF_MAX = 200;
45
46
  const KFD_MIN = 1.0, KFD_MAX = 3.0;
@@ -53,6 +54,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
53
54
  dispWeight: [0, 1],
54
55
  neutralZonePct: [0, 1],
55
56
  lookbackBars: [LB_MIN, LB_MAX],
57
+ amaEmaSpan: [EMA_MIN, EMA_MAX],
56
58
  amaMaxSlopePct: [AMA_MS_MIN, AMA_MS_MAX],
57
59
  kalmanMaxSlopePct: [KAL_MS_MIN, KAL_MS_MAX],
58
60
  clipPct: [0, CLIP_PCT_MAX],
@@ -117,6 +119,10 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
117
119
  const defaultKalmanDispThresholdMult = data.kalmanDispThresholdMult ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_DISP_THRESHOLD_MULT_DEFAULT;
118
120
  const defaultKalmanSmoothSpanPct = data.kalmanSmoothSpanPct ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_SMOOTH_SPAN_PCT_DEFAULT;
119
121
  const defaultSignalConfirmBars = data.signalConfirmBars ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_SIGNAL_CONFIRM_BARS_DEFAULT;
122
+ // Research-only `ema` knob: EMA span in bars over the AMA input before the
123
+ // slope is taken. 0 (default) = off, keeps the raw AMA input.
124
+ const defaultAmaEmaSpanRaw = data.amaEmaSpan ?? ma.amaEmaSpan ?? 0;
125
+ const defaultAmaEmaSpan = Math.round(Math.min(EMA_MAX, Math.max(EMA_MIN, Number.isFinite(defaultAmaEmaSpanRaw) ? defaultAmaEmaSpanRaw : EMA_MIN)));
120
126
  const regimeInitSlider = Math.round(defaultRegimeSensitivity * 100);
121
127
 
122
128
  const interval = results.length > 1 ?
@@ -224,8 +230,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
224
230
  <head>
225
231
  <meta charset="UTF-8">
226
232
  <title>${escapeHtml(title)}</title>
227
- <link rel="stylesheet" href="../uplot/uPlot.min.css">
228
- <script src="../uplot/uPlot.iife.min.js"></script>
233
+ ${uplotInlineTags()}
229
234
  <style>
230
235
  ${sharedChartCSS()}
231
236
  #price-panel { flex: 0 0 34%; min-height: 0; position: relative; border-bottom: 1px solid #30363d; }
@@ -236,7 +241,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
236
241
  .legend-item { gap: 3px; }
237
242
  .legend-val { font-family: monospace; display: inline-block; text-align: right; min-width: 45px; }
238
243
  #l-price, #l-ama3 { min-width: 62px; }
239
- #l-ama-slope, #l-kal-vel, #l-kal-disp { min-width: 54px; }
244
+ #l-ama-slope, #l-ama-slope-raw, #l-kal-vel, #l-kal-disp { min-width: 54px; }
240
245
  #l-signal, #l-signal-latched { min-width: 70px; text-align: left; }
241
246
  #l-combined-raw, #l-combined-echo { min-width: 42px; }
242
247
  #l-mult { min-width: 38px; }
@@ -281,6 +286,9 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
281
286
  .ctrl.lb label { min-width: 20px; }
282
287
  .ctrl.lb input[type="range"] { accent-color: #39d0d8; width: 80px; }
283
288
  .ctrl.lb .val { color: #39d0d8; }
289
+ .ctrl.ema label { min-width: 20px; }
290
+ .ctrl.ema input[type="range"] { accent-color: #f0a000; width: 80px; }
291
+ .ctrl.ema .val { color: #f0a000; }
284
292
  .ctrl.off input[type="range"] { accent-color: #3fb950; }
285
293
  .ctrl.off .val { color: #3fb950; }
286
294
  .ctrl.nz input[type="range"] { accent-color: #8b949e; }
@@ -326,6 +334,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
326
334
  <div class="section-label">AMA SLOPE INPUT</div>
327
335
  <div class="legend">
328
336
  <div class="legend-item"><div class="dot" style="background:#f0a000;"></div>Slope‰: <span id="l-ama-slope" class="legend-val" style="font-weight:bold;">-</span></div>
337
+ <div class="legend-item"><div class="dot" style="background:#8b949e;"></div>Raw‰: <span id="l-ama-slope-raw" class="legend-val">-</span></div>
329
338
  </div>
330
339
  <div id="ama-chart"></div>
331
340
  </div>
@@ -356,6 +365,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
356
365
  <div class="group-sep"></div>
357
366
  <div class="ctrl nz"><label for="nz-slider">nz%</label><input type="range" id="nz-slider" min="0" max="100" value="${Math.round(defaultNeutralZone * 100)}" title="Neutral Zone %"><span class="val" id="nz-value">${defaultNeutralZone.toFixed(2)}</span></div>
358
367
  <div class="ctrl lb"><label for="lb-slider">lb</label><input type="range" id="lb-slider" min="0" max="1000" value="${lbInitSlider}" title="Lookback Bars (${LB_MIN}-${LB_MAX})"><span class="val" id="lb-value">${lookbackBars}</span></div>
368
+ <div class="ctrl ema"><label for="ema-slider">ema</label><input type="range" id="ema-slider" min="${EMA_MIN}" max="${EMA_MAX}" value="${defaultAmaEmaSpan}" title="AMA input EMA span in bars (${EMA_MIN} = off; noise std ~ 1/sqrt(span), lag ~ (span-1)/2 bars; span ~ lb is the balance point)"><span class="val" id="ema-value">${defaultAmaEmaSpan}</span></div>
359
369
  <div class="ctrl ms-ama"><label for="ama-ms-slider">amaS%</label><input type="range" id="ama-ms-slider" min="0" max="1000" value="${amaMsInitSlider}" title="AMA Max Slope % per bar (${AMA_MS_MIN}-${AMA_MS_MAX})"><span class="val" id="ama-ms-value">${defaultAmaMaxSlopePct.toFixed(4)}</span></div>
360
370
  <div class="ctrl clip"><label for="clip-slider">clip%</label><input type="range" id="clip-slider" min="0" max="${CLIP_PCT_MAX}" value="${Math.min(defaultClipPct, CLIP_PCT_MAX)}" title="Outlier Clip %"><span class="val" id="clip-value">${Math.min(defaultClipPct, CLIP_PCT_MAX)}%</span></div>
361
371
 
@@ -389,7 +399,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
389
399
  </div>
390
400
  </div>
391
401
 
392
- <script id="payload" type="application/json">${serializeJsonForScript({ dates, prices, hurstArr, peArr, hurstSegments, peSegments, ama3Prices, amaSeedSmaLine, amaSlopePct, kalmanVelocityPctRaw, kalmanVelocityPct, kalmanDisplacementPct, kalmanIsReady, signals, alpha: defaultAlpha, gain: defaultGain, kalmanSmoothPct: defaultKalmanSmoothPct, kalmanDispScaleMult: defaultKalmanDispScaleMult, kalmanDispThresholdMult: defaultKalmanDispThresholdMult, kalmanSmoothSpanPct: defaultKalmanSmoothSpanPct, signalConfirmBars: defaultSignalConfirmBars, neutralZonePct: defaultNeutralZone, dispWeight: defaultDispWeight, amaMaxSlopePct: defaultAmaMaxSlopePct, kalmanMaxSlopePct: defaultKalmanMaxSlopePct, maxDispPct, clipPct: defaultClipPct, minOutputThreshold: defaultMinOutputThreshold, outputClamp: defaultOutputClamp, regimeSensitivity: defaultRegimeSensitivity, absoluteThreshold: defaultAbsoluteThreshold, lookbackBars, amaErPeriod, amaSlowPeriod, amaWarmupBars, amaSlopeReadyBars, realBarCount, amaPctMax, kalPctMax, amaPercentiles, kalPercentiles, amaSlopeLogMin: AMA_MS_LOG_MIN_N, amaSlopeLogMax: AMA_MS_LOG_MAX_N, kalSlopeLogMin: KAL_MS_LOG_MIN_N, kalSlopeLogMax: KAL_MS_LOG_MAX_N, lbLogMin: LB_LOG_MIN_N, lbLogMax: LB_LOG_MAX_N, gainLogMin: GAIN_LOG_MIN_N, gainLogMax: GAIN_LOG_MAX_N, sliderRanges: SLIDER_RANGES, dispScaleMinPct: defaultDispScaleMinPct, weightMin: MARKET_ADAPTER.DYNAMIC_WEIGHT_MIN_WEIGHT, weightMax: MARKET_ADAPTER.DYNAMIC_WEIGHT_MAX_WEIGHT, marketAdapter: ma, amaWeightConfig, hNodes: [0.5 + MARKET_ADAPTER.HURST_ZONE_BAND, 0.5, 0.5 - MARKET_ADAPTER.HURST_ZONE_BAND], pNodes: MARKET_ADAPTER.PE_NODES, regimeTable: MARKET_ADAPTER.REGIME_TABLE })}</script>
402
+ <script id="payload" type="application/json">${serializeJsonForScript({ dates, prices, hurstArr, peArr, hurstSegments, peSegments, ama3Prices, amaSeedSmaLine, amaSlopePct, kalmanVelocityPctRaw, kalmanVelocityPct, kalmanDisplacementPct, kalmanIsReady, signals, alpha: defaultAlpha, gain: defaultGain, kalmanSmoothPct: defaultKalmanSmoothPct, kalmanDispScaleMult: defaultKalmanDispScaleMult, kalmanDispThresholdMult: defaultKalmanDispThresholdMult, kalmanSmoothSpanPct: defaultKalmanSmoothSpanPct, signalConfirmBars: defaultSignalConfirmBars, neutralZonePct: defaultNeutralZone, dispWeight: defaultDispWeight, amaMaxSlopePct: defaultAmaMaxSlopePct, kalmanMaxSlopePct: defaultKalmanMaxSlopePct, maxDispPct, clipPct: defaultClipPct, minOutputThreshold: defaultMinOutputThreshold, outputClamp: defaultOutputClamp, regimeSensitivity: defaultRegimeSensitivity, absoluteThreshold: defaultAbsoluteThreshold, lookbackBars, amaErPeriod, amaSlowPeriod, amaWarmupBars, amaSlopeReadyBars, realBarCount, amaPctMax, kalPctMax, amaPercentiles, kalPercentiles, amaSlopeLogMin: AMA_MS_LOG_MIN_N, amaSlopeLogMax: AMA_MS_LOG_MAX_N, kalSlopeLogMin: KAL_MS_LOG_MIN_N, kalSlopeLogMax: KAL_MS_LOG_MAX_N, lbLogMin: LB_LOG_MIN_N, lbLogMax: LB_LOG_MAX_N, amaEmaSpan: defaultAmaEmaSpan, gainLogMin: GAIN_LOG_MIN_N, gainLogMax: GAIN_LOG_MAX_N, sliderRanges: SLIDER_RANGES, dispScaleMinPct: defaultDispScaleMinPct, weightMin: MARKET_ADAPTER.DYNAMIC_WEIGHT_MIN_WEIGHT, weightMax: MARKET_ADAPTER.DYNAMIC_WEIGHT_MAX_WEIGHT, marketAdapter: ma, amaWeightConfig, hNodes: [0.5 + MARKET_ADAPTER.HURST_ZONE_BAND, 0.5, 0.5 - MARKET_ADAPTER.HURST_ZONE_BAND], pNodes: MARKET_ADAPTER.PE_NODES, regimeTable: MARKET_ADAPTER.REGIME_TABLE })}</script>
393
403
 
394
404
  <script>
395
405
  const data = JSON.parse(document.getElementById('payload').textContent);
@@ -431,6 +441,8 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
431
441
  const kalMsSliderToVal = (pos) => Math.exp(KAL_MS_LOG_MIN + (pos / 1000) * (KAL_MS_LOG_MAX - KAL_MS_LOG_MIN));
432
442
 
433
443
  let currentLookbackBars = data.lookbackBars ?? ${JSON.stringify(lookbackBars)};
444
+ let currentAmaEmaSpan = Math.max(SR.amaEmaSpan[0], Math.min(SR.amaEmaSpan[1], Math.round(data.amaEmaSpan ?? 0)));
445
+ let currentAmaSource = data.ama3Prices;
434
446
  const LB_LOG_MIN = data.lbLogMin;
435
447
  const LB_LOG_MAX = data.lbLogMax;
436
448
  const lbSliderToVal = (pos) => Math.round(Math.exp(LB_LOG_MIN + (pos / 1000) * (LB_LOG_MAX - LB_LOG_MIN)));
@@ -469,6 +481,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
469
481
  const currentLatchedSignals = new Array(data.dates.length).fill(null);
470
482
  const dynamicAmaOff = new Array(data.dates.length).fill(null);
471
483
  const dynamicAmaSlopePct = new Array(data.dates.length).fill(null);
484
+ const dynamicAmaSlopePctRaw = new Array(data.dates.length).fill(null);
472
485
  const dynamicKalOff = new Array(data.dates.length).fill(null);
473
486
  const combinedOff = new Array(data.dates.length).fill(null);
474
487
  const combinedSell = new Array(data.dates.length).fill(null);
@@ -583,9 +596,40 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
583
596
  }
584
597
  }
585
598
 
586
- function computeSlopeAtIndex(idx, lb) {
599
+ /**
600
+ * Research-only ema knob — EMA low-pass over the AMA input before the
601
+ * slope is taken (EMA-then-difference == difference-then-EMA, both are
602
+ * linear filters). span bars, alpha = 2/(span+1): noise std shrinks ~
603
+ * 1/sqrt(span), lag grows ~ (span-1)/2 bars. Seeded with an SMA over the
604
+ * first 'span' finite values (mirrors the AMA seed SMA) so the transient
605
+ * is gone before any slope bar can go ready. Nulls (warmup / trailing
606
+ * padding) stay null so no fabricated values reach the clip-percentile
607
+ * pool or the offset pipeline. span <= 0 returns the input untouched.
608
+ */
609
+ function buildEmaSeries(values, span) {
610
+ if (!(span > 0) || !Array.isArray(values)) return values;
611
+ const n = values.length;
612
+ const out = new Array(n);
613
+ const alpha = 2 / (span + 1);
614
+ let seedSum = 0, seedCount = 0;
615
+ for (let i = 0; i < n && seedCount < span; i++) {
616
+ const v = values[i];
617
+ if (v == null || !Number.isFinite(v)) continue;
618
+ seedSum += v; seedCount++;
619
+ }
620
+ let prev = seedCount > 0 ? seedSum / seedCount : null;
621
+ for (let i = 0; i < n; i++) {
622
+ const v = values[i];
623
+ if (v == null || !Number.isFinite(v)) { out[i] = null; continue; }
624
+ prev = prev == null ? v : alpha * v + (1 - alpha) * prev;
625
+ out[i] = prev;
626
+ }
627
+ return out;
628
+ }
629
+
630
+ function computeSlopeAtIndex(idx, lb, values) {
587
631
  // Canonical AMA slope % (computeAverageAmaSlopePct injected above).
588
- const sp = computeAverageAmaSlopePct(data.ama3Prices[idx], data.ama3Prices[idx - lb], lb);
632
+ const sp = computeAverageAmaSlopePct(values[idx], values[idx - lb], lb);
589
633
  return sp == null ? 0 : sp;
590
634
  }
591
635
 
@@ -593,6 +637,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
593
637
  recalcKalmanVelocity();
594
638
  recalcKalmanClipThreshold();
595
639
  recalcLatchedSignals();
640
+ currentAmaSource = buildEmaSeries(data.ama3Prices, currentAmaEmaSpan);
596
641
  const lb = currentLookbackBars;
597
642
  const amaErWarmup = Math.max(0, Number.isFinite(data.amaErPeriod) ? Math.ceil(data.amaErPeriod) : ${JSON.stringify(MARKET_ADAPTER.AMAS[MARKET_ADAPTER.DEFAULT_AMA_KEY as keyof typeof MARKET_ADAPTER.AMAS].erPeriod)});
598
643
  const amaReadyBar = Math.max(lb, amaErWarmup + lb);
@@ -601,14 +646,20 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
601
646
  // implementation (computeAmaSlopeClipThreshold injected above),
602
647
  // shared with the live market adapter service. Trailing padded
603
648
  // nulls beyond realBarCount are skipped by its finiteness guards.
604
- dynamicClipThreshold = computeAmaSlopeClipThreshold(data.ama3Prices, amaErWarmup, lb, currentClipPct);
649
+ // Clip pool follows the (possibly EMA-filtered) AMA input so the
650
+ // percentile threshold matches the series the offsets derive from.
651
+ dynamicClipThreshold = computeAmaSlopeClipThreshold(currentAmaSource, amaErWarmup, lb, currentClipPct);
605
652
 
606
653
  for (let i = 0; i < data.realBarCount; i++) {
607
- // AMA raw slope % for the display panel (offsets computed in recalcWeights)
608
- dynamicAmaSlopePct[i] = i < amaReadyBar ? null : computeSlopeAtIndex(i, lb);
654
+ // AMA slope % for the display panel (offsets computed in
655
+ // recalcWeights): the raw reference line from the unfiltered AMA
656
+ // input, the plotted series from the EMA-filtered input.
657
+ dynamicAmaSlopePctRaw[i] = i < amaReadyBar ? null : computeSlopeAtIndex(i, lb, data.ama3Prices);
658
+ dynamicAmaSlopePct[i] = i < amaReadyBar ? null : computeSlopeAtIndex(i, lb, currentAmaSource);
609
659
  }
610
660
  for (let i = data.realBarCount; i < data.dates.length; i++) {
611
661
  dynamicAmaSlopePct[i] = null;
662
+ dynamicAmaSlopePctRaw[i] = null;
612
663
  }
613
664
  }
614
665
 
@@ -634,7 +685,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
634
685
  // Per-bar offset pipeline — canonical implementation (computeDynamicWeightSeries
635
686
  // injected above), shared with the live market adapter service.
636
687
  const res = computeDynamicWeightSeries({
637
- amaValues: data.ama3Prices,
688
+ amaValues: currentAmaSource,
638
689
  kalmanVelocityPct: currentKalmanVelocityPct,
639
690
  kalmanDisplacementPct: data.kalmanDisplacementPct,
640
691
  kalmanIsReady: data.kalmanIsReady,
@@ -718,7 +769,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
718
769
  max: Number.isFinite(xs.max) ? xs.max : xMax,
719
770
  } : null;
720
771
 
721
- amaChart.setData([data.dates, dynamicAmaSlopePct], false);
772
+ amaChart.setData([data.dates, dynamicAmaSlopePctRaw, dynamicAmaSlopePct], false);
722
773
  kalmanChart.setData([data.dates, currentKalmanVelocityPct, data.kalmanDisplacementPct], false);
723
774
  outputChart.setData([data.dates, combinedOff, echoCombinedOff], false);
724
775
 
@@ -753,6 +804,11 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
753
804
  if (sp == null) { spEl.textContent = '-'; spEl.style.color = '#8b949e'; }
754
805
  else { spEl.textContent = (sp >= 0 ? '+' : '') + (sp * 10).toFixed(3) + '‰'; spEl.style.color = sp > 0.01 ? '#2ea043' : sp < -0.01 ? '#f85149' : '#8b949e'; }
755
806
 
807
+ const spRaw = dynamicAmaSlopePctRaw[idx];
808
+ const spRawEl = document.getElementById('l-ama-slope-raw');
809
+ if (spRaw == null) { spRawEl.textContent = '-'; spRawEl.style.color = '#8b949e'; }
810
+ else { spRawEl.textContent = (spRaw >= 0 ? '+' : '') + (spRaw * 10).toFixed(3) + '‰'; spRawEl.style.color = '#8b949e'; }
811
+
756
812
  const vp = currentKalmanVelocityPct[idx];
757
813
  const vpEl = document.getElementById('l-kal-vel');
758
814
  if (vp == null) { vpEl.textContent = '-'; vpEl.style.color = '#8b949e'; }
@@ -976,6 +1032,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
976
1032
  document.getElementById('clip-slider').value = currentClipPct;
977
1033
  document.getElementById('nz-slider').value = Math.round(currentNz * 100);
978
1034
  document.getElementById('lb-slider').value = lbValToSlider(currentLookbackBars);
1035
+ document.getElementById('ema-slider').value = currentAmaEmaSpan;
979
1036
 
980
1037
  const priceEl = document.getElementById('price-panel');
981
1038
  const amaEl = document.getElementById('ama-panel');
@@ -1011,6 +1068,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
1011
1068
  scales: { x: { time: true }, p: { auto: true } },
1012
1069
  series: [
1013
1070
  { label: 'Time' },
1071
+ { label: 'Raw‰', stroke: '#8b949e', width: 1, dash: [3, 3], scale: 'p', points: { show: false } },
1014
1072
  { label: 'Slope‰', stroke: '#f0a000', width: 2, scale: 'p', points: { show: false } },
1015
1073
  ],
1016
1074
  axes: [
@@ -1020,7 +1078,7 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
1020
1078
  ],
1021
1079
  cursor: cursorCfg,
1022
1080
  hooks: { draw: [makePctFillHook(dynamicAmaSlopePct, 'p', 'rgba(46,160,67,0.20)', 'rgba(248,81,73,0.20)'), makeSignalBgHook('p')] }
1023
- }, [data.dates, dynamicAmaSlopePct], document.getElementById('ama-chart'));
1081
+ }, [data.dates, dynamicAmaSlopePctRaw, dynamicAmaSlopePct], document.getElementById('ama-chart'));
1024
1082
 
1025
1083
  kalmanChart = new uPlot({
1026
1084
  width: kalmanEl.offsetWidth, height: kalmanEl.offsetHeight,
@@ -1182,6 +1240,12 @@ function generateHTML(data: any, title = 'Dynamic Weight Research') {
1182
1240
  onSliderChange();
1183
1241
  });
1184
1242
 
1243
+ document.getElementById('ema-slider').addEventListener('input', (e) => {
1244
+ currentAmaEmaSpan = parseInt(e.target.value, 10);
1245
+ document.getElementById('ema-value').textContent = currentAmaEmaSpan;
1246
+ onSliderChange();
1247
+ });
1248
+
1185
1249
  document.getElementById('regime-slider').addEventListener('input', (e) => {
1186
1250
  currentRegimeSensitivity = parseInt(e.target.value, 10) / 100;
1187
1251
  document.getElementById('regime-value').textContent = currentRegimeSensitivity.toFixed(2);
@@ -1277,6 +1341,11 @@ function applyParams(p, btn) {
1277
1341
  document.getElementById('lb-slider').value = lbValToSlider(currentLookbackBars);
1278
1342
  document.getElementById('lb-value').textContent = currentLookbackBars;
1279
1343
  }
1344
+ if (p.amaEmaSpan != null) {
1345
+ currentAmaEmaSpan = Math.max(SR.amaEmaSpan[0], Math.min(SR.amaEmaSpan[1], Math.round(p.amaEmaSpan)));
1346
+ document.getElementById('ema-slider').value = currentAmaEmaSpan;
1347
+ document.getElementById('ema-value').textContent = currentAmaEmaSpan;
1348
+ }
1280
1349
  recalcInputs();
1281
1350
  recalcWeights();
1282
1351
  refreshChartsPreservingZoom();
@@ -1303,6 +1372,7 @@ function applyParams(p, btn) {
1303
1372
  regimeSensitivity: 'regime',
1304
1373
  dispWeight: 'dw',
1305
1374
  lookbackBars: 'lb',
1375
+ amaEmaSpan: 'ema',
1306
1376
  };
1307
1377
  document.getElementById('paste-confirm-vals').innerHTML = Object.entries(labels)
1308
1378
  .filter(([k]) => p[k] != null)
@@ -1356,6 +1426,7 @@ function applyParams(p, btn) {
1356
1426
  regimeSensitivity: +currentRegimeSensitivity.toFixed(2),
1357
1427
  dispWeight: +currentDw.toFixed(2),
1358
1428
  lookbackBars: currentLookbackBars,
1429
+ amaEmaSpan: currentAmaEmaSpan,
1359
1430
  };
1360
1431
  const json = JSON.stringify(params, null, 2);
1361
1432
  localStorage.setItem(LS_KEY, json);
@@ -3,7 +3,7 @@
3
3
  import { MARKET_ADAPTER } from '../../modules/constants.js';
4
4
  import { computeDynamicWeightSeries } from '../../market_adapter/core/strategies/dynamic_weight_series.js';
5
5
  import { computeAbsolutePercentileThreshold } from '../../market_adapter/core/signals/kalman_velocity_smoothing.js';
6
- import { escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT } from '../chart_utils.js';
6
+ import { escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT, uplotInlineTags } from '../chart_utils.js';
7
7
  import { sharedChartCSS } from '../chart_css.js';
8
8
  import { Y_AXIS_SIZE, makeCursorConfig, bindHoverStateFn, wireChartEvents, zoomResetScript, sizeChartsFn } from '../chart_ui.js';
9
9
 
@@ -94,8 +94,7 @@ function generateHTML(data: any, title = 'Kalman Trajectory Analysis') {
94
94
  <head>
95
95
  <meta charset="UTF-8">
96
96
  <title>${escapeHtml(title)}</title>
97
- <link rel="stylesheet" href="../uplot/uPlot.min.css">
98
- <script src="../uplot/uPlot.iife.min.js"></script>
97
+ ${uplotInlineTags()}
99
98
  <style>
100
99
  ${sharedChartCSS()}
101
100
  #price-panel { flex: 0 0 55%; min-height: 0; position: relative; border-bottom: 1px solid #30363d; }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trend-detection",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "Kalman filter trend detection with tactical/modal state tracking",
5
5
  "main": "../../dist/analysis/trend_detection/kalman_trend_analyzer.js",
6
6
  "scripts": {
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  import { MARKET_ADAPTER } from '../../modules/constants.js';
4
- import { escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT } from '../chart_utils.js';
4
+ import { escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT, uplotInlineTags } from '../chart_utils.js';
5
5
  import { sharedChartCSS } from '../chart_css.js';
6
6
  import { Y_AXIS_SIZE, makeCursorConfig, bindHoverStateFn, wireChartEvents, zoomResetScript, sizeChartsFn, fmtDateFn } from '../chart_ui.js';
7
7
 
@@ -91,8 +91,7 @@ function generateRegimeHTML(data: any, title = 'Regime Analysis') {
91
91
  <head>
92
92
  <meta charset="UTF-8">
93
93
  <title>${escapeHtml(title)}</title>
94
- <link rel="stylesheet" href="../uplot/uPlot.min.css">
95
- <script src="../uplot/uPlot.iife.min.js"></script>
94
+ ${uplotInlineTags()}
96
95
  <style>
97
96
  ${sharedChartCSS()}
98
97
  #price-panel { flex: 0 0 34%; min-height: 0; position: relative; border-bottom: 1px solid #30363d; }
@@ -3,7 +3,7 @@
3
3
  import { MARKET_ADAPTER } from '../../modules/constants.js';
4
4
  import { computeATRSeries } from '../../market_adapter/core/strategies/atr/calculator.js';
5
5
  import { computeVolatilityShift } from '../../market_adapter/core/strategies/volatility_shift.js';
6
- import { embedFunctionSources, escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT } from '../chart_utils.js';
6
+ import { embedFunctionSources, escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT, uplotInlineTags } from '../chart_utils.js';
7
7
  import { sharedChartCSS } from '../chart_css.js';
8
8
  import { Y_AXIS_SIZE, makeCursorConfig, bindHoverStateFn, wireChartEvents, zoomResetScript, sizeChartsFn } from '../chart_ui.js';
9
9
 
@@ -66,8 +66,7 @@ function generateHTML(data: any, title = 'ATR Volatility Research') {
66
66
  <meta name="color-scheme" content="dark">
67
67
  <meta name="darkreader-lock">
68
68
  <title>${escapeHtml(title)}</title>
69
- <link rel="stylesheet" href="../uplot/uPlot.min.css">
70
- <script src="../uplot/uPlot.iife.min.js"></script>
69
+ ${uplotInlineTags()}
71
70
  <style>
72
71
  ${sharedChartCSS()}
73
72
  :root { color-scheme: dark; }
@@ -7,7 +7,7 @@ import { Config } from '../../modules/config.js';
7
7
  import { PATHS } from '../../modules/paths.js';
8
8
  import { writeJsonFileAtomic as baseWriteJsonFileAtomic } from '../../modules/bots_file_lock.js';
9
9
  import { acquireFileLock } from '../../market_adapter/utils/file_lock.js';
10
- import { assertNoDuplicateBotKeys } from '../../modules/bot_settings.js';
10
+ import { assertNoDuplicateBotKeys, normalizeBotEntry } from '../../modules/bot_settings.js';
11
11
  import { BOT_LIVE_CONFIG_KEYS } from '../../modules/runtime_settings.js';
12
12
  import { clone } from './utils.js';
13
13
  import { createBotKey, sanitizeKey } from '../../modules/account_orders.js';
@@ -682,8 +682,9 @@ async function normalizeBotEntries(rawEntries: Record<string, any>[], options: P
682
682
  if (logger) {
683
683
  validateBotEntry(entry, index, logger);
684
684
  }
685
- const normalized = { ...entry, active: entry.active === undefined ? true : !!entry.active };
686
- results.push({ ...normalized, botIndex: index, botKey: createBotKey(normalized, index) });
685
+ // Shared seeder semantics with modules/bot_settings.normalizeBotEntry:
686
+ // missing active → DEFAULT_CONFIG.active, present values pass through raw.
687
+ results.push(normalizeBotEntry(entry, index));
687
688
  }
688
689
  return results;
689
690
  }
package/claw/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dexbot2-claw",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "private": true,
5
5
  "description": "Claw bridge and runtime integration layer for BitShares and DEXBot2.",
6
6
  "type": "module",
@@ -2,7 +2,7 @@
2
2
  "id": "bitshares-claw",
3
3
  "name": "BitShares Claw",
4
4
  "description": "Native BitShares and DEXBot2 tools from the DEXBot2 claw integration",
5
- "version": "1.6.3",
5
+ "version": "1.6.5",
6
6
  "skills": [
7
7
  "skills"
8
8
  ],
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bitshares-claw-openclaw-plugin",
3
3
  "private": true,
4
- "version": "1.6.3",
4
+ "version": "1.6.5",
5
5
  "type": "module",
6
6
  "main": "index.js"
7
7
  }
@@ -30,7 +30,7 @@ async function testParserAcceptsJsonlAcrossChunksAndSingleBuffer() {
30
30
  capabilities: {},
31
31
  clientInfo: {
32
32
  name: 'claw-mcp-transport-test',
33
- version: '1.6.3'
33
+ version: '1.6.5'
34
34
  },
35
35
  protocolVersion: '2024-11-05'
36
36
  }
@@ -187,7 +187,7 @@ async function testMainEntrypointHandlesRealProcessInitialize() {
187
187
  capabilities: {},
188
188
  clientInfo: {
189
189
  name: 'claw-mcp-transport-test',
190
- version: '1.6.3'
190
+ version: '1.6.5'
191
191
  },
192
192
  protocolVersion: '2024-11-05'
193
193
  }
@@ -4,7 +4,7 @@ const assert = require('assert');
4
4
  const fs = require('fs/promises');
5
5
  const os = require('os');
6
6
  const path = require('path');
7
- const { GRID_LIMITS } = require('../../modules/constants');
7
+ const { GRID_LIMITS, DEFAULT_CONFIG } = require('../../modules/constants');
8
8
 
9
9
  const {
10
10
  createBotKey,
@@ -608,8 +608,25 @@ async function testGeometryPatchKeepsTriggerByDefault() {
608
608
  await fs.access(result.triggerPath);
609
609
  }
610
610
 
611
+ async function testNormalizeBotEntriesDelegatesToSharedSeeder() {
612
+ console.log(' - Testing normalizeBotEntries delegates to shared bot defaults...');
613
+ const entries = await normalizeBotEntries([
614
+ { name: 'missing-active' },
615
+ { name: 'null-active', active: null },
616
+ { name: 'false-active', active: false },
617
+ ]);
618
+ assert.strictEqual(entries[0].active, DEFAULT_CONFIG.active,
619
+ 'missing active → DEFAULT_CONFIG.active (shared seeder, was hardcoded true)');
620
+ assert.strictEqual(entries[1].active, null,
621
+ 'present null passes through raw (claw used to coerce to false — unified in Phase 3)');
622
+ assert.strictEqual(entries[2].active, false, 'active:false preserved');
623
+ assert.strictEqual(entries[0].botIndex, 0, 'index stamped');
624
+ assert.ok(entries[0].botKey, 'botKey generated');
625
+ }
626
+
611
627
  async function main() {
612
628
  await testNormalizeAcceptsAssetIdAliases();
629
+ await testNormalizeBotEntriesDelegatesToSharedSeeder();
613
630
  testCreateBotKeyFallsBackToAssetIds();
614
631
  await testMatchBotIdentifierHandlesIdOnlyBots();
615
632
  await testAtomicWriteFailsFastWhenLockCannotBeAcquired();