dexbot 1.4.21 → 1.4.23

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 (601) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +8 -5
  3. package/analysis/ama_fitting/analyze_ama_price_changes.ts +7 -3
  4. package/analysis/ama_fitting/analyze_lambda_vs_slow.ts +6 -6
  5. package/analysis/ama_fitting/calibrate_convergence_er.ts +7 -5
  6. package/analysis/ama_fitting/fetch_lp_candles.ts +8 -3
  7. package/analysis/ama_fitting/generate_unified_comparison_chart.ts +30 -67
  8. package/analysis/ama_fitting/optimizer_high_resolution.ts +26 -17
  9. package/analysis/ama_fitting/package.json +1 -1
  10. package/analysis/analyze_derivatives.ts +4 -4
  11. package/analysis/analyze_dynamic_weight.ts +21 -5
  12. package/analysis/analyze_kalman.ts +54 -25
  13. package/analysis/analyze_regime.ts +2 -2
  14. package/analysis/analyze_regime_windows.ts +27 -19
  15. package/analysis/analyze_risk_profile.ts +3 -3
  16. package/analysis/analyze_trade_heatmap.ts +3 -3
  17. package/analysis/analyze_volatility.ts +2 -2
  18. package/analysis/bot_fitting/README.md +93 -19
  19. package/analysis/bot_fitting/backtest_ama_sweep.ts +316 -199
  20. package/analysis/bot_fitting/backtest_bot_fitting.ts +520 -85
  21. package/analysis/bot_fitting/shared_utils.ts +15 -9
  22. package/analysis/bot_key_utils.ts +13 -6
  23. package/analysis/bot_usage/discover_bot_accounts.ts +5 -3
  24. package/analysis/bot_usage/kibana_bot_queries.ts +1 -1
  25. package/analysis/chart_css.ts +6 -4
  26. package/analysis/chart_ui.ts +0 -1
  27. package/analysis/chart_utils.ts +11 -2
  28. package/analysis/derivative_chart_generator.ts +2 -2
  29. package/analysis/price_sources.ts +8 -2
  30. package/analysis/resolve_source.ts +1 -1
  31. package/analysis/trade_profitability.ts +61 -28
  32. package/analysis/tradingview/README.md +24 -4
  33. package/analysis/tradingview/analyze_tradingview.ts +1 -1
  34. package/analysis/tradingview/tradingview_uplot_chart_generator.ts +356 -86
  35. package/analysis/trend_detection/DYNAMIC_WEIGHT_RESEARCH.md +1 -1
  36. package/analysis/trend_detection/derivative_analyzer.ts +12 -3
  37. package/analysis/trend_detection/dynamic_weight_chart_generator.ts +25 -27
  38. package/analysis/trend_detection/hurst_analyzer.ts +1 -1
  39. package/analysis/trend_detection/kalman_chart_generator.ts +42 -14
  40. package/analysis/trend_detection/package.json +1 -1
  41. package/analysis/trend_detection/regime_chart_generator.ts +39 -19
  42. package/analysis/trend_detection/tests/test_kalman_trend.ts +1 -1
  43. package/analysis/trend_detection/tests/test_kalman_velocity_smoothing.ts +1 -1
  44. package/analysis/trend_detection/volatility_chart_generator.ts +1 -1
  45. package/claw/index.ts +1 -1
  46. package/claw/modules/claw_bridge.ts +3 -3
  47. package/claw/modules/claw_catalog.ts +1 -1
  48. package/claw/modules/claw_launcher.ts +1 -1
  49. package/claw/modules/claw_skill_md.ts +7 -10
  50. package/claw/modules/credit_runtime_adapter.ts +1 -1
  51. package/claw/modules/decision_loop.ts +5 -9
  52. package/claw/modules/dexbot_bridge.ts +7 -7
  53. package/claw/modules/feed_price_source.ts +1 -1
  54. package/claw/modules/kibana_price_source.ts +1 -1
  55. package/claw/modules/launcher_mode_detector.ts +1 -1
  56. package/claw/modules/launcher_paths.ts +1 -1
  57. package/claw/modules/position_discovery.ts +1 -1
  58. package/claw/modules/position_health.ts +1 -1
  59. package/claw/package.json +3 -3
  60. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  61. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  62. package/claw/skills/launcher-ops/references/launcher-workflow.md +2 -2
  63. package/claw/tests/package.json +1 -1
  64. package/claw/tests/test_claw_bridge.ts +45 -21
  65. package/claw/tests/test_claw_catalog_and_credentials.ts +8 -4
  66. package/claw/tests/test_claw_chain_layer.ts +39 -19
  67. package/claw/tests/test_claw_data_flow.ts +28 -20
  68. package/claw/tests/test_claw_domain_logic.ts +25 -19
  69. package/claw/tests/test_claw_manifest_and_matrix.ts +23 -3
  70. package/claw/tests/test_claw_mcp_transport.ts +13 -8
  71. package/claw/tests/test_claw_regressions.ts +254 -128
  72. package/claw/tests/test_claw_skill_generation.ts +1 -1
  73. package/claw/tests/test_nullclaw_tmp_integration.ts +2 -3
  74. package/claw/tests/test_position_health.ts +1 -77
  75. package/claw/tests/test_position_manager.ts +20 -18
  76. package/claw/tests/test_position_manager_watch_health.ts +39 -43
  77. package/claw/tests/test_short_mpa_strategy.ts +20 -17
  78. package/claw/tsconfig.json +4 -3
  79. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js +7 -3
  80. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js.map +1 -1
  81. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +6 -6
  82. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -1
  83. package/dist/analysis/ama_fitting/calibrate_convergence_er.js +7 -5
  84. package/dist/analysis/ama_fitting/calibrate_convergence_er.js.map +1 -1
  85. package/dist/analysis/ama_fitting/fetch_lp_candles.js +8 -3
  86. package/dist/analysis/ama_fitting/fetch_lp_candles.js.map +1 -1
  87. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts +2 -2
  88. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts.map +1 -1
  89. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js +31 -75
  90. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js.map +1 -1
  91. package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts.map +1 -1
  92. package/dist/analysis/ama_fitting/optimizer_high_resolution.js +25 -17
  93. package/dist/analysis/ama_fitting/optimizer_high_resolution.js.map +1 -1
  94. package/dist/analysis/analyze_derivatives.d.ts +0 -18
  95. package/dist/analysis/analyze_derivatives.d.ts.map +1 -1
  96. package/dist/analysis/analyze_derivatives.js +4 -4
  97. package/dist/analysis/analyze_derivatives.js.map +1 -1
  98. package/dist/analysis/analyze_dynamic_weight.d.ts +0 -11
  99. package/dist/analysis/analyze_dynamic_weight.d.ts.map +1 -1
  100. package/dist/analysis/analyze_dynamic_weight.js +19 -5
  101. package/dist/analysis/analyze_dynamic_weight.js.map +1 -1
  102. package/dist/analysis/analyze_kalman.d.ts +0 -10
  103. package/dist/analysis/analyze_kalman.d.ts.map +1 -1
  104. package/dist/analysis/analyze_kalman.js +51 -26
  105. package/dist/analysis/analyze_kalman.js.map +1 -1
  106. package/dist/analysis/analyze_regime.d.ts +0 -17
  107. package/dist/analysis/analyze_regime.d.ts.map +1 -1
  108. package/dist/analysis/analyze_regime.js +2 -2
  109. package/dist/analysis/analyze_regime.js.map +1 -1
  110. package/dist/analysis/analyze_regime_windows.d.ts +0 -16
  111. package/dist/analysis/analyze_regime_windows.d.ts.map +1 -1
  112. package/dist/analysis/analyze_regime_windows.js +28 -21
  113. package/dist/analysis/analyze_regime_windows.js.map +1 -1
  114. package/dist/analysis/analyze_risk_profile.js +3 -3
  115. package/dist/analysis/analyze_risk_profile.js.map +1 -1
  116. package/dist/analysis/analyze_trade_heatmap.js +3 -3
  117. package/dist/analysis/analyze_trade_heatmap.js.map +1 -1
  118. package/dist/analysis/analyze_volatility.d.ts +0 -19
  119. package/dist/analysis/analyze_volatility.d.ts.map +1 -1
  120. package/dist/analysis/analyze_volatility.js +2 -2
  121. package/dist/analysis/analyze_volatility.js.map +1 -1
  122. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts +30 -14
  123. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts.map +1 -1
  124. package/dist/analysis/bot_fitting/backtest_ama_sweep.js +330 -193
  125. package/dist/analysis/bot_fitting/backtest_ama_sweep.js.map +1 -1
  126. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts +136 -1
  127. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts.map +1 -1
  128. package/dist/analysis/bot_fitting/backtest_bot_fitting.js +527 -83
  129. package/dist/analysis/bot_fitting/backtest_bot_fitting.js.map +1 -1
  130. package/dist/analysis/bot_fitting/shared_utils.d.ts +1 -1
  131. package/dist/analysis/bot_fitting/shared_utils.d.ts.map +1 -1
  132. package/dist/analysis/bot_fitting/shared_utils.js +14 -8
  133. package/dist/analysis/bot_fitting/shared_utils.js.map +1 -1
  134. package/dist/analysis/bot_key_utils.d.ts.map +1 -1
  135. package/dist/analysis/bot_key_utils.js +13 -6
  136. package/dist/analysis/bot_key_utils.js.map +1 -1
  137. package/dist/analysis/bot_usage/discover_bot_accounts.js +5 -3
  138. package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
  139. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
  140. package/dist/analysis/bot_usage/kibana_bot_queries.js +1 -1
  141. package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
  142. package/dist/analysis/chart_css.d.ts +3 -1
  143. package/dist/analysis/chart_css.d.ts.map +1 -1
  144. package/dist/analysis/chart_css.js +6 -3
  145. package/dist/analysis/chart_css.js.map +1 -1
  146. package/dist/analysis/chart_ui.d.ts.map +1 -1
  147. package/dist/analysis/chart_ui.js.map +1 -1
  148. package/dist/analysis/chart_utils.d.ts.map +1 -1
  149. package/dist/analysis/chart_utils.js +15 -2
  150. package/dist/analysis/chart_utils.js.map +1 -1
  151. package/dist/analysis/derivative_chart_generator.js +2 -2
  152. package/dist/analysis/derivative_chart_generator.js.map +1 -1
  153. package/dist/analysis/price_sources.d.ts +1 -0
  154. package/dist/analysis/price_sources.d.ts.map +1 -1
  155. package/dist/analysis/price_sources.js +8 -2
  156. package/dist/analysis/price_sources.js.map +1 -1
  157. package/dist/analysis/resolve_source.d.ts.map +1 -1
  158. package/dist/analysis/resolve_source.js +1 -1
  159. package/dist/analysis/resolve_source.js.map +1 -1
  160. package/dist/analysis/trade_profitability.d.ts.map +1 -1
  161. package/dist/analysis/trade_profitability.js +58 -29
  162. package/dist/analysis/trade_profitability.js.map +1 -1
  163. package/dist/analysis/tradingview/analyze_tradingview.js +1 -1
  164. package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
  165. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
  166. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +356 -86
  167. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
  168. package/dist/analysis/trend_detection/derivative_analyzer.d.ts +1 -0
  169. package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +1 -1
  170. package/dist/analysis/trend_detection/derivative_analyzer.js +12 -3
  171. package/dist/analysis/trend_detection/derivative_analyzer.js.map +1 -1
  172. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -1
  173. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +26 -27
  174. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -1
  175. package/dist/analysis/trend_detection/hurst_analyzer.d.ts +1 -1
  176. package/dist/analysis/trend_detection/hurst_analyzer.d.ts.map +1 -1
  177. package/dist/analysis/trend_detection/hurst_analyzer.js +1 -1
  178. package/dist/analysis/trend_detection/hurst_analyzer.js.map +1 -1
  179. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -1
  180. package/dist/analysis/trend_detection/kalman_chart_generator.js +41 -13
  181. package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -1
  182. package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -1
  183. package/dist/analysis/trend_detection/regime_chart_generator.js +38 -19
  184. package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -1
  185. package/dist/analysis/trend_detection/tests/test_kalman_trend.js +1 -1
  186. package/dist/analysis/trend_detection/tests/test_kalman_trend.js.map +1 -1
  187. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js +1 -1
  188. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js.map +1 -1
  189. package/dist/analysis/trend_detection/volatility_chart_generator.js +1 -1
  190. package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -1
  191. package/dist/bot.js +1 -1
  192. package/dist/bot.js.map +1 -1
  193. package/dist/credential-daemon.d.ts +1 -1
  194. package/dist/credential-daemon.js +1 -1
  195. package/dist/dexbot.d.ts.map +1 -1
  196. package/dist/dexbot.js +7 -8
  197. package/dist/dexbot.js.map +1 -1
  198. package/dist/market_adapter/ama_signal_runner.js +3 -1
  199. package/dist/market_adapter/ama_signal_runner.js.map +1 -1
  200. package/dist/market_adapter/candle_utils.d.ts +1 -3
  201. package/dist/market_adapter/candle_utils.d.ts.map +1 -1
  202. package/dist/market_adapter/candle_utils.js +1 -11
  203. package/dist/market_adapter/candle_utils.js.map +1 -1
  204. package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -1
  205. package/dist/market_adapter/core/asymmetric_bounds.js +33 -30
  206. package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -1
  207. package/dist/market_adapter/core/config_normalizers.d.ts.map +1 -1
  208. package/dist/market_adapter/core/config_normalizers.js +10 -1
  209. package/dist/market_adapter/core/config_normalizers.js.map +1 -1
  210. package/dist/market_adapter/core/kibana_candles.d.ts +18 -42
  211. package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -1
  212. package/dist/market_adapter/core/kibana_candles.js +101 -7
  213. package/dist/market_adapter/core/kibana_candles.js.map +1 -1
  214. package/dist/market_adapter/core/kibana_client.d.ts +0 -14
  215. package/dist/market_adapter/core/kibana_client.d.ts.map +1 -1
  216. package/dist/market_adapter/core/kibana_client.js +39 -6
  217. package/dist/market_adapter/core/kibana_client.js.map +1 -1
  218. package/dist/market_adapter/core/kibana_market_candles.d.ts +0 -27
  219. package/dist/market_adapter/core/kibana_market_candles.d.ts.map +1 -1
  220. package/dist/market_adapter/core/kibana_market_candles.js +1 -1
  221. package/dist/market_adapter/core/kibana_market_candles.js.map +1 -1
  222. package/dist/market_adapter/core/market_adapter_service.d.ts +21 -11
  223. package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -1
  224. package/dist/market_adapter/core/market_adapter_service.js +95 -37
  225. package/dist/market_adapter/core/market_adapter_service.js.map +1 -1
  226. package/dist/market_adapter/core/signals/hurst_analyzer.d.ts +10 -1
  227. package/dist/market_adapter/core/signals/hurst_analyzer.d.ts.map +1 -1
  228. package/dist/market_adapter/core/signals/hurst_analyzer.js +28 -17
  229. package/dist/market_adapter/core/signals/hurst_analyzer.js.map +1 -1
  230. package/dist/market_adapter/core/signals/kalman_trend_analyzer.d.ts +5 -0
  231. package/dist/market_adapter/core/signals/kalman_trend_analyzer.d.ts.map +1 -1
  232. package/dist/market_adapter/core/signals/kalman_trend_analyzer.js +24 -24
  233. package/dist/market_adapter/core/signals/kalman_trend_analyzer.js.map +1 -1
  234. package/dist/market_adapter/core/signals/kalman_velocity_smoothing.d.ts.map +1 -1
  235. package/dist/market_adapter/core/signals/kalman_velocity_smoothing.js +5 -1
  236. package/dist/market_adapter/core/signals/kalman_velocity_smoothing.js.map +1 -1
  237. package/dist/market_adapter/core/signals/permutation_entropy_analyzer.d.ts.map +1 -1
  238. package/dist/market_adapter/core/signals/permutation_entropy_analyzer.js +20 -3
  239. package/dist/market_adapter/core/signals/permutation_entropy_analyzer.js.map +1 -1
  240. package/dist/market_adapter/core/strategies/ama.js +1 -1
  241. package/dist/market_adapter/core/strategies/ama.js.map +1 -1
  242. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts +2 -2
  243. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts.map +1 -1
  244. package/dist/market_adapter/core/strategies/ama_slope_model.js +16 -4
  245. package/dist/market_adapter/core/strategies/ama_slope_model.js.map +1 -1
  246. package/dist/market_adapter/core/strategies/atr/calculator.d.ts +4 -3
  247. package/dist/market_adapter/core/strategies/atr/calculator.d.ts.map +1 -1
  248. package/dist/market_adapter/core/strategies/atr/calculator.js +16 -8
  249. package/dist/market_adapter/core/strategies/atr/calculator.js.map +1 -1
  250. package/dist/market_adapter/core/strategies/collateral_manager.d.ts.map +1 -1
  251. package/dist/market_adapter/core/strategies/collateral_manager.js +8 -3
  252. package/dist/market_adapter/core/strategies/collateral_manager.js.map +1 -1
  253. package/dist/market_adapter/core/strategies/dynamic_weight_series.d.ts +40 -1
  254. package/dist/market_adapter/core/strategies/dynamic_weight_series.d.ts.map +1 -1
  255. package/dist/market_adapter/core/strategies/dynamic_weight_series.js +116 -2
  256. package/dist/market_adapter/core/strategies/dynamic_weight_series.js.map +1 -1
  257. package/dist/market_adapter/core/strategies/regime_gate.d.ts.map +1 -1
  258. package/dist/market_adapter/core/strategies/regime_gate.js +26 -20
  259. package/dist/market_adapter/core/strategies/regime_gate.js.map +1 -1
  260. package/dist/market_adapter/core/strategies/volatility_shift.d.ts.map +1 -1
  261. package/dist/market_adapter/core/strategies/volatility_shift.js +3 -0
  262. package/dist/market_adapter/core/strategies/volatility_shift.js.map +1 -1
  263. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +69 -55
  264. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -1
  265. package/dist/market_adapter/inputs/fetch_lp_data.d.ts +0 -26
  266. package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
  267. package/dist/market_adapter/inputs/fetch_lp_data.js +67 -14
  268. package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
  269. package/dist/market_adapter/inputs/kibana_source.d.ts +4 -28
  270. package/dist/market_adapter/inputs/kibana_source.d.ts.map +1 -1
  271. package/dist/market_adapter/inputs/kibana_source.js +5 -2
  272. package/dist/market_adapter/inputs/kibana_source.js.map +1 -1
  273. package/dist/market_adapter/lp_chart_core.js +1 -1
  274. package/dist/market_adapter/lp_chart_core.js.map +1 -1
  275. package/dist/market_adapter/lp_chart_runner.d.ts +10 -1
  276. package/dist/market_adapter/lp_chart_runner.d.ts.map +1 -1
  277. package/dist/market_adapter/lp_chart_runner.js +2 -2
  278. package/dist/market_adapter/lp_chart_runner.js.map +1 -1
  279. package/dist/market_adapter/lp_chart_strategy_loader.js +1 -1
  280. package/dist/market_adapter/lp_chart_strategy_loader.js.map +1 -1
  281. package/dist/market_adapter/market_adapter.d.ts +1 -2
  282. package/dist/market_adapter/market_adapter.d.ts.map +1 -1
  283. package/dist/market_adapter/market_adapter.js +9 -8
  284. package/dist/market_adapter/market_adapter.js.map +1 -1
  285. package/dist/market_adapter/test_helpers.d.ts +3 -3
  286. package/dist/market_adapter/test_helpers.d.ts.map +1 -1
  287. package/dist/market_adapter/test_helpers.js +3 -3
  288. package/dist/market_adapter/test_helpers.js.map +1 -1
  289. package/dist/market_adapter/utils/adapter_client.js +1 -1
  290. package/dist/market_adapter/utils/adapter_client.js.map +1 -1
  291. package/dist/market_adapter/utils/atomic_write.js +1 -1
  292. package/dist/market_adapter/utils/atomic_write.js.map +1 -1
  293. package/dist/market_adapter/utils/chain.d.ts +0 -2
  294. package/dist/market_adapter/utils/chain.d.ts.map +1 -1
  295. package/dist/market_adapter/utils/chain.js +2 -3
  296. package/dist/market_adapter/utils/chain.js.map +1 -1
  297. package/dist/market_adapter/utils/data_discovery.d.ts.map +1 -1
  298. package/dist/market_adapter/utils/data_discovery.js +24 -8
  299. package/dist/market_adapter/utils/data_discovery.js.map +1 -1
  300. package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts.map +1 -1
  301. package/dist/market_adapter/utils/dynamic_grid_snapshot.js +2 -5
  302. package/dist/market_adapter/utils/dynamic_grid_snapshot.js.map +1 -1
  303. package/dist/market_adapter/utils/file_lock.d.ts +4 -1
  304. package/dist/market_adapter/utils/file_lock.d.ts.map +1 -1
  305. package/dist/market_adapter/utils/file_lock.js +89 -25
  306. package/dist/market_adapter/utils/file_lock.js.map +1 -1
  307. package/dist/market_adapter/utils/native_history.d.ts.map +1 -1
  308. package/dist/market_adapter/utils/native_history.js +5 -1
  309. package/dist/market_adapter/utils/native_history.js.map +1 -1
  310. package/dist/modules/account_bots.d.ts.map +1 -1
  311. package/dist/modules/account_bots.js +47 -12
  312. package/dist/modules/account_bots.js.map +1 -1
  313. package/dist/modules/authority_resolver.js +1 -1
  314. package/dist/modules/authority_resolver.js.map +1 -1
  315. package/dist/modules/bitshares-native/chain_client.js +1 -1
  316. package/dist/modules/bitshares-native/chain_client.js.map +1 -1
  317. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts +0 -12
  318. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -1
  319. package/dist/modules/bitshares-native/crypto/ecc.browser.js +1 -1
  320. package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -1
  321. package/dist/modules/bitshares-native/crypto/ecc_selector.js +1 -1
  322. package/dist/modules/bitshares-native/crypto/ecc_selector.js.map +1 -1
  323. package/dist/modules/bitshares-native/index.d.ts.map +1 -1
  324. package/dist/modules/bitshares-native/index.js +1 -1
  325. package/dist/modules/bitshares-native/index.js.map +1 -1
  326. package/dist/modules/bitshares-native/lru_cache.js +1 -1
  327. package/dist/modules/bitshares-native/lru_cache.js.map +1 -1
  328. package/dist/modules/bitshares-native/resolvers.d.ts +0 -2
  329. package/dist/modules/bitshares-native/resolvers.d.ts.map +1 -1
  330. package/dist/modules/bitshares-native/resolvers.js +18 -4
  331. package/dist/modules/bitshares-native/resolvers.js.map +1 -1
  332. package/dist/modules/bitshares-native/serial/chain_constants.js +1 -1
  333. package/dist/modules/bitshares-native/serial/chain_constants.js.map +1 -1
  334. package/dist/modules/bitshares-native/serial/index.d.ts.map +1 -1
  335. package/dist/modules/bitshares-native/serial/index.js +1 -1
  336. package/dist/modules/bitshares-native/serial/index.js.map +1 -1
  337. package/dist/modules/bitshares-native/serial/operations.js +1 -1
  338. package/dist/modules/bitshares-native/serial/operations.js.map +1 -1
  339. package/dist/modules/bitshares-native/serial/serializer.js +1 -1
  340. package/dist/modules/bitshares-native/serial/serializer.js.map +1 -1
  341. package/dist/modules/bitshares-native/serial/types.js +1 -1
  342. package/dist/modules/bitshares-native/serial/types.js.map +1 -1
  343. package/dist/modules/bitshares-native/signing_client.js +2 -2
  344. package/dist/modules/bitshares-native/signing_client.js.map +1 -1
  345. package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
  346. package/dist/modules/bitshares-native/subscriptions.js +5 -14
  347. package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
  348. package/dist/modules/bitshares-native/transport.js +1 -1
  349. package/dist/modules/bitshares-native/transport.js.map +1 -1
  350. package/dist/modules/bitshares-native/tx/builder.js +1 -1
  351. package/dist/modules/bitshares-native/tx/builder.js.map +1 -1
  352. package/dist/modules/bitshares-native/tx/tx_cache.d.ts +3 -2
  353. package/dist/modules/bitshares-native/tx/tx_cache.d.ts.map +1 -1
  354. package/dist/modules/bitshares-native/tx/tx_cache.js +7 -4
  355. package/dist/modules/bitshares-native/tx/tx_cache.js.map +1 -1
  356. package/dist/modules/bitshares_client.js +10 -10
  357. package/dist/modules/bitshares_client.js.map +1 -1
  358. package/dist/modules/bot_settings.d.ts.map +1 -1
  359. package/dist/modules/bot_settings.js +11 -18
  360. package/dist/modules/bot_settings.js.map +1 -1
  361. package/dist/modules/bots_file_lock.d.ts +1 -1
  362. package/dist/modules/bots_file_lock.js +1 -1
  363. package/dist/modules/broadcast_failure.d.ts +13 -0
  364. package/dist/modules/broadcast_failure.d.ts.map +1 -1
  365. package/dist/modules/broadcast_failure.js +0 -13
  366. package/dist/modules/broadcast_failure.js.map +1 -1
  367. package/dist/modules/chain_orders.d.ts.map +1 -1
  368. package/dist/modules/chain_orders.js +15 -17
  369. package/dist/modules/chain_orders.js.map +1 -1
  370. package/dist/modules/config.js +1 -1
  371. package/dist/modules/config.js.map +1 -1
  372. package/dist/modules/constants.d.ts +21 -20
  373. package/dist/modules/constants.d.ts.map +1 -1
  374. package/dist/modules/constants.js +60 -63
  375. package/dist/modules/constants.js.map +1 -1
  376. package/dist/modules/cr_planner.d.ts +1 -7
  377. package/dist/modules/cr_planner.d.ts.map +1 -1
  378. package/dist/modules/cr_planner.js +3 -77
  379. package/dist/modules/cr_planner.js.map +1 -1
  380. package/dist/modules/credential_policy.d.ts +0 -4
  381. package/dist/modules/credential_policy.d.ts.map +1 -1
  382. package/dist/modules/credential_policy.js +17 -2
  383. package/dist/modules/credential_policy.js.map +1 -1
  384. package/dist/modules/credit_runtime.d.ts +0 -7
  385. package/dist/modules/credit_runtime.d.ts.map +1 -1
  386. package/dist/modules/credit_runtime.js +21 -60
  387. package/dist/modules/credit_runtime.js.map +1 -1
  388. package/dist/modules/crypto/index.d.ts +0 -2
  389. package/dist/modules/crypto/index.d.ts.map +1 -1
  390. package/dist/modules/crypto/index.js +0 -2
  391. package/dist/modules/crypto/index.js.map +1 -1
  392. package/dist/modules/crypto/sync.js.map +1 -1
  393. package/dist/modules/daemon_node_health.js +1 -1
  394. package/dist/modules/daemon_node_health.js.map +1 -1
  395. package/dist/modules/dexbot_class.d.ts +8 -134
  396. package/dist/modules/dexbot_class.d.ts.map +1 -1
  397. package/dist/modules/dexbot_class.js +36 -197
  398. package/dist/modules/dexbot_class.js.map +1 -1
  399. package/dist/modules/dexbot_credential_client.js +1 -1
  400. package/dist/modules/dexbot_credential_client.js.map +1 -1
  401. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
  402. package/dist/modules/dexbot_fill_runtime.js +3 -6
  403. package/dist/modules/dexbot_fill_runtime.js.map +1 -1
  404. package/dist/modules/dexbot_maintenance_runtime.d.ts +6 -34
  405. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  406. package/dist/modules/dexbot_maintenance_runtime.js +27 -19
  407. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  408. package/dist/modules/dexbot_startup_runtime.d.ts.map +1 -1
  409. package/dist/modules/dexbot_startup_runtime.js +31 -15
  410. package/dist/modules/dexbot_startup_runtime.js.map +1 -1
  411. package/dist/modules/dexbot_state_recovery.d.ts +2 -2
  412. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  413. package/dist/modules/dexbot_state_recovery.js +9 -6
  414. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  415. package/dist/modules/fund_registry.d.ts.map +1 -1
  416. package/dist/modules/fund_registry.js +19 -4
  417. package/dist/modules/fund_registry.js.map +1 -1
  418. package/dist/modules/general_settings.d.ts +1 -2
  419. package/dist/modules/general_settings.d.ts.map +1 -1
  420. package/dist/modules/general_settings.js +1 -2
  421. package/dist/modules/general_settings.js.map +1 -1
  422. package/dist/modules/graceful_shutdown.d.ts.map +1 -1
  423. package/dist/modules/graceful_shutdown.js +10 -1
  424. package/dist/modules/graceful_shutdown.js.map +1 -1
  425. package/dist/modules/grid_price_source.d.ts +19 -0
  426. package/dist/modules/grid_price_source.d.ts.map +1 -0
  427. package/dist/modules/grid_price_source.js +23 -0
  428. package/dist/modules/grid_price_source.js.map +1 -0
  429. package/dist/modules/key_store.d.ts +0 -5
  430. package/dist/modules/key_store.d.ts.map +1 -1
  431. package/dist/modules/key_store.js +1 -14
  432. package/dist/modules/key_store.js.map +1 -1
  433. package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
  434. package/dist/modules/launcher/bot_supervisor.js +9 -19
  435. package/dist/modules/launcher/bot_supervisor.js.map +1 -1
  436. package/dist/modules/launcher/child_env.d.ts +1 -2
  437. package/dist/modules/launcher/child_env.d.ts.map +1 -1
  438. package/dist/modules/launcher/child_env.js +1 -1
  439. package/dist/modules/launcher/child_env.js.map +1 -1
  440. package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -1
  441. package/dist/modules/launcher/foreign_cred_daemon.js +2 -14
  442. package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -1
  443. package/dist/modules/launcher/market_adapter_runtime.d.ts +1 -1
  444. package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -1
  445. package/dist/modules/launcher/market_adapter_runtime.js +3 -14
  446. package/dist/modules/launcher/market_adapter_runtime.js.map +1 -1
  447. package/dist/modules/launcher/market_adapter_watchdog.d.ts.map +1 -1
  448. package/dist/modules/launcher/market_adapter_watchdog.js +3 -2
  449. package/dist/modules/launcher/market_adapter_watchdog.js.map +1 -1
  450. package/dist/modules/launcher/monolithic_runtime.d.ts +0 -1
  451. package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
  452. package/dist/modules/launcher/monolithic_runtime.js +3 -18
  453. package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
  454. package/dist/modules/launcher/runtime_entry.d.ts.map +1 -1
  455. package/dist/modules/launcher/runtime_entry.js +4 -5
  456. package/dist/modules/launcher/runtime_entry.js.map +1 -1
  457. package/dist/modules/launcher/status_reporting.js +1 -1
  458. package/dist/modules/launcher/status_reporting.js.map +1 -1
  459. package/dist/modules/launcher/supervisor_control.js +1 -1
  460. package/dist/modules/launcher/supervisor_control.js.map +1 -1
  461. package/dist/modules/market_adapter_whitelist.d.ts +2 -2
  462. package/dist/modules/market_adapter_whitelist.d.ts.map +1 -1
  463. package/dist/modules/market_adapter_whitelist.js +12 -6
  464. package/dist/modules/market_adapter_whitelist.js.map +1 -1
  465. package/dist/modules/node_failure_ledger.d.ts +23 -0
  466. package/dist/modules/node_failure_ledger.d.ts.map +1 -1
  467. package/dist/modules/node_failure_ledger.js +0 -23
  468. package/dist/modules/node_failure_ledger.js.map +1 -1
  469. package/dist/modules/node_manager.d.ts.map +1 -1
  470. package/dist/modules/node_manager.js +3 -3
  471. package/dist/modules/node_manager.js.map +1 -1
  472. package/dist/modules/order/accounting.js +1 -1
  473. package/dist/modules/order/accounting.js.map +1 -1
  474. package/dist/modules/order/async_lock.d.ts +6 -0
  475. package/dist/modules/order/async_lock.d.ts.map +1 -1
  476. package/dist/modules/order/async_lock.js +11 -0
  477. package/dist/modules/order/async_lock.js.map +1 -1
  478. package/dist/modules/order/export.d.ts +1 -1
  479. package/dist/modules/order/export.js +10 -10
  480. package/dist/modules/order/export.js.map +1 -1
  481. package/dist/modules/order/format.d.ts +1 -0
  482. package/dist/modules/order/format.d.ts.map +1 -1
  483. package/dist/modules/order/format.js +0 -6
  484. package/dist/modules/order/format.js.map +1 -1
  485. package/dist/modules/order/grid.d.ts +2 -12
  486. package/dist/modules/order/grid.d.ts.map +1 -1
  487. package/dist/modules/order/grid.js +40 -27
  488. package/dist/modules/order/grid.js.map +1 -1
  489. package/dist/modules/order/grid_reconcile.d.ts +1 -1
  490. package/dist/modules/order/grid_reconcile.js +1 -1
  491. package/dist/modules/order/grid_reconcile_internal.js +1 -1
  492. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  493. package/dist/modules/order/logger.d.ts +3 -0
  494. package/dist/modules/order/logger.d.ts.map +1 -1
  495. package/dist/modules/order/logger.js +22 -13
  496. package/dist/modules/order/logger.js.map +1 -1
  497. package/dist/modules/order/logger_state.d.ts +4 -9
  498. package/dist/modules/order/logger_state.d.ts.map +1 -1
  499. package/dist/modules/order/logger_state.js +5 -14
  500. package/dist/modules/order/logger_state.js.map +1 -1
  501. package/dist/modules/order/manager.d.ts +1 -4
  502. package/dist/modules/order/manager.d.ts.map +1 -1
  503. package/dist/modules/order/manager.js +34 -33
  504. package/dist/modules/order/manager.js.map +1 -1
  505. package/dist/modules/order/processed_fill_store.d.ts.map +1 -1
  506. package/dist/modules/order/processed_fill_store.js +9 -3
  507. package/dist/modules/order/processed_fill_store.js.map +1 -1
  508. package/dist/modules/order/strategy.d.ts +6 -4
  509. package/dist/modules/order/strategy.d.ts.map +1 -1
  510. package/dist/modules/order/strategy.js +6 -4
  511. package/dist/modules/order/strategy.js.map +1 -1
  512. package/dist/modules/order/sync_engine.js +7 -7
  513. package/dist/modules/order/sync_engine.js.map +1 -1
  514. package/dist/modules/order/utils/math.d.ts +29 -3
  515. package/dist/modules/order/utils/math.d.ts.map +1 -1
  516. package/dist/modules/order/utils/math.js +54 -10
  517. package/dist/modules/order/utils/math.js.map +1 -1
  518. package/dist/modules/order/utils/order.d.ts.map +1 -1
  519. package/dist/modules/order/utils/order.js +14 -2
  520. package/dist/modules/order/utils/order.js.map +1 -1
  521. package/dist/modules/order/utils/system.d.ts +3 -8
  522. package/dist/modules/order/utils/system.d.ts.map +1 -1
  523. package/dist/modules/order/utils/system.js +14 -4
  524. package/dist/modules/order/utils/system.js.map +1 -1
  525. package/dist/modules/order/utils/validate.js +1 -1
  526. package/dist/modules/order/utils/validate.js.map +1 -1
  527. package/dist/modules/path_api.js +1 -1
  528. package/dist/modules/path_api.js.map +1 -1
  529. package/dist/modules/paths.d.ts +1 -7
  530. package/dist/modules/paths.d.ts.map +1 -1
  531. package/dist/modules/paths.js +1 -1
  532. package/dist/modules/paths.js.map +1 -1
  533. package/dist/modules/process_discovery.d.ts +3 -0
  534. package/dist/modules/process_discovery.d.ts.map +1 -1
  535. package/dist/modules/process_discovery.js +15 -1
  536. package/dist/modules/process_discovery.js.map +1 -1
  537. package/dist/modules/runtime_settings.js +1 -1
  538. package/dist/modules/runtime_settings.js.map +1 -1
  539. package/dist/modules/settings_merge.d.ts +5 -1
  540. package/dist/modules/settings_merge.d.ts.map +1 -1
  541. package/dist/modules/settings_merge.js +14 -3
  542. package/dist/modules/settings_merge.js.map +1 -1
  543. package/dist/modules/socket_json_client.d.ts.map +1 -1
  544. package/dist/modules/socket_json_client.js +12 -2
  545. package/dist/modules/socket_json_client.js.map +1 -1
  546. package/dist/modules/storage/browser_adapter.d.ts.map +1 -1
  547. package/dist/modules/storage/browser_adapter.js +5 -3
  548. package/dist/modules/storage/browser_adapter.js.map +1 -1
  549. package/dist/modules/storage/index.d.ts +1 -1
  550. package/dist/modules/storage/index.js +1 -1
  551. package/dist/modules/types.d.ts +1 -1
  552. package/dist/modules/types.d.ts.map +1 -1
  553. package/dist/modules/validate_profiles.d.ts.map +1 -1
  554. package/dist/modules/validate_profiles.js +6 -8
  555. package/dist/modules/validate_profiles.js.map +1 -1
  556. package/dist/pm2.d.ts.map +1 -1
  557. package/dist/pm2.js +2 -3
  558. package/dist/pm2.js.map +1 -1
  559. package/dist/scripts/analyze-git.d.ts +1 -1
  560. package/dist/scripts/analyze-git.js +1 -1
  561. package/dist/scripts/analyze-orders.d.ts.map +1 -1
  562. package/dist/scripts/analyze-orders.js +61 -18
  563. package/dist/scripts/analyze-orders.js.map +1 -1
  564. package/dist/scripts/diagnose-pool-history.js +1 -1
  565. package/dist/scripts/divergence-calc.d.ts +1 -1
  566. package/dist/scripts/divergence-calc.js +3 -3
  567. package/dist/scripts/native_release_gates.js +4 -2
  568. package/dist/scripts/native_release_gates.js.map +1 -1
  569. package/dist/scripts/print_grid.d.ts +1 -1
  570. package/dist/scripts/print_grid.js +1 -1
  571. package/dist/scripts/run-tests.js +38 -19
  572. package/dist/scripts/run-tests.js.map +1 -1
  573. package/dist/scripts/runner.d.ts +3 -5
  574. package/dist/scripts/runner.d.ts.map +1 -1
  575. package/dist/scripts/runner.js +3 -5
  576. package/dist/scripts/runner.js.map +1 -1
  577. package/dist/scripts/sync-version.d.ts +2 -2
  578. package/dist/scripts/sync-version.js +7 -4
  579. package/dist/scripts/sync-version.js.map +1 -1
  580. package/dist/scripts/update.d.ts +1 -1
  581. package/dist/scripts/update.js +1 -1
  582. package/dist/scripts/validate_bots.d.ts +1 -1
  583. package/dist/scripts/validate_bots.js +1 -1
  584. package/dist/scripts/verify-browser-bundle.d.ts +1 -1
  585. package/dist/scripts/verify-browser-bundle.js +1 -1
  586. package/dist/unlock.js +4 -4
  587. package/dist/unlock.js.map +1 -1
  588. package/docs/BITSHARES_ONBOARDING.md +15 -10
  589. package/docs/COPY_ON_WRITE_MASTER_PLAN.md +9 -9
  590. package/docs/DEXBOT_COMPARISON.md +19 -19
  591. package/docs/EVOLUTION.md +18 -44
  592. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
  593. package/docs/GRID_RECALCULATION.md +1 -1
  594. package/docs/README.md +3 -3
  595. package/docs/architecture.md +5 -5
  596. package/docs/developer_guide.md +6 -6
  597. package/market_adapter/README.md +13 -8
  598. package/package.json +22 -14
  599. package/scripts/README.md +33 -7
  600. package/scripts/clean-dist.js +10 -2
  601. package/tests/README.md +4 -3
@@ -1,13 +1,15 @@
1
+ 'use strict';
1
2
 
2
3
  import fs from 'node:fs';
3
4
  import { MARKET_ADAPTER } from '../../modules/constants.js';
4
- import { escapeHtml, serializeJsonForScript } from '../chart_utils.js';
5
+ import { escapeHtml, serializeJsonForScript, UPLOT_SHARED_SCRIPT } from '../chart_utils.js';
6
+ import { cursorCSS, uplotBgCSS } from '../chart_css.js';
7
+ import { zoomResetScript } from '../chart_ui.js';
5
8
  import { normalizeCandle } from '../math_utils.js';
6
9
  import { PATHS } from '../../modules/paths.js';
7
10
  import { getStorage } from '../../modules/storage/index.js';
8
11
  const { readJSON } = getStorage();
9
12
  import { getErrorMessage } from '../../modules/utils/errors.js';
10
- 'use strict';
11
13
 
12
14
 
13
15
  function inferBaseIntervalSeconds(candles: any[], fallback: any = 3600) {
@@ -393,10 +395,8 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
393
395
  .legend-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: #eef4fb; font-weight: 700; }
394
396
  .status { display: inline-flex; align-items: center; gap: 8px; font-size: 10px; color: #8290a2; text-transform: uppercase; letter-spacing: 0.5px; }
395
397
  .pill { border: 1px solid rgba(255,255,255,0.06); border-radius: 999px; padding: 4px 8px; background: rgba(255,255,255,0.03); }
396
- .uplot { background: #0b0f14; }
397
- .u-cursor-x { border-left: 1px dashed rgba(255,255,255,0.3) !important; }
398
- .u-cursor-y { border-top: 1px dashed rgba(255,255,255,0.3) !important; display: none; }
399
- .is-hovered .u-cursor-y { display: block; }
398
+ ${uplotBgCSS('#0b0f14')}
399
+ ${cursorCSS()}
400
400
  @media (max-width: 980px) {
401
401
  #topbar { flex-direction: column; align-items: flex-start; }
402
402
  #toolbar { justify-content: flex-start; }
@@ -497,7 +497,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
497
497
  <span class="legend-item"><span class="legend-dot" style="background:#93c5fd"></span><span class="legend-label">VWMA</span> <span class="legend-value" id="legend-vwap">-</span></span>
498
498
  </div>
499
499
  </div>
500
- <div id="price-chart"></div>
500
+ <div id="price-chart" title="Wheel: zoom time (ulos = tyhjaa tilaa datan ymparilla) · Drag: siirra aika- ja hintanakymaa · Wheel/drag hinta-akselilla: zoomaa hintaa · Double-click hinta-akselia: autofit"></div>
501
501
  <div id="volume-chart"></div>
502
502
  </div>
503
503
  </div>
@@ -556,8 +556,12 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
556
556
  let lastRenderedPriceScale = null;
557
557
  let charts = [];
558
558
  let chartEventsBound = false;
559
+ let manualYRange = null;
560
+ let lastCandleKey = null;
559
561
  let pendingRange = null;
560
562
  let pendingRangeRaf = 0;
563
+ let xMin = 0;
564
+ let xMax = 0;
561
565
  let smaWorker = null;
562
566
  let smaWorkerJob = null;
563
567
  let smaWorkerStartRaf = 0;
@@ -569,22 +573,122 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
569
573
  const vwapCache = new Map();
570
574
  const seriesCache = new Map();
571
575
 
576
+ ${UPLOT_SHARED_SCRIPT}
577
+
578
+ // TradingView-exporter interaction overrides (PR #3 / #11 feature):
579
+ // redeclare after UPLOT_SHARED_SCRIPT so these hoisted declarations win
580
+ // for this page only, without mutating the shared stack used by other charts.
581
+ function clampXRange(min, max) {
582
+ if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) return { min: xMin, max: xMax };
583
+ // Salli tyhjaa tilaa datan molemmin puolin (TradingView-tyyli):
584
+ // enintaan 75 % datan pituudesta reunapuskurina kummallekin puolelle.
585
+ const dataSpan = Math.max(1, xMax - xMin);
586
+ const maxPad = dataSpan * 0.75;
587
+ let lo = Math.max(xMin - maxPad, min);
588
+ let hi = Math.min(xMax + maxPad, max);
589
+ if (lo < xMin && hi <= lo) lo = xMin;
590
+ if (hi > xMax + maxPad) hi = xMax + maxPad;
591
+ if (hi <= lo) return { min: xMin, max: xMax };
592
+ return { min: lo, max: hi };
593
+ }
594
+ function bindWheelZoom(chart) {
595
+ chart.root.addEventListener('wheel', (e) => {
596
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
597
+ e.preventDefault();
598
+ const rect = chart.root.getBoundingClientRect();
599
+ if (chart === priceChart && inYAxisZone(chart, e.clientX)) {
600
+ const centerY = chart.posToVal(e.clientY - rect.top, 'y');
601
+ zoomYAt(chart, centerY, e.deltaY < 0 ? 0.91 : 1.10);
602
+ return;
603
+ }
604
+ e.stopPropagation();
605
+ const left = e.clientX - rect.left - (chart.bbox.left / (chart.pxRatio || 1));
606
+ const center = chart.posToVal(left, 'x');
607
+ const s = chart.scales.x || {};
608
+ const currMin = Number.isFinite(s.min) ? s.min : xMin;
609
+ const currMax = Number.isFinite(s.max) ? s.max : xMax;
610
+ const span = currMax - currMin;
611
+ if (!Number.isFinite(span) || span <= 0) return;
612
+ const factor = e.deltaY < 0 ? 0.85 : 1.15;
613
+ // Uloszoomaus datan reunojen yli: kokonaisnakyyma enintaan 2.5x datan pituus
614
+ const fullSpan = Math.max(1, xMax - xMin);
615
+ const nextSpan = Math.max(1, Math.min(fullSpan * 2.5, span * factor));
616
+ const ratio = (center - currMin) / span;
617
+ syncXRange(center - nextSpan * ratio, center - nextSpan * ratio + nextSpan);
618
+ }, { passive: false });
619
+ }
572
620
  function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
621
+ // Significant-digit price formatting (5 "active" digits), matching the
622
+ // DEXBot order-price style (formatCurrency) but with one extra digit.
623
+ // Very small values get SI micro-suffixes (m/µ/n/p/...); large values
624
+ // get comma grouping. Examples: 34.935 -> "34.935", 119.975 -> "119.98",
625
+ // 3014.9 -> "3,014.9", 0.00567 -> "5.67m".
573
626
  function fmtPrice(v) {
574
627
  if (v == null || !Number.isFinite(v)) return '-';
575
- const abs = Math.abs(v);
576
- if (abs >= 1000) return v.toFixed(2);
577
- if (abs >= 100) return v.toFixed(3);
578
- if (abs >= 1) return v.toFixed(4);
579
- return v.toPrecision(6);
628
+ const num = Number(v);
629
+ if (num === 0) return '0';
630
+ const abs = Math.abs(num);
631
+ if (abs < 0.1) {
632
+ if (abs >= 0.001) return fmtPrice(num * 1000) + 'm';
633
+ if (abs >= 0.000001) return fmtPrice(num * 1e6) + 'µ';
634
+ if (abs >= 1e-9) return fmtPrice(num * 1e9) + 'n';
635
+ if (abs >= 1e-12) return fmtPrice(num * 1e12) + 'p';
636
+ if (abs >= 1e-15) return fmtPrice(num * 1e15) + 'f';
637
+ if (abs >= 1e-18) return fmtPrice(num * 1e18) + 'a';
638
+ return num.toFixed(6);
639
+ }
640
+ const digits = 4;
641
+ let intDigits = Math.floor(Math.log10(abs)) + 1;
642
+ if (abs < 1) intDigits = 1;
643
+ const formatted = intDigits >= digits ? String(Math.round(num)) : num.toFixed(digits - intDigits);
644
+ return intDigits >= 4 ? formatted.replace(/\B(?=(\d{3})+(?!\d))/g, ',') : formatted;
645
+ }
646
+ function fmtPriceAxis(vals) {
647
+ if (!Array.isArray(vals) || vals.length === 0) return [];
648
+ return vals.map((v) => (v == null || !Number.isFinite(v)) ? '' : fmtPrice(v));
649
+ }
650
+ function fmtPriceLabel(v) {
651
+ if (v == null || !Number.isFinite(v)) return '-';
652
+ return fmtPrice(v);
653
+ }
654
+ function logAxisSplits(self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) {
655
+ if (!Number.isFinite(scaleMin) || !Number.isFinite(scaleMax) || scaleMin <= 0 || scaleMax <= 0) return [];
656
+ let h = 600;
657
+ if (self && self.bbox && Number.isFinite(self.bbox.height)) h = self.bbox.height / (self.pxRatio || 1);
658
+ else if (self && self.over && self.over.clientHeight) h = self.over.clientHeight;
659
+ const target = Math.max(8, Math.min(16, Math.floor(h / 30)));
660
+ const lmin = Math.log10(scaleMin);
661
+ const lmax = Math.log10(scaleMax);
662
+ const out = [];
663
+ for (let i = 0; i <= target; i++) {
664
+ out.push(Math.pow(10, lmin + (lmax - lmin) * (i / target)));
665
+ }
666
+ return out;
580
667
  }
668
+ // Significant-digit volume formatting (4 "active" digits) with K/M/B/T
669
+ // suffixes, matching the price-axis style. Examples: 1234567 -> "1235K",
670
+ // 1234567890 -> "1.235B".
581
671
  function fmtVolume(v) {
582
672
  if (v == null || !Number.isFinite(v)) return '-';
583
- const abs = Math.abs(v);
584
- if (abs >= 1e9) return (v / 1e9).toFixed(2) + 'B';
585
- if (abs >= 1e6) return (v / 1e6).toFixed(2) + 'M';
586
- if (abs >= 1e3) return (v / 1e3).toFixed(2) + 'K';
587
- return v.toFixed(2);
673
+ const num = Number(v);
674
+ if (num === 0) return '0';
675
+ const abs = Math.abs(num);
676
+ const units = [[1e12, 'T'], [1e9, 'B'], [1e6, 'M'], [1e3, 'K']];
677
+ for (const [factor, suffix] of units) {
678
+ if (abs >= factor) {
679
+ const scaled = num / factor;
680
+ const a = Math.abs(scaled);
681
+ let intDigits = Math.floor(Math.log10(a)) + 1;
682
+ if (a < 1) intDigits = 1;
683
+ const digits = 4;
684
+ const formatted = intDigits >= digits ? String(Math.round(scaled)) : scaled.toFixed(digits - intDigits);
685
+ return formatted + suffix;
686
+ }
687
+ }
688
+ let intDigits = Math.floor(Math.log10(abs)) + 1;
689
+ if (abs < 1) intDigits = 1;
690
+ const digits = 4;
691
+ return intDigits >= digits ? String(Math.round(num)) : num.toFixed(digits - intDigits);
588
692
  }
589
693
  function candleDirection(open, close) {
590
694
  if (close > open) return 'up';
@@ -1007,8 +1111,9 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1007
1111
  if (Number.isFinite(v)) { if (v < min) min = v; if (v > max) max = v; }
1008
1112
  }
1009
1113
  if (!Number.isFinite(min) || !Number.isFinite(max) || min <= 0 || max <= 0) return null;
1010
- if (min === max) return [min * 0.98, max * 1.02];
1011
- return [min * 0.97, max * 1.03];
1114
+ if (min === max) return [min * 0.96, max * 1.04];
1115
+ // ~7 % hengitystilaa yla- ja alapuolelle (TradingView-tyyli)
1116
+ return [min * 0.93, max * 1.07];
1012
1117
  }
1013
1118
  function visibleVolumeRange(u) {
1014
1119
  if (!currentCandles.length) return null;
@@ -1264,6 +1369,10 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1264
1369
  currentSeriesCloseValues = currentSeriesState.closeValues;
1265
1370
  const aggregated = aggregateCandles(currentSeriesCandles, tf?.seconds || 3600);
1266
1371
  currentCandles = aggregated.candles;
1372
+ if (currentCandles.length) {
1373
+ xMin = currentCandles[0].time;
1374
+ xMax = currentCandles[currentCandles.length - 1].time;
1375
+ }
1267
1376
  currentDisplayCandles = deriveDisplayCandles(currentCandles);
1268
1377
  currentOpen = currentDisplayCandles.map((c) => c.open);
1269
1378
  currentHigh = currentCandles.map((c) => c.high);
@@ -1342,6 +1451,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1342
1451
  distr: isLogScale ? 3 : 1,
1343
1452
  log: isLogScale ? 10 : undefined,
1344
1453
  range: (u, min, max) => {
1454
+ if (manualYRange) return [manualYRange.min, manualYRange.max];
1345
1455
  const vis = visiblePriceRange(u);
1346
1456
  if (vis) return vis;
1347
1457
  if (isLogScale) {
@@ -1368,8 +1478,24 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1368
1478
  ],
1369
1479
  axes: [
1370
1480
  makeTimeAxis(false),
1371
- { scale: 'y', size: 62, stroke: '#ffffff', grid: { stroke: '#1c2128' }, ticks: { stroke: '#30363d', width: 1 }, values: (u, vals) => vals.map((v) => (v == null ? '' : fmtPrice(v))) },
1481
+ {
1482
+ scale: 'y',
1483
+ side: 1,
1484
+ size: 84,
1485
+ space: isLogScale ? 1 : 45,
1486
+ stroke: '#ffffff',
1487
+ grid: { stroke: '#272f3a' },
1488
+ ticks: { stroke: '#414b57', width: 1 },
1489
+ font: '600 13px Segoe UI, sans-serif',
1490
+ splits: isLogScale ? logAxisSplits : undefined,
1491
+ values: (u, vals) => isLogScale
1492
+ ? vals.map((v) => (Number.isFinite(v) ? fmtPrice(v) : ''))
1493
+ : fmtPriceAxis(vals),
1494
+ },
1372
1495
  ],
1496
+ hooks: {
1497
+ draw: [(u) => positionPriceMarker(u)],
1498
+ },
1373
1499
  };
1374
1500
 
1375
1501
  const plugin = candlePlugin();
@@ -1403,7 +1529,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1403
1529
  ],
1404
1530
  axes: [
1405
1531
  makeTimeAxis(true),
1406
- { scale: 'y', size: 62, stroke: '#ffffff', grid: { stroke: '#1c2128' }, ticks: { stroke: '#30363d', width: 1 }, values: (u, vals) => vals.map((v) => (v == null ? '' : fmtVolume(v))) },
1532
+ { scale: 'y', side: 1, size: 84, space: 22, stroke: '#ffffff', grid: { stroke: '#1c2128' }, ticks: { stroke: '#30363d', width: 1 }, font: '600 12px Segoe UI, sans-serif', values: (u, vals) => vals.map((v) => (v == null ? '' : fmtVolume(v))) },
1407
1533
  ],
1408
1534
  };
1409
1535
 
@@ -1417,83 +1543,212 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1417
1543
  lastRenderedPriceScale = currentPriceScale;
1418
1544
  return priceChart;
1419
1545
  }
1420
- function clampRange(min, max) {
1421
- if (!currentCandles.length || !Number.isFinite(min) || !Number.isFinite(max) || max <= min) return null;
1422
- const first = currentCandles[0].time;
1423
- const last = currentCandles[currentCandles.length - 1].time;
1424
- let lo = min;
1425
- let hi = max;
1426
- if (lo < first) { hi += first - lo; lo = first; }
1427
- if (hi > last) { lo -= hi - last; hi = last; }
1428
- if (lo < first) lo = first;
1429
- if (hi > last) hi = last;
1430
- if (hi <= lo) return null;
1431
- return { min: lo, max: hi };
1546
+ // True when clientX sits over the price-axis region. The chart canvas
1547
+ // (plot + axis gutters) can be wider than the viewport and the right
1548
+ // gutter clipped off-screen, so we key the price zone off the ON-SCREEN
1549
+ // chart container width (always reachable) rather than the far-right gutter.
1550
+ // The rightmost ~120px of the container zooms price; the rest pans time.
1551
+ function inYAxisZone(chart, clientX) {
1552
+ const rootRect = chart.root.getBoundingClientRect();
1553
+ if (!rootRect || rootRect.width <= 0) return false;
1554
+ const x = clientX - rootRect.left;
1555
+ if (chart === priceChart) {
1556
+ return x > rootRect.width - 120;
1557
+ }
1558
+ return false;
1432
1559
  }
1433
- function syncXRange(min, max) {
1434
- pendingRange = clampRange(min, max);
1435
- if (pendingRangeRaf) return;
1436
- pendingRangeRaf = requestAnimationFrame(() => {
1437
- const next = pendingRange;
1438
- pendingRange = null;
1439
- pendingRangeRaf = 0;
1440
- if (!next || charts.length === 0) return;
1441
- charts.forEach((chart) => chart.batch(() => chart.setScale('x', next)));
1442
- });
1560
+ let priceMarkerLabel = null;
1561
+ let priceMarkerLine = null;
1562
+ function ensurePriceMarker(u) {
1563
+ if (priceMarkerLabel && priceMarkerLabel.parentNode === u.root) return;
1564
+ if (priceMarkerLabel && priceMarkerLabel.parentNode) priceMarkerLabel.parentNode.removeChild(priceMarkerLabel);
1565
+ if (priceMarkerLine && priceMarkerLine.parentNode) priceMarkerLine.parentNode.removeChild(priceMarkerLine);
1566
+ try { if (getComputedStyle(u.root).position === 'static') u.root.style.position = 'relative'; } catch (e) {}
1567
+ priceMarkerLabel = document.createElement('div');
1568
+ priceMarkerLabel.style.cssText = 'position:absolute;z-index:30;pointer-events:none;font:600 12px Segoe UI, sans-serif;line-height:19px;height:19px;padding:0 7px;border-radius:3px;color:#ffffff;white-space:nowrap;box-sizing:border-box;text-align:center;overflow:hidden;';
1569
+ priceMarkerLine = document.createElement('div');
1570
+ priceMarkerLine.style.cssText = 'position:absolute;z-index:1;pointer-events:none;height:0;border-top:1px dashed currentColor;opacity:0.6;';
1571
+ u.root.appendChild(priceMarkerLine);
1572
+ u.root.appendChild(priceMarkerLabel);
1573
+ }
1574
+ function positionPriceMarker(u) {
1575
+ if (!u || !u.over || !currentCandles.length) return;
1576
+ ensurePriceMarker(u);
1577
+ const last = currentCandles[currentCandles.length - 1];
1578
+ const prev = currentCandles.length > 1 ? currentCandles[currentCandles.length - 2] : last;
1579
+ const price = last.close;
1580
+ if (!Number.isFinite(price)) {
1581
+ priceMarkerLabel.style.display = 'none';
1582
+ priceMarkerLine.style.display = 'none';
1583
+ return;
1584
+ }
1585
+ const up = price >= (Number.isFinite(prev.close) ? prev.close : price);
1586
+ const color = up ? '#26a69a' : '#ef5350';
1587
+ const s = u.scales.y || {};
1588
+ const sMin = Number.isFinite(s.min) ? s.min : null;
1589
+ const sMax = Number.isFinite(s.max) ? s.max : null;
1590
+ if (sMin == null || sMax == null || sMax <= sMin) {
1591
+ priceMarkerLabel.style.display = 'none';
1592
+ priceMarkerLine.style.display = 'none';
1593
+ return;
1594
+ }
1595
+ let frac;
1596
+ if (currentPriceScale === 'log') {
1597
+ const lmin = Math.log10(sMin);
1598
+ const lmax = Math.log10(sMax);
1599
+ const lp = Math.log10(Math.max(price, 1e-12));
1600
+ frac = (lp - lmin) / (lmax - lmin);
1601
+ } else {
1602
+ frac = (price - sMin) / (sMax - sMin);
1603
+ }
1604
+ const rootRect = u.root.getBoundingClientRect();
1605
+ const overRect = u.over.getBoundingClientRect();
1606
+ if (!overRect || overRect.height <= 0) return;
1607
+ const plotTop = overRect.top - rootRect.top;
1608
+ const yRel = (1 - frac) * overRect.height;
1609
+ const inRange = frac >= 0 && frac <= 1;
1610
+ priceMarkerLabel.style.display = 'block';
1611
+ priceMarkerLabel.textContent = fmtPriceLabel(price);
1612
+ priceMarkerLabel.style.background = color;
1613
+ priceMarkerLabel.style.top = (plotTop + Math.max(0, Math.min(overRect.height, yRel)) - 10) + 'px';
1614
+ const leftAxisW = overRect.left - rootRect.left;
1615
+ const rightAxisW = rootRect.right - overRect.right;
1616
+ if (leftAxisW >= rightAxisW) {
1617
+ priceMarkerLabel.style.left = '0px';
1618
+ priceMarkerLabel.style.right = '';
1619
+ priceMarkerLabel.style.width = Math.max(40, leftAxisW - 4) + 'px';
1620
+ } else {
1621
+ priceMarkerLabel.style.left = '';
1622
+ priceMarkerLabel.style.right = '0px';
1623
+ priceMarkerLabel.style.width = Math.max(40, rightAxisW - 4) + 'px';
1624
+ }
1625
+ if (inRange) {
1626
+ priceMarkerLine.style.display = 'block';
1627
+ priceMarkerLine.style.color = color;
1628
+ priceMarkerLine.style.top = (plotTop + yRel) + 'px';
1629
+ priceMarkerLine.style.left = (overRect.left - rootRect.left) + 'px';
1630
+ priceMarkerLine.style.width = overRect.width + 'px';
1631
+ } else {
1632
+ priceMarkerLine.style.display = 'none';
1633
+ }
1443
1634
  }
1444
- function bindWheelZoom(chart) {
1445
- chart.root.addEventListener('wheel', (e) => {
1446
- if (e.ctrlKey || e.metaKey || e.altKey) return;
1447
- e.preventDefault();
1448
- const rect = chart.root.getBoundingClientRect();
1449
- const center = chart.posToVal(e.clientX - rect.left, 'x');
1450
- const s = chart.scales.x || {};
1451
- const currMin = Number.isFinite(s.min) ? s.min : currentCandles[0].time;
1452
- const currMax = Number.isFinite(s.max) ? s.max : currentCandles[currentCandles.length - 1].time;
1453
- const span = currMax - currMin;
1454
- const first = currentCandles[0]?.time;
1455
- const last = currentCandles[currentCandles.length - 1]?.time;
1456
- const fullSpan = Number.isFinite(first) && Number.isFinite(last) ? Math.max(1, last - first) : span;
1457
- if (!Number.isFinite(span) || span <= 0) return;
1458
- const factor = e.deltaY < 0 ? 0.85 : 1.15;
1459
- const nextSpan = Math.max(1, Math.min(fullSpan, span * factor));
1460
- const ratio = (center - currMin) / span;
1461
- syncXRange(center - nextSpan * ratio, center - nextSpan * ratio + nextSpan);
1462
- }, { passive: false });
1635
+ function currentYRange(chart) {
1636
+ const s = chart.scales.y || {};
1637
+ let min = Number.isFinite(s.min) ? s.min : null;
1638
+ let max = Number.isFinite(s.max) ? s.max : null;
1639
+ if (min == null || max == null) {
1640
+ const vis = visiblePriceRange(chart);
1641
+ if (vis) { min = vis[0]; max = vis[1]; }
1642
+ }
1643
+ if (min == null || max == null || !Number.isFinite(min) || !Number.isFinite(max) || max <= min) return null;
1644
+ return { min: min, max: max };
1463
1645
  }
1464
- function bindPan(chart) {
1646
+ let priceBounds = null;
1647
+ function computePriceBounds() {
1648
+ let min = Infinity;
1649
+ let max = -Infinity;
1650
+ for (let i = 0; i < currentCandles.length; i++) {
1651
+ const lo = currentLow[i];
1652
+ const hi = currentHigh[i];
1653
+ if (Number.isFinite(lo) && lo > 0 && lo < min) min = lo;
1654
+ if (Number.isFinite(hi) && hi > max) max = hi;
1655
+ }
1656
+ if (!Number.isFinite(min) || !Number.isFinite(max) || min <= 0 || max <= min) {
1657
+ priceBounds = null;
1658
+ return;
1659
+ }
1660
+ const range = max - min;
1661
+ // Uloszoomauksen rajat: alin naytettava hinta voi painua viimeistaan
1662
+ // ~35 % datan minimin alapuolelle, ylin ~1.6x datan maksimin, ja
1663
+ // kokonaisvali saa olla enintaan ~1.6x datan hintahaarukasta —
1664
+ // ei siis paase "lipsahtamaan" candleita nakyvasta kadottaen.
1665
+ priceBounds = { min: min, max: max, floor: min * 0.35, ceil: max * 1.6, maxSpan: range * 1.6 };
1666
+ }
1667
+ function applyYRange(chart, min, max) {
1668
+ if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) return false;
1669
+ const ref = Math.max(Math.abs(min), Math.abs(max), 1e-12);
1670
+ if (max - min < ref * 1e-9) return false;
1671
+ if (currentPriceScale === 'log' && min <= 0) {
1672
+ min = max > 0 ? max * 1e-6 : 1e-6;
1673
+ if (max <= min) return false;
1674
+ }
1675
+ if (priceBounds) {
1676
+ let nMin = Math.max(min, priceBounds.floor);
1677
+ let nMax = Math.min(max, priceBounds.ceil);
1678
+ if (nMax - nMin > priceBounds.maxSpan) {
1679
+ const c = (nMin + nMax) / 2;
1680
+ nMin = c - priceBounds.maxSpan / 2;
1681
+ nMax = c + priceBounds.maxSpan / 2;
1682
+ nMin = Math.max(nMin, priceBounds.floor);
1683
+ nMax = Math.min(nMax, priceBounds.ceil);
1684
+ }
1685
+ if (nMax <= nMin) return false;
1686
+ min = nMin;
1687
+ max = nMax;
1688
+ }
1689
+ manualYRange = { min: min, max: max };
1690
+ chart.setScale('y', { min: min, max: max });
1691
+ return true;
1692
+ }
1693
+ function zoomYAt(chart, centerY, factor) {
1694
+ const r = currentYRange(chart);
1695
+ if (!r) return;
1696
+ const span = r.max - r.min;
1697
+ let ratio = (centerY - r.min) / span;
1698
+ if (!Number.isFinite(ratio)) ratio = 0.5;
1699
+ ratio = Math.max(0.05, Math.min(0.95, ratio));
1700
+ const nextSpan = span * factor;
1701
+ const nextMin = centerY - nextSpan * ratio;
1702
+ applyYRange(chart, nextMin, nextMin + nextSpan);
1703
+ }
1704
+ function bindYAxisDrag(chart) {
1465
1705
  let dragging = false;
1466
- let startClientX = 0;
1467
- let startMin = 0;
1468
- let startMax = 0;
1706
+ let startClientY = 0;
1707
+ let startRange = null;
1469
1708
  const onMove = (e) => {
1470
- if (!dragging) return;
1709
+ if (!dragging || !startRange) return;
1471
1710
  e.preventDefault();
1472
- const rect = chart.root.getBoundingClientRect();
1473
- const delta = chart.posToVal(e.clientX - rect.left, 'x') - chart.posToVal(startClientX - rect.left, 'x');
1474
- syncXRange(startMin - delta, startMax - delta);
1711
+ chart.root.style.cursor = 'ns-resize';
1712
+ const deltaPx = e.clientY - startClientY;
1713
+ if (!Number.isFinite(deltaPx) || deltaPx === 0) return;
1714
+ const factor = Math.exp(deltaPx / 350);
1715
+ const span = startRange.max - startRange.min;
1716
+ const center = (startRange.min + startRange.max) / 2;
1717
+ const nextSpan = span * factor;
1718
+ applyYRange(chart, center - nextSpan / 2, center + nextSpan / 2);
1475
1719
  };
1476
1720
  const endDrag = () => {
1477
1721
  if (!dragging) return;
1478
1722
  dragging = false;
1479
- document.body.style.cursor = '';
1723
+ startRange = null;
1724
+ chart.root.style.cursor = '';
1480
1725
  window.removeEventListener('mousemove', onMove);
1481
1726
  window.removeEventListener('mouseup', endDrag);
1482
1727
  };
1483
1728
  chart.root.addEventListener('mousedown', (e) => {
1484
1729
  if (!e || e.button !== 0 || e.ctrlKey || e.metaKey || e.altKey) return;
1730
+ // capture phase so we pre-empt uPlot's own plot drag when in the y-zone
1485
1731
  const rect = chart.root.getBoundingClientRect();
1486
1732
  if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) return;
1733
+ if (chart !== priceChart || !inYAxisZone(chart, e.clientX)) return;
1734
+ const range = currentYRange(chart);
1735
+ if (!range) return;
1487
1736
  e.preventDefault();
1488
1737
  e.stopPropagation();
1489
1738
  dragging = true;
1490
- startClientX = e.clientX;
1491
- const s = chart.scales.x || {};
1492
- startMin = Number.isFinite(s.min) ? s.min : currentCandles[0].time;
1493
- startMax = Number.isFinite(s.max) ? s.max : currentCandles[currentCandles.length - 1].time;
1494
- document.body.style.cursor = 'grabbing';
1739
+ startClientY = e.clientY;
1740
+ startRange = range;
1741
+ chart.root.style.cursor = 'ns-resize';
1495
1742
  window.addEventListener('mousemove', onMove);
1496
1743
  window.addEventListener('mouseup', endDrag, { once: true });
1744
+ }, true);
1745
+ }
1746
+ function bindYAxisReset(chart) {
1747
+ chart.root.addEventListener('dblclick', (e) => {
1748
+ if (chart !== priceChart) return;
1749
+ manualYRange = null;
1750
+ const vis = visiblePriceRange(chart);
1751
+ if (vis) chart.setScale('y', { min: vis[0], max: vis[1] });
1497
1752
  });
1498
1753
  }
1499
1754
  function refreshLegend() {
@@ -1521,6 +1776,9 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1521
1776
  const oldRange = keepRange && priceChart ? priceChart.scales.x : null;
1522
1777
  const needsRebuild = !priceChart || !volumeChart || lastRenderedPriceScale !== currentPriceScale;
1523
1778
  if (needsRebuild) {
1779
+ manualYRange = null;
1780
+ priceMarkerLabel = null;
1781
+ priceMarkerLine = null;
1524
1782
  if (priceEl) priceEl.innerHTML = '';
1525
1783
  if (volumeEl) volumeEl.innerHTML = '';
1526
1784
  priceChart = null;
@@ -1529,6 +1787,16 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1529
1787
  chartEventsBound = false;
1530
1788
  }
1531
1789
  const data = buildData();
1790
+ // Dataset changed (timeframe switch, fresh candles): drop any manual
1791
+ // price scale so auto-fit takes over again for the new data shape.
1792
+ const candleKey = currentCandles.length
1793
+ ? currentCandles[0].time + ':' + currentCandles[currentCandles.length - 1].time + ':' + currentCandles.length
1794
+ : '';
1795
+ if (candleKey !== lastCandleKey) {
1796
+ lastCandleKey = candleKey;
1797
+ manualYRange = null;
1798
+ }
1799
+ computePriceBounds();
1532
1800
  if (needsRebuild) {
1533
1801
  makeChart();
1534
1802
  } else {
@@ -1541,17 +1809,24 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1541
1809
  charts.forEach((chart) => {
1542
1810
  bindWheelZoom(chart);
1543
1811
  bindPan(chart);
1544
- chart.over.addEventListener('mousemove', () => {
1812
+ if (chart === priceChart) {
1813
+ bindYAxisDrag(chart);
1814
+ bindYAxisReset(chart);
1815
+ chart.root.addEventListener('mousemove', (e) => {
1816
+ chart.root.style.cursor = inYAxisZone(chart, e.clientX) ? 'ns-resize' : '';
1817
+ });
1818
+ }
1819
+ chart.root.addEventListener('mousemove', () => {
1545
1820
  if (chart.cursor.idx != null) updateLegend(chart.cursor.idx);
1546
1821
  });
1547
- chart.over.addEventListener('mouseleave', refreshLegend);
1822
+ chart.root.addEventListener('mouseleave', refreshLegend);
1548
1823
  chart.root.addEventListener('mouseenter', () => chart.root.classList.add('is-hovered'));
1549
1824
  chart.root.addEventListener('mouseleave', () => chart.root.classList.remove('is-hovered'));
1550
1825
  });
1551
1826
  chartEventsBound = true;
1552
1827
  }
1553
1828
  if (oldRange && Number.isFinite(oldRange.min) && Number.isFinite(oldRange.max)) {
1554
- const next = clampRange(oldRange.min, oldRange.max);
1829
+ const next = clampXRange(oldRange.min, oldRange.max);
1555
1830
  if (next) charts.forEach((chart) => chart.batch(() => chart.setScale('x', next)));
1556
1831
  }
1557
1832
  refreshLegend();
@@ -1584,6 +1859,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1584
1859
  if (pairToggle) {
1585
1860
  pairToggle.addEventListener('click', () => {
1586
1861
  currentPairMode = currentPairMode === 'inverse' ? 'normal' : 'inverse';
1862
+ manualYRange = null;
1587
1863
  setControls();
1588
1864
  rerender(true);
1589
1865
  });
@@ -1701,13 +1977,7 @@ function generateHTML(data: any, title: any = 'TradingView Style Research') {
1701
1977
  });
1702
1978
  });
1703
1979
 
1704
- window.addEventListener('keydown', (e) => {
1705
- if ((e.ctrlKey || e.metaKey) && e.key === '0' && charts.length) {
1706
- const first = currentCandles[0]?.time;
1707
- const last = currentCandles[currentCandles.length - 1]?.time;
1708
- if (Number.isFinite(first) && Number.isFinite(last)) syncXRange(first, last);
1709
- }
1710
- });
1980
+ ${zoomResetScript()}
1711
1981
  })();
1712
1982
  </script>
1713
1983
  </body>
@@ -159,7 +159,7 @@ All panels share aligned vertical time grid lines, and the bottom output panel s
159
159
  |------|-------|---------|---------|
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
- | **amaS%** | 0.04–0.12 | 0.085 | Logarithmic. Gear ratio for average per-bar AMA slope saturation |
162
+ | **amaS%** | 0.06–0.12 | 0.09 | Logarithmic. Gear ratio for average per-bar AMA slope saturation |
163
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