dexbot 1.1.10

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 (807) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -0
  3. package/claw/README.md +513 -0
  4. package/claw/scripts/memu_runner.py +385 -0
  5. package/claw/skills/bitshares-guide/SKILL.md +14 -0
  6. package/claw/skills/bitshares-guide/references/example-skill-shells.md +18 -0
  7. package/claw/skills/bitshares-guide/references/presentation-patterns.md +19 -0
  8. package/claw/skills/bitshares-guide/references/scope-guardrails.md +9 -0
  9. package/claw/skills/launcher-ops/SKILL.md +18 -0
  10. package/claw/skills/launcher-ops/references/launcher-workflow.md +23 -0
  11. package/claw/skills/margin-trading/SKILL.md +23 -0
  12. package/claw/skills/margin-trading/references/honest-asset-list.md +74 -0
  13. package/claw/skills/margin-trading/references/honest-assets.md +44 -0
  14. package/claw/skills/margin-trading/references/position-management.md +203 -0
  15. package/claw/skills/margin-trading/references/trading-concepts.md +80 -0
  16. package/claw/skills/memu-memory/SKILL.md +218 -0
  17. package/claw/skills/shared/references/js-automation-overview.md +24 -0
  18. package/claw/skills/shared/references/safety-and-staleness.md +11 -0
  19. package/claw/skills/shared/references/skill-boundaries.md +17 -0
  20. package/claw/skills/trend-detection/SKILL.md +33 -0
  21. package/claw/skills/trend-detection/agents/openai.yaml +3 -0
  22. package/claw/skills/trend-detection/references/service.md +45 -0
  23. package/dist/analysis/ama_fitting/analyze_ama_price_changes.d.ts +3 -0
  24. package/dist/analysis/ama_fitting/analyze_ama_price_changes.d.ts.map +1 -0
  25. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js +149 -0
  26. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js.map +1 -0
  27. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts +3 -0
  28. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts.map +1 -0
  29. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +387 -0
  30. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -0
  31. package/dist/analysis/ama_fitting/calibrate_convergence_er.d.ts +3 -0
  32. package/dist/analysis/ama_fitting/calibrate_convergence_er.d.ts.map +1 -0
  33. package/dist/analysis/ama_fitting/calibrate_convergence_er.js +200 -0
  34. package/dist/analysis/ama_fitting/calibrate_convergence_er.js.map +1 -0
  35. package/dist/analysis/ama_fitting/fetch_lp_candles.d.ts +3 -0
  36. package/dist/analysis/ama_fitting/fetch_lp_candles.d.ts.map +1 -0
  37. package/dist/analysis/ama_fitting/fetch_lp_candles.js +212 -0
  38. package/dist/analysis/ama_fitting/fetch_lp_candles.js.map +1 -0
  39. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts +31 -0
  40. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts.map +1 -0
  41. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js +249 -0
  42. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js.map +1 -0
  43. package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts +2 -0
  44. package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts.map +1 -0
  45. package/dist/analysis/ama_fitting/optimizer_high_resolution.js +782 -0
  46. package/dist/analysis/ama_fitting/optimizer_high_resolution.js.map +1 -0
  47. package/dist/analysis/analyze_derivatives.d.ts +95 -0
  48. package/dist/analysis/analyze_derivatives.d.ts.map +1 -0
  49. package/dist/analysis/analyze_derivatives.js +295 -0
  50. package/dist/analysis/analyze_derivatives.js.map +1 -0
  51. package/dist/analysis/analyze_dynamic_weight.d.ts +14 -0
  52. package/dist/analysis/analyze_dynamic_weight.d.ts.map +1 -0
  53. package/dist/analysis/analyze_dynamic_weight.js +208 -0
  54. package/dist/analysis/analyze_dynamic_weight.js.map +1 -0
  55. package/dist/analysis/analyze_kalman.d.ts +13 -0
  56. package/dist/analysis/analyze_kalman.d.ts.map +1 -0
  57. package/dist/analysis/analyze_kalman.js +123 -0
  58. package/dist/analysis/analyze_kalman.js.map +1 -0
  59. package/dist/analysis/analyze_regime.d.ts +20 -0
  60. package/dist/analysis/analyze_regime.d.ts.map +1 -0
  61. package/dist/analysis/analyze_regime.js +139 -0
  62. package/dist/analysis/analyze_regime.js.map +1 -0
  63. package/dist/analysis/analyze_regime_windows.d.ts +19 -0
  64. package/dist/analysis/analyze_regime_windows.d.ts.map +1 -0
  65. package/dist/analysis/analyze_regime_windows.js +413 -0
  66. package/dist/analysis/analyze_regime_windows.js.map +1 -0
  67. package/dist/analysis/analyze_risk_profile.d.ts +3 -0
  68. package/dist/analysis/analyze_risk_profile.d.ts.map +1 -0
  69. package/dist/analysis/analyze_risk_profile.js +159 -0
  70. package/dist/analysis/analyze_risk_profile.js.map +1 -0
  71. package/dist/analysis/analyze_trade_heatmap.d.ts +3 -0
  72. package/dist/analysis/analyze_trade_heatmap.d.ts.map +1 -0
  73. package/dist/analysis/analyze_trade_heatmap.js +367 -0
  74. package/dist/analysis/analyze_trade_heatmap.js.map +1 -0
  75. package/dist/analysis/analyze_volatility.d.ts +22 -0
  76. package/dist/analysis/analyze_volatility.d.ts.map +1 -0
  77. package/dist/analysis/analyze_volatility.js +172 -0
  78. package/dist/analysis/analyze_volatility.js.map +1 -0
  79. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts +99 -0
  80. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts.map +1 -0
  81. package/dist/analysis/bot_fitting/backtest_ama_sweep.js +804 -0
  82. package/dist/analysis/bot_fitting/backtest_ama_sweep.js.map +1 -0
  83. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts +2 -0
  84. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts.map +1 -0
  85. package/dist/analysis/bot_fitting/backtest_bot_fitting.js +347 -0
  86. package/dist/analysis/bot_fitting/backtest_bot_fitting.js.map +1 -0
  87. package/dist/analysis/bot_fitting/shared_utils.d.ts +15 -0
  88. package/dist/analysis/bot_fitting/shared_utils.d.ts.map +1 -0
  89. package/dist/analysis/bot_fitting/shared_utils.js +48 -0
  90. package/dist/analysis/bot_fitting/shared_utils.js.map +1 -0
  91. package/dist/analysis/bot_key_utils.d.ts +16 -0
  92. package/dist/analysis/bot_key_utils.d.ts.map +1 -0
  93. package/dist/analysis/bot_key_utils.js +61 -0
  94. package/dist/analysis/bot_key_utils.js.map +1 -0
  95. package/dist/analysis/bot_usage/discover_bot_accounts.d.ts +3 -0
  96. package/dist/analysis/bot_usage/discover_bot_accounts.d.ts.map +1 -0
  97. package/dist/analysis/bot_usage/discover_bot_accounts.js +498 -0
  98. package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -0
  99. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +546 -0
  100. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -0
  101. package/dist/analysis/bot_usage/kibana_bot_queries.js +427 -0
  102. package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -0
  103. package/dist/analysis/chart_utils.d.ts +13 -0
  104. package/dist/analysis/chart_utils.d.ts.map +1 -0
  105. package/dist/analysis/chart_utils.js +142 -0
  106. package/dist/analysis/chart_utils.js.map +1 -0
  107. package/dist/analysis/derivative_chart_generator.d.ts +18 -0
  108. package/dist/analysis/derivative_chart_generator.d.ts.map +1 -0
  109. package/dist/analysis/derivative_chart_generator.js +896 -0
  110. package/dist/analysis/derivative_chart_generator.js.map +1 -0
  111. package/dist/analysis/math_utils.d.ts +35 -0
  112. package/dist/analysis/math_utils.d.ts.map +1 -0
  113. package/dist/analysis/math_utils.js +121 -0
  114. package/dist/analysis/math_utils.js.map +1 -0
  115. package/dist/analysis/price_sources.d.ts +34 -0
  116. package/dist/analysis/price_sources.d.ts.map +1 -0
  117. package/dist/analysis/price_sources.js +103 -0
  118. package/dist/analysis/price_sources.js.map +1 -0
  119. package/dist/analysis/trade_profitability.d.ts +115 -0
  120. package/dist/analysis/trade_profitability.d.ts.map +1 -0
  121. package/dist/analysis/trade_profitability.js +1247 -0
  122. package/dist/analysis/trade_profitability.js.map +1 -0
  123. package/dist/analysis/tradingview/analyze_tradingview.d.ts +38 -0
  124. package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -0
  125. package/dist/analysis/tradingview/analyze_tradingview.js +212 -0
  126. package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -0
  127. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts +9 -0
  128. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -0
  129. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +1717 -0
  130. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -0
  131. package/dist/analysis/trend_detection/derivative_analyzer.d.ts +250 -0
  132. package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +1 -0
  133. package/dist/analysis/trend_detection/derivative_analyzer.js +903 -0
  134. package/dist/analysis/trend_detection/derivative_analyzer.js.map +1 -0
  135. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts +6 -0
  136. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -0
  137. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +1449 -0
  138. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -0
  139. package/dist/analysis/trend_detection/hurst_analyzer.d.ts +42 -0
  140. package/dist/analysis/trend_detection/hurst_analyzer.d.ts.map +1 -0
  141. package/dist/analysis/trend_detection/hurst_analyzer.js +159 -0
  142. package/dist/analysis/trend_detection/hurst_analyzer.js.map +1 -0
  143. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts +6 -0
  144. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -0
  145. package/dist/analysis/trend_detection/kalman_chart_generator.js +408 -0
  146. package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -0
  147. package/dist/analysis/trend_detection/kalman_trend_analyzer.d.ts +103 -0
  148. package/dist/analysis/trend_detection/kalman_trend_analyzer.d.ts.map +1 -0
  149. package/dist/analysis/trend_detection/kalman_trend_analyzer.js +229 -0
  150. package/dist/analysis/trend_detection/kalman_trend_analyzer.js.map +1 -0
  151. package/dist/analysis/trend_detection/kalman_velocity_smoothing.d.ts +34 -0
  152. package/dist/analysis/trend_detection/kalman_velocity_smoothing.d.ts.map +1 -0
  153. package/dist/analysis/trend_detection/kalman_velocity_smoothing.js +89 -0
  154. package/dist/analysis/trend_detection/kalman_velocity_smoothing.js.map +1 -0
  155. package/dist/analysis/trend_detection/permutation_entropy_analyzer.d.ts +39 -0
  156. package/dist/analysis/trend_detection/permutation_entropy_analyzer.d.ts.map +1 -0
  157. package/dist/analysis/trend_detection/permutation_entropy_analyzer.js +132 -0
  158. package/dist/analysis/trend_detection/permutation_entropy_analyzer.js.map +1 -0
  159. package/dist/analysis/trend_detection/regime_chart_generator.d.ts +6 -0
  160. package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -0
  161. package/dist/analysis/trend_detection/regime_chart_generator.js +345 -0
  162. package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -0
  163. package/dist/analysis/trend_detection/tests/test_kalman_trend.d.ts +2 -0
  164. package/dist/analysis/trend_detection/tests/test_kalman_trend.d.ts.map +1 -0
  165. package/dist/analysis/trend_detection/tests/test_kalman_trend.js +130 -0
  166. package/dist/analysis/trend_detection/tests/test_kalman_trend.js.map +1 -0
  167. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.d.ts +2 -0
  168. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.d.ts.map +1 -0
  169. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js +38 -0
  170. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js.map +1 -0
  171. package/dist/analysis/trend_detection/volatility_chart_generator.d.ts +6 -0
  172. package/dist/analysis/trend_detection/volatility_chart_generator.d.ts.map +1 -0
  173. package/dist/analysis/trend_detection/volatility_chart_generator.js +732 -0
  174. package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -0
  175. package/dist/bot.d.ts +49 -0
  176. package/dist/bot.d.ts.map +1 -0
  177. package/dist/bot.js +221 -0
  178. package/dist/bot.js.map +1 -0
  179. package/dist/credential-daemon.d.ts +64 -0
  180. package/dist/credential-daemon.d.ts.map +1 -0
  181. package/dist/credential-daemon.js +987 -0
  182. package/dist/credential-daemon.js.map +1 -0
  183. package/dist/dexbot.d.ts +87 -0
  184. package/dist/dexbot.d.ts.map +1 -0
  185. package/dist/dexbot.js +1222 -0
  186. package/dist/dexbot.js.map +1 -0
  187. package/dist/market_adapter/ama_signal_runner.d.ts +3 -0
  188. package/dist/market_adapter/ama_signal_runner.d.ts.map +1 -0
  189. package/dist/market_adapter/ama_signal_runner.js +175 -0
  190. package/dist/market_adapter/ama_signal_runner.js.map +1 -0
  191. package/dist/market_adapter/candle_utils.d.ts +57 -0
  192. package/dist/market_adapter/candle_utils.d.ts.map +1 -0
  193. package/dist/market_adapter/candle_utils.js +252 -0
  194. package/dist/market_adapter/candle_utils.js.map +1 -0
  195. package/dist/market_adapter/core/asymmetric_bounds.d.ts +20 -0
  196. package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -0
  197. package/dist/market_adapter/core/asymmetric_bounds.js +88 -0
  198. package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -0
  199. package/dist/market_adapter/core/config_normalizers.d.ts +10 -0
  200. package/dist/market_adapter/core/config_normalizers.d.ts.map +1 -0
  201. package/dist/market_adapter/core/config_normalizers.js +24 -0
  202. package/dist/market_adapter/core/config_normalizers.js.map +1 -0
  203. package/dist/market_adapter/core/kibana_candles.d.ts +74 -0
  204. package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -0
  205. package/dist/market_adapter/core/kibana_candles.js +249 -0
  206. package/dist/market_adapter/core/kibana_candles.js.map +1 -0
  207. package/dist/market_adapter/core/kibana_client.d.ts +27 -0
  208. package/dist/market_adapter/core/kibana_client.d.ts.map +1 -0
  209. package/dist/market_adapter/core/kibana_client.js +120 -0
  210. package/dist/market_adapter/core/kibana_client.js.map +1 -0
  211. package/dist/market_adapter/core/kibana_market_candles.d.ts +143 -0
  212. package/dist/market_adapter/core/kibana_market_candles.d.ts.map +1 -0
  213. package/dist/market_adapter/core/kibana_market_candles.js +170 -0
  214. package/dist/market_adapter/core/kibana_market_candles.js.map +1 -0
  215. package/dist/market_adapter/core/market_adapter_service.d.ts +210 -0
  216. package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -0
  217. package/dist/market_adapter/core/market_adapter_service.js +2328 -0
  218. package/dist/market_adapter/core/market_adapter_service.js.map +1 -0
  219. package/dist/market_adapter/core/strategies/ama.d.ts +2 -0
  220. package/dist/market_adapter/core/strategies/ama.d.ts.map +1 -0
  221. package/dist/market_adapter/core/strategies/ama.js +138 -0
  222. package/dist/market_adapter/core/strategies/ama.js.map +1 -0
  223. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts +50 -0
  224. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts.map +1 -0
  225. package/dist/market_adapter/core/strategies/ama_slope_model.js +143 -0
  226. package/dist/market_adapter/core/strategies/ama_slope_model.js.map +1 -0
  227. package/dist/market_adapter/core/strategies/atr/calculator.d.ts +12 -0
  228. package/dist/market_adapter/core/strategies/atr/calculator.d.ts.map +1 -0
  229. package/dist/market_adapter/core/strategies/atr/calculator.js +47 -0
  230. package/dist/market_adapter/core/strategies/atr/calculator.js.map +1 -0
  231. package/dist/market_adapter/core/strategies/collateral_manager.d.ts +32 -0
  232. package/dist/market_adapter/core/strategies/collateral_manager.d.ts.map +1 -0
  233. package/dist/market_adapter/core/strategies/collateral_manager.js +68 -0
  234. package/dist/market_adapter/core/strategies/collateral_manager.js.map +1 -0
  235. package/dist/market_adapter/core/strategies/regime_gate.d.ts +66 -0
  236. package/dist/market_adapter/core/strategies/regime_gate.d.ts.map +1 -0
  237. package/dist/market_adapter/core/strategies/regime_gate.js +189 -0
  238. package/dist/market_adapter/core/strategies/regime_gate.js.map +1 -0
  239. package/dist/market_adapter/index.d.ts +10 -0
  240. package/dist/market_adapter/index.d.ts.map +1 -0
  241. package/dist/market_adapter/index.js +71 -0
  242. package/dist/market_adapter/index.js.map +1 -0
  243. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.d.ts +3 -0
  244. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.d.ts.map +1 -0
  245. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +1165 -0
  246. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -0
  247. package/dist/market_adapter/inputs/fetch_lp_data.d.ts +54 -0
  248. package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -0
  249. package/dist/market_adapter/inputs/fetch_lp_data.js +743 -0
  250. package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -0
  251. package/dist/market_adapter/inputs/kibana_source.d.ts +68 -0
  252. package/dist/market_adapter/inputs/kibana_source.d.ts.map +1 -0
  253. package/dist/market_adapter/inputs/kibana_source.js +143 -0
  254. package/dist/market_adapter/inputs/kibana_source.js.map +1 -0
  255. package/dist/market_adapter/interval_utils.d.ts +6 -0
  256. package/dist/market_adapter/interval_utils.d.ts.map +1 -0
  257. package/dist/market_adapter/interval_utils.js +14 -0
  258. package/dist/market_adapter/interval_utils.js.map +1 -0
  259. package/dist/market_adapter/log_format.d.ts +16 -0
  260. package/dist/market_adapter/log_format.d.ts.map +1 -0
  261. package/dist/market_adapter/log_format.js +72 -0
  262. package/dist/market_adapter/log_format.js.map +1 -0
  263. package/dist/market_adapter/lp_chart_core.d.ts +6 -0
  264. package/dist/market_adapter/lp_chart_core.d.ts.map +1 -0
  265. package/dist/market_adapter/lp_chart_core.js +874 -0
  266. package/dist/market_adapter/lp_chart_core.js.map +1 -0
  267. package/dist/market_adapter/lp_chart_runner.d.ts +135 -0
  268. package/dist/market_adapter/lp_chart_runner.d.ts.map +1 -0
  269. package/dist/market_adapter/lp_chart_runner.js +337 -0
  270. package/dist/market_adapter/lp_chart_runner.js.map +1 -0
  271. package/dist/market_adapter/lp_chart_strategy_loader.d.ts +23 -0
  272. package/dist/market_adapter/lp_chart_strategy_loader.d.ts.map +1 -0
  273. package/dist/market_adapter/lp_chart_strategy_loader.js +169 -0
  274. package/dist/market_adapter/lp_chart_strategy_loader.js.map +1 -0
  275. package/dist/market_adapter/market_adapter.d.ts +133 -0
  276. package/dist/market_adapter/market_adapter.d.ts.map +1 -0
  277. package/dist/market_adapter/market_adapter.js +1331 -0
  278. package/dist/market_adapter/market_adapter.js.map +1 -0
  279. package/dist/market_adapter/merge_lp_data.d.ts +3 -0
  280. package/dist/market_adapter/merge_lp_data.d.ts.map +1 -0
  281. package/dist/market_adapter/merge_lp_data.js +125 -0
  282. package/dist/market_adapter/merge_lp_data.js.map +1 -0
  283. package/dist/market_adapter/test_helpers.d.ts +14 -0
  284. package/dist/market_adapter/test_helpers.d.ts.map +1 -0
  285. package/dist/market_adapter/test_helpers.js +22 -0
  286. package/dist/market_adapter/test_helpers.js.map +1 -0
  287. package/dist/market_adapter/utils/adapter_client.d.ts +20 -0
  288. package/dist/market_adapter/utils/adapter_client.d.ts.map +1 -0
  289. package/dist/market_adapter/utils/adapter_client.js +88 -0
  290. package/dist/market_adapter/utils/adapter_client.js.map +1 -0
  291. package/dist/market_adapter/utils/atomic_write.d.ts +9 -0
  292. package/dist/market_adapter/utils/atomic_write.d.ts.map +1 -0
  293. package/dist/market_adapter/utils/atomic_write.js +13 -0
  294. package/dist/market_adapter/utils/atomic_write.js.map +1 -0
  295. package/dist/market_adapter/utils/chain.d.ts +50 -0
  296. package/dist/market_adapter/utils/chain.d.ts.map +1 -0
  297. package/dist/market_adapter/utils/chain.js +196 -0
  298. package/dist/market_adapter/utils/chain.js.map +1 -0
  299. package/dist/market_adapter/utils/data_discovery.d.ts +6 -0
  300. package/dist/market_adapter/utils/data_discovery.d.ts.map +1 -0
  301. package/dist/market_adapter/utils/data_discovery.js +35 -0
  302. package/dist/market_adapter/utils/data_discovery.js.map +1 -0
  303. package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts +15 -0
  304. package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts.map +1 -0
  305. package/dist/market_adapter/utils/dynamic_grid_snapshot.js +60 -0
  306. package/dist/market_adapter/utils/dynamic_grid_snapshot.js.map +1 -0
  307. package/dist/market_adapter/utils/file_lock.d.ts +19 -0
  308. package/dist/market_adapter/utils/file_lock.d.ts.map +1 -0
  309. package/dist/market_adapter/utils/file_lock.js +191 -0
  310. package/dist/market_adapter/utils/file_lock.js.map +1 -0
  311. package/dist/market_adapter/utils/native_history.d.ts +19 -0
  312. package/dist/market_adapter/utils/native_history.d.ts.map +1 -0
  313. package/dist/market_adapter/utils/native_history.js +172 -0
  314. package/dist/market_adapter/utils/native_history.js.map +1 -0
  315. package/dist/market_adapter/utils/paths.d.ts +5 -0
  316. package/dist/market_adapter/utils/paths.d.ts.map +1 -0
  317. package/dist/market_adapter/utils/paths.js +7 -0
  318. package/dist/market_adapter/utils/paths.js.map +1 -0
  319. package/dist/modules/account_bots.d.ts +112 -0
  320. package/dist/modules/account_bots.d.ts.map +1 -0
  321. package/dist/modules/account_bots.js +1259 -0
  322. package/dist/modules/account_bots.js.map +1 -0
  323. package/dist/modules/account_orders.d.ts +273 -0
  324. package/dist/modules/account_orders.d.ts.map +1 -0
  325. package/dist/modules/account_orders.js +662 -0
  326. package/dist/modules/account_orders.js.map +1 -0
  327. package/dist/modules/authority_resolver.d.ts +2 -0
  328. package/dist/modules/authority_resolver.d.ts.map +1 -0
  329. package/dist/modules/authority_resolver.js +231 -0
  330. package/dist/modules/authority_resolver.js.map +1 -0
  331. package/dist/modules/bitshares-native/chain_client.d.ts +38 -0
  332. package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -0
  333. package/dist/modules/bitshares-native/chain_client.js +322 -0
  334. package/dist/modules/bitshares-native/chain_client.js.map +1 -0
  335. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts +59 -0
  336. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -0
  337. package/dist/modules/bitshares-native/crypto/ecc.browser.js +456 -0
  338. package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -0
  339. package/dist/modules/bitshares-native/crypto/ecc.d.ts +66 -0
  340. package/dist/modules/bitshares-native/crypto/ecc.d.ts.map +1 -0
  341. package/dist/modules/bitshares-native/crypto/ecc.js +557 -0
  342. package/dist/modules/bitshares-native/crypto/ecc.js.map +1 -0
  343. package/dist/modules/bitshares-native/crypto/ecc_selector.d.ts +2 -0
  344. package/dist/modules/bitshares-native/crypto/ecc_selector.d.ts.map +1 -0
  345. package/dist/modules/bitshares-native/crypto/ecc_selector.js +20 -0
  346. package/dist/modules/bitshares-native/crypto/ecc_selector.js.map +1 -0
  347. package/dist/modules/bitshares-native/index.d.ts +3 -0
  348. package/dist/modules/bitshares-native/index.d.ts.map +1 -0
  349. package/dist/modules/bitshares-native/index.js +32 -0
  350. package/dist/modules/bitshares-native/index.js.map +1 -0
  351. package/dist/modules/bitshares-native/resolvers.d.ts +40 -0
  352. package/dist/modules/bitshares-native/resolvers.d.ts.map +1 -0
  353. package/dist/modules/bitshares-native/resolvers.js +161 -0
  354. package/dist/modules/bitshares-native/resolvers.js.map +1 -0
  355. package/dist/modules/bitshares-native/serial/chain_constants.d.ts +56 -0
  356. package/dist/modules/bitshares-native/serial/chain_constants.d.ts.map +1 -0
  357. package/dist/modules/bitshares-native/serial/chain_constants.js +92 -0
  358. package/dist/modules/bitshares-native/serial/chain_constants.js.map +1 -0
  359. package/dist/modules/bitshares-native/serial/index.d.ts +10 -0
  360. package/dist/modules/bitshares-native/serial/index.d.ts.map +1 -0
  361. package/dist/modules/bitshares-native/serial/index.js +14 -0
  362. package/dist/modules/bitshares-native/serial/index.js.map +1 -0
  363. package/dist/modules/bitshares-native/serial/operations.d.ts +102 -0
  364. package/dist/modules/bitshares-native/serial/operations.d.ts.map +1 -0
  365. package/dist/modules/bitshares-native/serial/operations.js +615 -0
  366. package/dist/modules/bitshares-native/serial/operations.js.map +1 -0
  367. package/dist/modules/bitshares-native/serial/serializer.d.ts +64 -0
  368. package/dist/modules/bitshares-native/serial/serializer.d.ts.map +1 -0
  369. package/dist/modules/bitshares-native/serial/serializer.js +252 -0
  370. package/dist/modules/bitshares-native/serial/serializer.js.map +1 -0
  371. package/dist/modules/bitshares-native/serial/types.d.ts +120 -0
  372. package/dist/modules/bitshares-native/serial/types.d.ts.map +1 -0
  373. package/dist/modules/bitshares-native/serial/types.js +837 -0
  374. package/dist/modules/bitshares-native/serial/types.js.map +1 -0
  375. package/dist/modules/bitshares-native/signing_client.d.ts +6 -0
  376. package/dist/modules/bitshares-native/signing_client.d.ts.map +1 -0
  377. package/dist/modules/bitshares-native/signing_client.js +141 -0
  378. package/dist/modules/bitshares-native/signing_client.js.map +1 -0
  379. package/dist/modules/bitshares-native/subscriptions.d.ts +6 -0
  380. package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -0
  381. package/dist/modules/bitshares-native/subscriptions.js +865 -0
  382. package/dist/modules/bitshares-native/subscriptions.js.map +1 -0
  383. package/dist/modules/bitshares-native/transport.d.ts +51 -0
  384. package/dist/modules/bitshares-native/transport.d.ts.map +1 -0
  385. package/dist/modules/bitshares-native/transport.js +447 -0
  386. package/dist/modules/bitshares-native/transport.js.map +1 -0
  387. package/dist/modules/bitshares-native/tx/builder.d.ts +30 -0
  388. package/dist/modules/bitshares-native/tx/builder.d.ts.map +1 -0
  389. package/dist/modules/bitshares-native/tx/builder.js +262 -0
  390. package/dist/modules/bitshares-native/tx/builder.js.map +1 -0
  391. package/dist/modules/bitshares_client.d.ts +129 -0
  392. package/dist/modules/bitshares_client.d.ts.map +1 -0
  393. package/dist/modules/bitshares_client.js +627 -0
  394. package/dist/modules/bitshares_client.js.map +1 -0
  395. package/dist/modules/bot_settings.d.ts +33 -0
  396. package/dist/modules/bot_settings.d.ts.map +1 -0
  397. package/dist/modules/bot_settings.js +328 -0
  398. package/dist/modules/bot_settings.js.map +1 -0
  399. package/dist/modules/bots_file_lock.d.ts +88 -0
  400. package/dist/modules/bots_file_lock.d.ts.map +1 -0
  401. package/dist/modules/bots_file_lock.js +161 -0
  402. package/dist/modules/bots_file_lock.js.map +1 -0
  403. package/dist/modules/chain_keys.d.ts +302 -0
  404. package/dist/modules/chain_keys.d.ts.map +1 -0
  405. package/dist/modules/chain_keys.js +1095 -0
  406. package/dist/modules/chain_keys.js.map +1 -0
  407. package/dist/modules/chain_orders.d.ts +423 -0
  408. package/dist/modules/chain_orders.d.ts.map +1 -0
  409. package/dist/modules/chain_orders.js +1201 -0
  410. package/dist/modules/chain_orders.js.map +1 -0
  411. package/dist/modules/cli_whitelist_args.d.ts +6 -0
  412. package/dist/modules/cli_whitelist_args.d.ts.map +1 -0
  413. package/dist/modules/cli_whitelist_args.js +13 -0
  414. package/dist/modules/cli_whitelist_args.js.map +1 -0
  415. package/dist/modules/config.d.ts +57 -0
  416. package/dist/modules/config.d.ts.map +1 -0
  417. package/dist/modules/config.js +107 -0
  418. package/dist/modules/config.js.map +1 -0
  419. package/dist/modules/constants.d.ts +635 -0
  420. package/dist/modules/constants.d.ts.map +1 -0
  421. package/dist/modules/constants.js +1464 -0
  422. package/dist/modules/constants.js.map +1 -0
  423. package/dist/modules/cr_planner.d.ts +51 -0
  424. package/dist/modules/cr_planner.d.ts.map +1 -0
  425. package/dist/modules/cr_planner.js +299 -0
  426. package/dist/modules/cr_planner.js.map +1 -0
  427. package/dist/modules/credential_policy.d.ts +188 -0
  428. package/dist/modules/credential_policy.d.ts.map +1 -0
  429. package/dist/modules/credential_policy.js +1452 -0
  430. package/dist/modules/credential_policy.js.map +1 -0
  431. package/dist/modules/credential_runtime.d.ts +42 -0
  432. package/dist/modules/credential_runtime.d.ts.map +1 -0
  433. package/dist/modules/credential_runtime.js +148 -0
  434. package/dist/modules/credential_runtime.js.map +1 -0
  435. package/dist/modules/credential_session_cache.d.ts +36 -0
  436. package/dist/modules/credential_session_cache.d.ts.map +1 -0
  437. package/dist/modules/credential_session_cache.js +106 -0
  438. package/dist/modules/credential_session_cache.js.map +1 -0
  439. package/dist/modules/credit_runtime.d.ts +412 -0
  440. package/dist/modules/credit_runtime.d.ts.map +1 -0
  441. package/dist/modules/credit_runtime.js +2866 -0
  442. package/dist/modules/credit_runtime.js.map +1 -0
  443. package/dist/modules/crypto/browser_provider.d.ts +15 -0
  444. package/dist/modules/crypto/browser_provider.d.ts.map +1 -0
  445. package/dist/modules/crypto/browser_provider.js +113 -0
  446. package/dist/modules/crypto/browser_provider.js.map +1 -0
  447. package/dist/modules/crypto/index.d.ts +9 -0
  448. package/dist/modules/crypto/index.d.ts.map +1 -0
  449. package/dist/modules/crypto/index.js +44 -0
  450. package/dist/modules/crypto/index.js.map +1 -0
  451. package/dist/modules/crypto/node_provider.d.ts +15 -0
  452. package/dist/modules/crypto/node_provider.d.ts.map +1 -0
  453. package/dist/modules/crypto/node_provider.js +74 -0
  454. package/dist/modules/crypto/node_provider.js.map +1 -0
  455. package/dist/modules/crypto/provider.d.ts +34 -0
  456. package/dist/modules/crypto/provider.d.ts.map +1 -0
  457. package/dist/modules/crypto/provider.js +3 -0
  458. package/dist/modules/crypto/provider.js.map +1 -0
  459. package/dist/modules/crypto/pure_ripemd160.d.ts +8 -0
  460. package/dist/modules/crypto/pure_ripemd160.d.ts.map +1 -0
  461. package/dist/modules/crypto/pure_ripemd160.js +142 -0
  462. package/dist/modules/crypto/pure_ripemd160.js.map +1 -0
  463. package/dist/modules/crypto/pure_scrypt.d.ts +23 -0
  464. package/dist/modules/crypto/pure_scrypt.d.ts.map +1 -0
  465. package/dist/modules/crypto/pure_scrypt.js +161 -0
  466. package/dist/modules/crypto/pure_scrypt.js.map +1 -0
  467. package/dist/modules/crypto/pure_secp256k1.d.ts +29 -0
  468. package/dist/modules/crypto/pure_secp256k1.d.ts.map +1 -0
  469. package/dist/modules/crypto/pure_secp256k1.js +195 -0
  470. package/dist/modules/crypto/pure_secp256k1.js.map +1 -0
  471. package/dist/modules/crypto/sync.d.ts +13 -0
  472. package/dist/modules/crypto/sync.d.ts.map +1 -0
  473. package/dist/modules/crypto/sync.js +37 -0
  474. package/dist/modules/crypto/sync.js.map +1 -0
  475. package/dist/modules/dexbot_class.d.ts +1074 -0
  476. package/dist/modules/dexbot_class.d.ts.map +1 -0
  477. package/dist/modules/dexbot_class.js +4613 -0
  478. package/dist/modules/dexbot_class.js.map +1 -0
  479. package/dist/modules/dexbot_credential_client.d.ts +71 -0
  480. package/dist/modules/dexbot_credential_client.d.ts.map +1 -0
  481. package/dist/modules/dexbot_credential_client.js +224 -0
  482. package/dist/modules/dexbot_credential_client.js.map +1 -0
  483. package/dist/modules/dexbot_fill_runtime.d.ts +173 -0
  484. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -0
  485. package/dist/modules/dexbot_fill_runtime.js +217 -0
  486. package/dist/modules/dexbot_fill_runtime.js.map +1 -0
  487. package/dist/modules/dexbot_maintenance_runtime.d.ts +279 -0
  488. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -0
  489. package/dist/modules/dexbot_maintenance_runtime.js +1613 -0
  490. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -0
  491. package/dist/modules/env.d.ts +12 -0
  492. package/dist/modules/env.d.ts.map +1 -0
  493. package/dist/modules/env.js +57 -0
  494. package/dist/modules/env.js.map +1 -0
  495. package/dist/modules/fund_registry.d.ts +96 -0
  496. package/dist/modules/fund_registry.d.ts.map +1 -0
  497. package/dist/modules/fund_registry.js +261 -0
  498. package/dist/modules/fund_registry.js.map +1 -0
  499. package/dist/modules/general_settings.d.ts +36 -0
  500. package/dist/modules/general_settings.d.ts.map +1 -0
  501. package/dist/modules/general_settings.js +63 -0
  502. package/dist/modules/general_settings.js.map +1 -0
  503. package/dist/modules/graceful_shutdown.d.ts +86 -0
  504. package/dist/modules/graceful_shutdown.d.ts.map +1 -0
  505. package/dist/modules/graceful_shutdown.js +164 -0
  506. package/dist/modules/graceful_shutdown.js.map +1 -0
  507. package/dist/modules/key_store.d.ts +60 -0
  508. package/dist/modules/key_store.d.ts.map +1 -0
  509. package/dist/modules/key_store.js +215 -0
  510. package/dist/modules/key_store.js.map +1 -0
  511. package/dist/modules/launcher/bot_supervisor.d.ts +84 -0
  512. package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -0
  513. package/dist/modules/launcher/bot_supervisor.js +1067 -0
  514. package/dist/modules/launcher/bot_supervisor.js.map +1 -0
  515. package/dist/modules/launcher/child_env.d.ts +9 -0
  516. package/dist/modules/launcher/child_env.d.ts.map +1 -0
  517. package/dist/modules/launcher/child_env.js +78 -0
  518. package/dist/modules/launcher/child_env.js.map +1 -0
  519. package/dist/modules/launcher/credential_bootstrap.d.ts +22 -0
  520. package/dist/modules/launcher/credential_bootstrap.d.ts.map +1 -0
  521. package/dist/modules/launcher/credential_bootstrap.js +303 -0
  522. package/dist/modules/launcher/credential_bootstrap.js.map +1 -0
  523. package/dist/modules/launcher/credential_daemon.d.ts +32 -0
  524. package/dist/modules/launcher/credential_daemon.d.ts.map +1 -0
  525. package/dist/modules/launcher/credential_daemon.js +173 -0
  526. package/dist/modules/launcher/credential_daemon.js.map +1 -0
  527. package/dist/modules/launcher/credential_secret.d.ts +6 -0
  528. package/dist/modules/launcher/credential_secret.d.ts.map +1 -0
  529. package/dist/modules/launcher/credential_secret.js +15 -0
  530. package/dist/modules/launcher/credential_secret.js.map +1 -0
  531. package/dist/modules/launcher/foreign_cred_daemon.d.ts +2 -0
  532. package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -0
  533. package/dist/modules/launcher/foreign_cred_daemon.js +166 -0
  534. package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -0
  535. package/dist/modules/launcher/headless_password.d.ts +8 -0
  536. package/dist/modules/launcher/headless_password.d.ts.map +1 -0
  537. package/dist/modules/launcher/headless_password.js +33 -0
  538. package/dist/modules/launcher/headless_password.js.map +1 -0
  539. package/dist/modules/launcher/launch_modes.d.ts +36 -0
  540. package/dist/modules/launcher/launch_modes.d.ts.map +1 -0
  541. package/dist/modules/launcher/launch_modes.js +129 -0
  542. package/dist/modules/launcher/launch_modes.js.map +1 -0
  543. package/dist/modules/launcher/market_adapter_runtime.d.ts +43 -0
  544. package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -0
  545. package/dist/modules/launcher/market_adapter_runtime.js +217 -0
  546. package/dist/modules/launcher/market_adapter_runtime.js.map +1 -0
  547. package/dist/modules/launcher/market_adapter_watchdog.d.ts +26 -0
  548. package/dist/modules/launcher/market_adapter_watchdog.d.ts.map +1 -0
  549. package/dist/modules/launcher/market_adapter_watchdog.js +214 -0
  550. package/dist/modules/launcher/market_adapter_watchdog.js.map +1 -0
  551. package/dist/modules/launcher/monolithic_runtime.d.ts +89 -0
  552. package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -0
  553. package/dist/modules/launcher/monolithic_runtime.js +425 -0
  554. package/dist/modules/launcher/monolithic_runtime.js.map +1 -0
  555. package/dist/modules/launcher/runtime_entry.d.ts +26 -0
  556. package/dist/modules/launcher/runtime_entry.d.ts.map +1 -0
  557. package/dist/modules/launcher/runtime_entry.js +49 -0
  558. package/dist/modules/launcher/runtime_entry.js.map +1 -0
  559. package/dist/modules/launcher/status_reporting.d.ts +48 -0
  560. package/dist/modules/launcher/status_reporting.d.ts.map +1 -0
  561. package/dist/modules/launcher/status_reporting.js +113 -0
  562. package/dist/modules/launcher/status_reporting.js.map +1 -0
  563. package/dist/modules/launcher/supervisor_control.d.ts +6 -0
  564. package/dist/modules/launcher/supervisor_control.d.ts.map +1 -0
  565. package/dist/modules/launcher/supervisor_control.js +59 -0
  566. package/dist/modules/launcher/supervisor_control.js.map +1 -0
  567. package/dist/modules/logger.d.ts +2 -0
  568. package/dist/modules/logger.d.ts.map +1 -0
  569. package/dist/modules/logger.js +71 -0
  570. package/dist/modules/logger.js.map +1 -0
  571. package/dist/modules/market_adapter_whitelist.d.ts +22 -0
  572. package/dist/modules/market_adapter_whitelist.d.ts.map +1 -0
  573. package/dist/modules/market_adapter_whitelist.js +82 -0
  574. package/dist/modules/market_adapter_whitelist.js.map +1 -0
  575. package/dist/modules/node_health_cache.d.ts +55 -0
  576. package/dist/modules/node_health_cache.d.ts.map +1 -0
  577. package/dist/modules/node_health_cache.js +134 -0
  578. package/dist/modules/node_health_cache.js.map +1 -0
  579. package/dist/modules/node_manager.d.ts +215 -0
  580. package/dist/modules/node_manager.d.ts.map +1 -0
  581. package/dist/modules/node_manager.js +628 -0
  582. package/dist/modules/node_manager.js.map +1 -0
  583. package/dist/modules/order/accounting.d.ts +349 -0
  584. package/dist/modules/order/accounting.d.ts.map +1 -0
  585. package/dist/modules/order/accounting.js +1191 -0
  586. package/dist/modules/order/accounting.js.map +1 -0
  587. package/dist/modules/order/async_lock.d.ts +126 -0
  588. package/dist/modules/order/async_lock.d.ts.map +1 -0
  589. package/dist/modules/order/async_lock.js +195 -0
  590. package/dist/modules/order/async_lock.js.map +1 -0
  591. package/dist/modules/order/export.d.ts +129 -0
  592. package/dist/modules/order/export.d.ts.map +1 -0
  593. package/dist/modules/order/export.js +316 -0
  594. package/dist/modules/order/export.js.map +1 -0
  595. package/dist/modules/order/format.d.ts +158 -0
  596. package/dist/modules/order/format.d.ts.map +1 -0
  597. package/dist/modules/order/format.js +232 -0
  598. package/dist/modules/order/format.js.map +1 -0
  599. package/dist/modules/order/grid.d.ts +549 -0
  600. package/dist/modules/order/grid.d.ts.map +1 -0
  601. package/dist/modules/order/grid.js +1746 -0
  602. package/dist/modules/order/grid.js.map +1 -0
  603. package/dist/modules/order/grid_reconcile.d.ts +154 -0
  604. package/dist/modules/order/grid_reconcile.d.ts.map +1 -0
  605. package/dist/modules/order/grid_reconcile.js +1192 -0
  606. package/dist/modules/order/grid_reconcile.js.map +1 -0
  607. package/dist/modules/order/index.d.ts +55 -0
  608. package/dist/modules/order/index.d.ts.map +1 -0
  609. package/dist/modules/order/index.js +79 -0
  610. package/dist/modules/order/index.js.map +1 -0
  611. package/dist/modules/order/logger.d.ts +136 -0
  612. package/dist/modules/order/logger.d.ts.map +1 -0
  613. package/dist/modules/order/logger.js +614 -0
  614. package/dist/modules/order/logger.js.map +1 -0
  615. package/dist/modules/order/logger_state.d.ts +133 -0
  616. package/dist/modules/order/logger_state.d.ts.map +1 -0
  617. package/dist/modules/order/logger_state.js +170 -0
  618. package/dist/modules/order/logger_state.js.map +1 -0
  619. package/dist/modules/order/manager.d.ts +413 -0
  620. package/dist/modules/order/manager.d.ts.map +1 -0
  621. package/dist/modules/order/manager.js +1605 -0
  622. package/dist/modules/order/manager.js.map +1 -0
  623. package/dist/modules/order/processed_fill_store.d.ts +135 -0
  624. package/dist/modules/order/processed_fill_store.d.ts.map +1 -0
  625. package/dist/modules/order/processed_fill_store.js +288 -0
  626. package/dist/modules/order/processed_fill_store.js.map +1 -0
  627. package/dist/modules/order/runner.d.ts +70 -0
  628. package/dist/modules/order/runner.d.ts.map +1 -0
  629. package/dist/modules/order/runner.js +140 -0
  630. package/dist/modules/order/runner.js.map +1 -0
  631. package/dist/modules/order/strategy.d.ts +196 -0
  632. package/dist/modules/order/strategy.d.ts.map +1 -0
  633. package/dist/modules/order/strategy.js +398 -0
  634. package/dist/modules/order/strategy.js.map +1 -0
  635. package/dist/modules/order/sync_engine.d.ts +386 -0
  636. package/dist/modules/order/sync_engine.d.ts.map +1 -0
  637. package/dist/modules/order/sync_engine.js +1459 -0
  638. package/dist/modules/order/sync_engine.js.map +1 -0
  639. package/dist/modules/order/utils/math.d.ts +677 -0
  640. package/dist/modules/order/utils/math.d.ts.map +1 -0
  641. package/dist/modules/order/utils/math.js +1172 -0
  642. package/dist/modules/order/utils/math.js.map +1 -0
  643. package/dist/modules/order/utils/order.d.ts +572 -0
  644. package/dist/modules/order/utils/order.d.ts.map +1 -0
  645. package/dist/modules/order/utils/order.js +1145 -0
  646. package/dist/modules/order/utils/order.js.map +1 -0
  647. package/dist/modules/order/utils/system.d.ts +268 -0
  648. package/dist/modules/order/utils/system.d.ts.map +1 -0
  649. package/dist/modules/order/utils/system.js +1261 -0
  650. package/dist/modules/order/utils/system.js.map +1 -0
  651. package/dist/modules/order/utils/validate.d.ts +280 -0
  652. package/dist/modules/order/utils/validate.d.ts.map +1 -0
  653. package/dist/modules/order/utils/validate.js +918 -0
  654. package/dist/modules/order/utils/validate.js.map +1 -0
  655. package/dist/modules/order/working_grid.d.ts +212 -0
  656. package/dist/modules/order/working_grid.d.ts.map +1 -0
  657. package/dist/modules/order/working_grid.js +279 -0
  658. package/dist/modules/order/working_grid.js.map +1 -0
  659. package/dist/modules/path_api.d.ts +32 -0
  660. package/dist/modules/path_api.d.ts.map +1 -0
  661. package/dist/modules/path_api.js +148 -0
  662. package/dist/modules/path_api.js.map +1 -0
  663. package/dist/modules/paths.d.ts +58 -0
  664. package/dist/modules/paths.d.ts.map +1 -0
  665. package/dist/modules/paths.js +82 -0
  666. package/dist/modules/paths.js.map +1 -0
  667. package/dist/modules/process_discovery.d.ts +58 -0
  668. package/dist/modules/process_discovery.d.ts.map +1 -0
  669. package/dist/modules/process_discovery.js +265 -0
  670. package/dist/modules/process_discovery.js.map +1 -0
  671. package/dist/modules/runtime.d.ts +34 -0
  672. package/dist/modules/runtime.d.ts.map +1 -0
  673. package/dist/modules/runtime.js +98 -0
  674. package/dist/modules/runtime.js.map +1 -0
  675. package/dist/modules/runtime_settings.d.ts +12 -0
  676. package/dist/modules/runtime_settings.d.ts.map +1 -0
  677. package/dist/modules/runtime_settings.js +122 -0
  678. package/dist/modules/runtime_settings.js.map +1 -0
  679. package/dist/modules/settings_merge.d.ts +38 -0
  680. package/dist/modules/settings_merge.d.ts.map +1 -0
  681. package/dist/modules/settings_merge.js +246 -0
  682. package/dist/modules/settings_merge.js.map +1 -0
  683. package/dist/modules/storage/browser_adapter.d.ts +56 -0
  684. package/dist/modules/storage/browser_adapter.d.ts.map +1 -0
  685. package/dist/modules/storage/browser_adapter.js +231 -0
  686. package/dist/modules/storage/browser_adapter.js.map +1 -0
  687. package/dist/modules/storage/index.d.ts +28 -0
  688. package/dist/modules/storage/index.d.ts.map +1 -0
  689. package/dist/modules/storage/index.js +52 -0
  690. package/dist/modules/storage/index.js.map +1 -0
  691. package/dist/modules/storage/node_adapter.d.ts +49 -0
  692. package/dist/modules/storage/node_adapter.d.ts.map +1 -0
  693. package/dist/modules/storage/node_adapter.js +167 -0
  694. package/dist/modules/storage/node_adapter.js.map +1 -0
  695. package/dist/modules/storage/types.d.ts +92 -0
  696. package/dist/modules/storage/types.d.ts.map +1 -0
  697. package/dist/modules/storage/types.js +3 -0
  698. package/dist/modules/storage/types.js.map +1 -0
  699. package/dist/modules/types.d.ts +1382 -0
  700. package/dist/modules/types.d.ts.map +1 -0
  701. package/dist/modules/types.js +11 -0
  702. package/dist/modules/types.js.map +1 -0
  703. package/dist/modules/utils/base58check.d.ts +12 -0
  704. package/dist/modules/utils/base58check.d.ts.map +1 -0
  705. package/dist/modules/utils/base58check.js +112 -0
  706. package/dist/modules/utils/base58check.js.map +1 -0
  707. package/dist/modules/utils/build_dir.d.ts +7 -0
  708. package/dist/modules/utils/build_dir.d.ts.map +1 -0
  709. package/dist/modules/utils/build_dir.js +8 -0
  710. package/dist/modules/utils/build_dir.js.map +1 -0
  711. package/dist/modules/utils/fs_utils.d.ts +25 -0
  712. package/dist/modules/utils/fs_utils.d.ts.map +1 -0
  713. package/dist/modules/utils/fs_utils.js +27 -0
  714. package/dist/modules/utils/fs_utils.js.map +1 -0
  715. package/dist/modules/utils/math_utils.d.ts +10 -0
  716. package/dist/modules/utils/math_utils.d.ts.map +1 -0
  717. package/dist/modules/utils/math_utils.js +17 -0
  718. package/dist/modules/utils/math_utils.js.map +1 -0
  719. package/dist/modules/validate_profiles.d.ts +37 -0
  720. package/dist/modules/validate_profiles.d.ts.map +1 -0
  721. package/dist/modules/validate_profiles.js +369 -0
  722. package/dist/modules/validate_profiles.js.map +1 -0
  723. package/dist/pm2.d.ts +167 -0
  724. package/dist/pm2.d.ts.map +1 -0
  725. package/dist/pm2.js +998 -0
  726. package/dist/pm2.js.map +1 -0
  727. package/dist/scripts/analyze-git.d.ts +11 -0
  728. package/dist/scripts/analyze-git.d.ts.map +1 -0
  729. package/dist/scripts/analyze-git.js +851 -0
  730. package/dist/scripts/analyze-git.js.map +1 -0
  731. package/dist/scripts/analyze-orders.d.ts +17 -0
  732. package/dist/scripts/analyze-orders.d.ts.map +1 -0
  733. package/dist/scripts/analyze-orders.js +1568 -0
  734. package/dist/scripts/analyze-orders.js.map +1 -0
  735. package/dist/scripts/diagnose-kibana-candles.d.ts +3 -0
  736. package/dist/scripts/diagnose-kibana-candles.d.ts.map +1 -0
  737. package/dist/scripts/diagnose-kibana-candles.js +82 -0
  738. package/dist/scripts/diagnose-kibana-candles.js.map +1 -0
  739. package/dist/scripts/diagnose-pool-history.d.ts +3 -0
  740. package/dist/scripts/diagnose-pool-history.d.ts.map +1 -0
  741. package/dist/scripts/diagnose-pool-history.js +296 -0
  742. package/dist/scripts/diagnose-pool-history.js.map +1 -0
  743. package/dist/scripts/divergence-calc.d.ts +26 -0
  744. package/dist/scripts/divergence-calc.d.ts.map +1 -0
  745. package/dist/scripts/divergence-calc.js +119 -0
  746. package/dist/scripts/divergence-calc.js.map +1 -0
  747. package/dist/scripts/generate_lp_chart.d.ts +31 -0
  748. package/dist/scripts/generate_lp_chart.d.ts.map +1 -0
  749. package/dist/scripts/generate_lp_chart.js +92 -0
  750. package/dist/scripts/generate_lp_chart.js.map +1 -0
  751. package/dist/scripts/generate_mainnet_corpus_report.d.ts +24 -0
  752. package/dist/scripts/generate_mainnet_corpus_report.d.ts.map +1 -0
  753. package/dist/scripts/generate_mainnet_corpus_report.js +237 -0
  754. package/dist/scripts/generate_mainnet_corpus_report.js.map +1 -0
  755. package/dist/scripts/generate_market_adapter_whitelist.d.ts +2 -0
  756. package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +1 -0
  757. package/dist/scripts/generate_market_adapter_whitelist.js +111 -0
  758. package/dist/scripts/generate_market_adapter_whitelist.js.map +1 -0
  759. package/dist/scripts/migrate_bot_keys.d.ts +6 -0
  760. package/dist/scripts/migrate_bot_keys.d.ts.map +1 -0
  761. package/dist/scripts/migrate_bot_keys.js +301 -0
  762. package/dist/scripts/migrate_bot_keys.js.map +1 -0
  763. package/dist/scripts/native_release_gates.d.ts +3 -0
  764. package/dist/scripts/native_release_gates.d.ts.map +1 -0
  765. package/dist/scripts/native_release_gates.js +52 -0
  766. package/dist/scripts/native_release_gates.js.map +1 -0
  767. package/dist/scripts/print_grid.d.ts +16 -0
  768. package/dist/scripts/print_grid.d.ts.map +1 -0
  769. package/dist/scripts/print_grid.js +80 -0
  770. package/dist/scripts/print_grid.js.map +1 -0
  771. package/dist/scripts/run-tests.d.ts +2 -0
  772. package/dist/scripts/run-tests.d.ts.map +1 -0
  773. package/dist/scripts/run-tests.js +49 -0
  774. package/dist/scripts/run-tests.js.map +1 -0
  775. package/dist/scripts/sync-version.d.ts +14 -0
  776. package/dist/scripts/sync-version.d.ts.map +1 -0
  777. package/dist/scripts/sync-version.js +158 -0
  778. package/dist/scripts/sync-version.js.map +1 -0
  779. package/dist/scripts/test-credit-renewal.d.ts +2 -0
  780. package/dist/scripts/test-credit-renewal.d.ts.map +1 -0
  781. package/dist/scripts/test-credit-renewal.js +329 -0
  782. package/dist/scripts/test-credit-renewal.js.map +1 -0
  783. package/dist/scripts/update.d.ts +24 -0
  784. package/dist/scripts/update.d.ts.map +1 -0
  785. package/dist/scripts/update.js +650 -0
  786. package/dist/scripts/update.js.map +1 -0
  787. package/dist/scripts/validate_bots.d.ts +16 -0
  788. package/dist/scripts/validate_bots.d.ts.map +1 -0
  789. package/dist/scripts/validate_bots.js +106 -0
  790. package/dist/scripts/validate_bots.js.map +1 -0
  791. package/dist/scripts/verify-browser-bundle.d.ts +22 -0
  792. package/dist/scripts/verify-browser-bundle.d.ts.map +1 -0
  793. package/dist/scripts/verify-browser-bundle.js +339 -0
  794. package/dist/scripts/verify-browser-bundle.js.map +1 -0
  795. package/dist/unlock.d.ts +48 -0
  796. package/dist/unlock.d.ts.map +1 -0
  797. package/dist/unlock.js +925 -0
  798. package/dist/unlock.js.map +1 -0
  799. package/package.json +112 -0
  800. package/scripts/README.md +356 -0
  801. package/scripts/clear-all.sh +195 -0
  802. package/scripts/clear-logs.sh +95 -0
  803. package/scripts/clear-market-adapter.sh +196 -0
  804. package/scripts/clear-orders.sh +113 -0
  805. package/scripts/create-bot-symlinks.sh +51 -0
  806. package/scripts/postinstall.js +9 -0
  807. package/scripts/reset-settings.sh +118 -0
@@ -0,0 +1,2328 @@
1
+ 'use strict';
2
+ const { calculateATR } = require('./strategies/atr/calculator');
3
+ const { computeAmaSlopeWeights, computeAverageAmaSlopePct, } = require('./strategies/ama_slope_model');
4
+ const { normalizeAtrPeriod, normalizeMaxVolatilityOffset, normalizeVolatilityThreshold, } = require('./config_normalizers');
5
+ const { normalizeMarketSource, hasNumericStartPrice, resolveMarketSourceForBot } = require('../utils/chain');
6
+ const { computeRegimeMultiplier } = require('./strategies/regime_gate');
7
+ const { calculateAMA, getAmaWarmupBars } = require('./strategies/ama');
8
+ const { KalmanTrendAnalyzer } = require('../../analysis/trend_detection/kalman_trend_analyzer');
9
+ const { buildKalmanVelocitySeries, computeAbsolutePercentileThreshold, } = require('../../analysis/trend_detection/kalman_velocity_smoothing');
10
+ const { adjustCollateralRatio } = require('./strategies/collateral_manager');
11
+ const { resolveMaxAsymmetryFactor, computeAsymmetricBoundsMetrics, } = require('./asymmetric_bounds');
12
+ const { DEFAULT_CONFIG, MARKET_ADAPTER } = require('../../modules/constants');
13
+ const { resolveConfiguredPriceBound } = require('../../modules/order/utils/order');
14
+ const Logger = require('../../modules/logger');
15
+ const { roundTo } = require('../../modules/utils/math_utils');
16
+ const marketAdapterServiceLogger = new Logger('MarketAdapterService');
17
+ const AMA_SLOPE_PERCENT_MODE_PER_BAR = 'perBar';
18
+ const AMA_SLOPE_PERCENT_MODE_WINDOW = 'window';
19
+ function normalizeAmaSlopePercentMode(value) {
20
+ const text = String(value || '').trim().toLowerCase();
21
+ if (['perbar', 'per_bar', 'per-bar', 'averageperbar', 'average_per_bar'].includes(text)) {
22
+ return AMA_SLOPE_PERCENT_MODE_PER_BAR;
23
+ }
24
+ if (['window', 'lookback', 'cumulative', 'legacy'].includes(text)) {
25
+ return AMA_SLOPE_PERCENT_MODE_WINDOW;
26
+ }
27
+ return null;
28
+ }
29
+ function normalizeAmaSlopeLookbackBars(value, fallback = MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_LOOKBACK_BARS) {
30
+ const n = Number(value);
31
+ if (Number.isFinite(n) && n > 0)
32
+ return Math.ceil(n);
33
+ return fallback;
34
+ }
35
+ function convertSlopePercentToPerBar(value, lookbackBars, mode) {
36
+ const n = Number(value);
37
+ if (!Number.isFinite(n))
38
+ return null;
39
+ return mode === AMA_SLOPE_PERCENT_MODE_PER_BAR
40
+ ? n
41
+ : n / normalizeAmaSlopeLookbackBars(lookbackBars);
42
+ }
43
+ function normalizePersistedAmaSlopeSnapshot(snapshot, lookbackBars, mode) {
44
+ if (!snapshot || typeof snapshot !== 'object')
45
+ return null;
46
+ const normalized = { ...snapshot };
47
+ const slopePct = convertSlopePercentToPerBar(snapshot.slopePct, lookbackBars, mode);
48
+ if (Number.isFinite(slopePct)) {
49
+ normalized.slopePct = slopePct;
50
+ }
51
+ return normalized;
52
+ }
53
+ function normalizePersistedAmaSlopeDiagnostics(data, lookbackBars) {
54
+ if (!data || typeof data !== 'object')
55
+ return data;
56
+ const mode = normalizeAmaSlopePercentMode(data.amaSlopePercentMode) || AMA_SLOPE_PERCENT_MODE_WINDOW;
57
+ const normalized = { ...data };
58
+ normalized.amaSlopePercentMode = AMA_SLOPE_PERCENT_MODE_PER_BAR;
59
+ normalized.amaSlope = normalizePersistedAmaSlopeSnapshot(data.amaSlope, lookbackBars, mode);
60
+ normalized.gridRangeScalingAmaSlope = normalizePersistedAmaSlopeSnapshot(data.gridRangeScalingAmaSlope, lookbackBars, mode);
61
+ const deltaPercent = convertSlopePercentToPerBar(data.amaSlopeDeltaPercent, lookbackBars, mode);
62
+ const thresholdPercent = convertSlopePercentToPerBar(data.amaSlopeThresholdPercent, lookbackBars, mode);
63
+ normalized.amaSlopeDeltaPercent = Number.isFinite(deltaPercent) ? deltaPercent : null;
64
+ normalized.amaSlopeThresholdPercent = Number.isFinite(thresholdPercent) ? thresholdPercent : null;
65
+ return normalized;
66
+ }
67
+ function computeGridPriceOffsetPlan(bot, amaSlope) {
68
+ const targetSpreadPercentRaw = Number(bot?.targetSpreadPercent);
69
+ const targetSpreadPercent = Number.isFinite(targetSpreadPercentRaw) && targetSpreadPercentRaw > 0
70
+ ? targetSpreadPercentRaw
71
+ : Number(DEFAULT_CONFIG.targetSpreadPercent);
72
+ const maxGridPriceOffsetPct = targetSpreadPercent / 2;
73
+ const trend = amaSlope?.trend;
74
+ const rawSlopeOffset = Number(amaSlope?.rawSlopeOffset);
75
+ const maxSlopeOffset = Number(amaSlope?.maxSlopeOffset);
76
+ const directionalSlope = Number.isFinite(rawSlopeOffset)
77
+ ? Math.abs(rawSlopeOffset)
78
+ : Math.abs(Number(amaSlope?.slopeOffset));
79
+ const slopeRatio = Number.isFinite(directionalSlope) && Number.isFinite(maxSlopeOffset) && maxSlopeOffset > 0
80
+ ? Math.min(directionalSlope / maxSlopeOffset, 1)
81
+ : 0;
82
+ const direction = trend === 'UP' ? 1 : trend === 'DOWN' ? -1 : 0;
83
+ const gridPriceOffsetPct = roundTo(direction * slopeRatio * maxGridPriceOffsetPct, 1e6) || 0;
84
+ return {
85
+ trend: trend || 'NEUTRAL',
86
+ rawSlopeOffset: Number.isFinite(rawSlopeOffset) ? rawSlopeOffset : null,
87
+ maxSlopeOffset: Number.isFinite(maxSlopeOffset) ? maxSlopeOffset : null,
88
+ slopeRatio: roundTo(slopeRatio, 1e6) || 0,
89
+ targetSpreadPercent: roundTo(targetSpreadPercent, 1e6),
90
+ maxGridPriceOffsetPct: roundTo(maxGridPriceOffsetPct, 1e6),
91
+ gridPriceOffsetPct,
92
+ };
93
+ }
94
+ class MarketAdapterService {
95
+ deps;
96
+ constructor(deps = {}) {
97
+ this.deps = deps;
98
+ }
99
+ static isRetryableClosedCandleFailure(reason) {
100
+ return reason === 'ama_center_persist_failed'
101
+ || reason === 'dynamic_weight_persist_failed'
102
+ || reason === 'ama_slope_persist_failed'
103
+ || reason === 'unresolved_candle_gaps';
104
+ }
105
+ getNowMs() {
106
+ return typeof this.deps.getNowMs === 'function' ? this.deps.getNowMs() : Date.now();
107
+ }
108
+ selectClosedCandles(candles, intervalSeconds, nowMs = this.getNowMs()) {
109
+ const bucketMs = Number(intervalSeconds) * 1000;
110
+ if (!Number.isFinite(bucketMs) || bucketMs <= 0) {
111
+ return {
112
+ closedCandles: Array.isArray(candles) ? candles.slice() : [],
113
+ currentBucketStartMs: null,
114
+ };
115
+ }
116
+ const currentBucketStartMs = Math.floor(Number(nowMs) / bucketMs) * bucketMs;
117
+ const closedCandles = (Array.isArray(candles) ? candles : [])
118
+ .filter((c) => Array.isArray(c) && Number.isFinite(c[0]) && c[0] < currentBucketStartMs);
119
+ return { closedCandles, currentBucketStartMs };
120
+ }
121
+ buildBotContextSignature(bot) {
122
+ return [
123
+ bot?.assetA,
124
+ bot?.assetB,
125
+ bot?.assetAId,
126
+ bot?.assetBId,
127
+ bot?.assetAPrecision,
128
+ bot?.assetBPrecision,
129
+ bot?.poolId,
130
+ bot?.startPrice,
131
+ ].map((v) => String(v ?? '')).join('|');
132
+ }
133
+ buildGapRepairTimeRange(missingTimestamps, intervalSeconds, maxGapHours = 24) {
134
+ const bucketMs = Number(intervalSeconds) * 1000;
135
+ if (!Array.isArray(missingTimestamps) || missingTimestamps.length === 0)
136
+ return null;
137
+ if (!Number.isFinite(bucketMs) || bucketMs <= 0)
138
+ return null;
139
+ const maxGapMs = Number.isFinite(maxGapHours) && maxGapHours > 0 ? maxGapHours * 3600 * 1000 : 24 * 3600 * 1000;
140
+ const requestedStart = missingTimestamps[0] - bucketMs;
141
+ const requestedEnd = missingTimestamps[missingTimestamps.length - 1] + (bucketMs * 2) - 1;
142
+ const cappedStart = Math.max(requestedStart, requestedEnd - maxGapMs);
143
+ return {
144
+ gte: new Date(cappedStart).toISOString(),
145
+ lte: new Date(requestedEnd).toISOString(),
146
+ };
147
+ }
148
+ getMissingTimestampsWithinTimeRange(missingTimestamps, timeRange) {
149
+ if (!Array.isArray(missingTimestamps) || missingTimestamps.length === 0 || !timeRange)
150
+ return [];
151
+ const gteMs = Date.parse(timeRange.gte);
152
+ const lteMs = Date.parse(timeRange.lte);
153
+ if (!Number.isFinite(gteMs) || !Number.isFinite(lteMs) || lteMs < gteMs)
154
+ return [];
155
+ return missingTimestamps.filter((ts) => Number.isFinite(ts) && ts >= gteMs && ts <= lteMs);
156
+ }
157
+ getGapRepairMaxHours(cfg) {
158
+ const intervalSeconds = Number(cfg?.intervalSeconds);
159
+ const intervalHours = Number.isFinite(intervalSeconds) && intervalSeconds > 0
160
+ ? intervalSeconds / 3600
161
+ : 1;
162
+ const maxCandles = Number.isFinite(cfg?.maxNativeGapFillCandles)
163
+ ? cfg.maxNativeGapFillCandles
164
+ : MARKET_ADAPTER.STALE_TAIL_THRESHOLD_CANDLES;
165
+ // Include the candle before and after the missing run so Kibana repair
166
+ // does not truncate a valid threshold-sized gap while still respecting
167
+ // the configured "trust native no-trade up to N candles" threshold.
168
+ return Math.max(1, (maxCandles + 2) * intervalHours);
169
+ }
170
+ getTrustedNoTradeGapThresholdCandles(cfg) {
171
+ return Number.isFinite(cfg?.maxNativeGapFillCandles)
172
+ ? cfg.maxNativeGapFillCandles
173
+ : MARKET_ADAPTER.STALE_TAIL_THRESHOLD_CANDLES;
174
+ }
175
+ fillNativeIncrementalClosedGaps(candles, previousLastTs, intervalSeconds, nowMs = this.getNowMs()) {
176
+ const deps = this.deps;
177
+ if (typeof deps.fillCandleGaps !== 'function')
178
+ return candles;
179
+ if (!Array.isArray(candles) || candles.length === 0)
180
+ return candles;
181
+ const bucketMs = Number(intervalSeconds) * 1000;
182
+ const startTs = Number(previousLastTs);
183
+ if (!Number.isFinite(bucketMs) || bucketMs <= 0 || !Number.isFinite(startTs) || startTs <= 0) {
184
+ return candles;
185
+ }
186
+ const currentBucketStartMs = Math.floor(Number(nowMs) / bucketMs) * bucketMs;
187
+ const latestClosedBucketTs = currentBucketStartMs - bucketMs;
188
+ if (!Number.isFinite(latestClosedBucketTs) || latestClosedBucketTs < startTs) {
189
+ return candles;
190
+ }
191
+ const tailCandles = candles.filter((c) => Array.isArray(c) && Number.isFinite(c[0]) && c[0] >= startTs);
192
+ if (tailCandles.length === 0)
193
+ return candles;
194
+ const filledTail = deps.fillCandleGaps(tailCandles, intervalSeconds, startTs, latestClosedBucketTs);
195
+ return deps.mergeCandles(candles, filledTail);
196
+ }
197
+ buildIncrementalCandleCollision(existing, incoming) {
198
+ const existingVol = Number(existing?.[5] || 0);
199
+ const incomingVol = Number(incoming?.[5] || 0);
200
+ if (existingVol <= 0 && incomingVol > 0)
201
+ return incoming;
202
+ if (incomingVol <= 0)
203
+ return existing;
204
+ return [
205
+ existing[0],
206
+ existing[1],
207
+ Math.max(existing[2], incoming[2]),
208
+ Math.min(existing[3], incoming[3]),
209
+ incoming[4],
210
+ existingVol + incomingVol,
211
+ ];
212
+ }
213
+ // Historical internal holes are different from the live trailing silence path:
214
+ // native incremental fetches cannot directly tell us "no trades happened here"
215
+ // because the hole is already detached from the current native fetch window.
216
+ // When Kibana also returns no candles for a bounded internal run, we accept
217
+ // that as verified no-trade and synthesize flat candles up to the same
218
+ // trusted no-trade threshold used by live native silence handling.
219
+ fillVerifiedInternalNoTradeGaps(candles, missingTimestamps, intervalSeconds, maxGapCandles) {
220
+ const deps = this.deps;
221
+ const bucketMs = Number(intervalSeconds) * 1000;
222
+ if (!Array.isArray(candles) || candles.length === 0 || !Array.isArray(missingTimestamps) || missingTimestamps.length === 0) {
223
+ return { candles, filledTimestamps: [] };
224
+ }
225
+ if (!Number.isFinite(bucketMs) || bucketMs <= 0)
226
+ return { candles, filledTimestamps: [] };
227
+ if (!Number.isFinite(maxGapCandles) || maxGapCandles <= 0)
228
+ return { candles, filledTimestamps: [] };
229
+ const sortedCandles = candles
230
+ .filter((c) => Array.isArray(c) && Number.isFinite(c[0]))
231
+ .slice()
232
+ .sort((a, b) => a[0] - b[0]);
233
+ const sortedMissing = missingTimestamps
234
+ .filter((ts) => Number.isFinite(ts))
235
+ .slice()
236
+ .sort((a, b) => a - b);
237
+ if (sortedCandles.length === 0 || sortedMissing.length === 0) {
238
+ return { candles: sortedCandles, filledTimestamps: [] };
239
+ }
240
+ const candleByTs = new Map(sortedCandles.map((c) => [c[0], c]));
241
+ const synthesized = [];
242
+ const filledTimestamps = [];
243
+ let runStart = sortedMissing[0];
244
+ let previousMissingTs = sortedMissing[0];
245
+ const flushRun = (startTs, endTs) => {
246
+ const runLength = Math.round((endTs - startTs) / bucketMs) + 1;
247
+ if (runLength <= 0 || runLength > maxGapCandles)
248
+ return;
249
+ const previousCandle = candleByTs.get(startTs - bucketMs);
250
+ const nextCandle = candleByTs.get(endTs + bucketMs);
251
+ if (!previousCandle || !nextCandle)
252
+ return;
253
+ const baselineClose = Number(previousCandle[4]);
254
+ if (!Number.isFinite(baselineClose) || baselineClose <= 0)
255
+ return;
256
+ for (let ts = startTs; ts <= endTs; ts += bucketMs) {
257
+ const c = [ts, baselineClose, baselineClose, baselineClose, baselineClose, 0];
258
+ synthesized.push(c);
259
+ filledTimestamps.push(ts);
260
+ }
261
+ };
262
+ for (let i = 1; i < sortedMissing.length; i++) {
263
+ const currentTs = sortedMissing[i];
264
+ if (currentTs !== previousMissingTs + bucketMs) {
265
+ flushRun(runStart, previousMissingTs);
266
+ runStart = currentTs;
267
+ }
268
+ previousMissingTs = currentTs;
269
+ }
270
+ flushRun(runStart, previousMissingTs);
271
+ if (synthesized.length === 0)
272
+ return { candles: sortedCandles, filledTimestamps: [] };
273
+ return {
274
+ candles: deps.mergeCandles(sortedCandles, synthesized),
275
+ filledTimestamps,
276
+ };
277
+ }
278
+ getNativeRecentTradeSequences(trades, limit = 8) {
279
+ const seen = new Set();
280
+ return (Array.isArray(trades) ? trades : [])
281
+ .filter((t) => Number.isFinite(Number(t?.sequence)))
282
+ .sort((a, b) => {
283
+ const at = Number(a.tsMs || 0);
284
+ const bt = Number(b.tsMs || 0);
285
+ if (bt !== at)
286
+ return bt - at;
287
+ return Number(b.sequence) - Number(a.sequence);
288
+ })
289
+ .map((t) => Number(t.sequence))
290
+ .filter((seq) => {
291
+ const key = String(seq);
292
+ if (seen.has(key))
293
+ return false;
294
+ seen.add(key);
295
+ return true;
296
+ })
297
+ .slice(0, limit);
298
+ }
299
+ filterTimeBasedNativeNewTrades(trades, knownSequences, nativeLastTradeTs, lastCandleTs, intervalSeconds) {
300
+ const seqSet = knownSequences instanceof Set
301
+ ? knownSequences
302
+ : new Set((Array.isArray(knownSequences) ? knownSequences : []).map((seq) => String(seq)));
303
+ const seqNumbers = [...seqSet]
304
+ .map((seq) => Number(seq))
305
+ .filter(Number.isFinite);
306
+ const maxKnownSeq = seqNumbers.length > 0 ? Math.max(...seqNumbers) : null;
307
+ const lastTradeTs = Number(nativeLastTradeTs);
308
+ const lastTs = Number(lastCandleTs);
309
+ const bucketMs = Number(intervalSeconds) * 1000;
310
+ return (Array.isArray(trades) ? trades : []).filter((trade) => {
311
+ const seq = Number(trade?.sequence);
312
+ const seqKey = Number.isFinite(seq) ? String(seq) : null;
313
+ if (seqKey && seqSet.has(seqKey))
314
+ return false;
315
+ if (Number.isFinite(seq) && Number.isFinite(maxKnownSeq))
316
+ return seq > maxKnownSeq;
317
+ const tsMs = Number(trade?.tsMs);
318
+ if (!Number.isFinite(tsMs))
319
+ return true;
320
+ if (Number.isFinite(lastTradeTs) && lastTradeTs > 0)
321
+ return tsMs > lastTradeTs;
322
+ if (Number.isFinite(bucketMs) && bucketMs > 0 && Number.isFinite(lastTs) && lastTs > 0) {
323
+ const tradeBucketTs = Math.floor(tsMs / bucketMs) * bucketMs;
324
+ return tradeBucketTs > lastTs;
325
+ }
326
+ return true;
327
+ });
328
+ }
329
+ clampGridPriceToBounds(centerPrice, referencePrice, bot) {
330
+ const base = Number(centerPrice);
331
+ const ref = Number(referencePrice);
332
+ if (!Number.isFinite(base) || base <= 0)
333
+ return centerPrice;
334
+ try {
335
+ const startPrice = Number.isFinite(ref) && ref > 0 ? ref : base;
336
+ const minP = resolveConfiguredPriceBound(bot?.minPrice, DEFAULT_CONFIG.minPrice, startPrice, 'min');
337
+ const maxP = resolveConfiguredPriceBound(bot?.maxPrice, DEFAULT_CONFIG.maxPrice, startPrice, 'max');
338
+ if (!Number.isFinite(minP) || !Number.isFinite(maxP))
339
+ return base;
340
+ return Math.min(maxP, Math.max(minP, base));
341
+ }
342
+ catch (err) {
343
+ throw new Error(`clampGridPriceToBounds: failed to resolve bounds: ${err.message}`);
344
+ }
345
+ }
346
+ computeAppliedAsymmetryMetrics(bot, centerPrice, dynamicWeights) {
347
+ const maxAsymmetryFactor = resolveMaxAsymmetryFactor(bot?.asymmetricBounds?.maxAsymmetryFactor, dynamicWeights?.maxAsymmetryFactor, MARKET_ADAPTER.ASYMMETRIC_BOUNDS_MAX_ASYMMETRY_FACTOR);
348
+ let minP = null;
349
+ let maxP = null;
350
+ try {
351
+ minP = resolveConfiguredPriceBound(bot?.minPrice, DEFAULT_CONFIG.minPrice, centerPrice, 'min');
352
+ maxP = resolveConfiguredPriceBound(bot?.maxPrice, DEFAULT_CONFIG.maxPrice, centerPrice, 'max');
353
+ }
354
+ catch (_) {
355
+ // Intentional: if bound resolution fails, minP/maxP stay null.
356
+ // computeAsymmetricBoundsMetrics handles null bounds with safe defaults.
357
+ }
358
+ return computeAsymmetricBoundsMetrics({
359
+ centerPrice,
360
+ minPrice: minP,
361
+ maxPrice: maxP,
362
+ trend: dynamicWeights?.trend,
363
+ slopeOffset: Number.isFinite(dynamicWeights?.rawSlopeOffset)
364
+ ? dynamicWeights.rawSlopeOffset
365
+ : dynamicWeights?.slopeOffset,
366
+ maxSlopeOffset: dynamicWeights?.maxSlopeOffset,
367
+ maxAsymmetryFactor,
368
+ });
369
+ }
370
+ buildDefaultBotState(bot, overrides = {}) {
371
+ return {
372
+ botName: bot.name,
373
+ botKey: bot.botKey,
374
+ marketSource: null,
375
+ priceMode: null,
376
+ lastCycleSource: null,
377
+ lastCycleAt: null,
378
+ pendingClosedCandle: false,
379
+ lastTriggerSuppressedReason: null,
380
+ poolId: null,
381
+ candleFile: null,
382
+ candleCount: 0,
383
+ analysisCandleCount: 0,
384
+ kibanaGapRepairCount: 0,
385
+ kibanaBackfillCount: 0,
386
+ unresolvedGapCount: 0,
387
+ nativeRecentTradeSequences: [],
388
+ nativeLastTradeTs: null,
389
+ nativeOverlapCount: null,
390
+ nativePagesFetched: null,
391
+ lastCandleTs: null,
392
+ rawLastCandleTs: null,
393
+ lastClosedCandleTs: null,
394
+ gridCenterPrice: null,
395
+ centerPrice: null,
396
+ amaCenterPrice: null,
397
+ amaConfig: null,
398
+ atr: null,
399
+ weightVariance: null,
400
+ weights: null,
401
+ effectiveWeights: null,
402
+ collateralRecommendation: null,
403
+ amaSlope: null,
404
+ amaSlopeDeltaPercent: null,
405
+ amaSlopeThresholdPercent: null,
406
+ rawKeepCount: 0,
407
+ analysisKeepCount: 0,
408
+ amaWarmupBars: 0,
409
+ staleData: false,
410
+ staleAgeHours: null,
411
+ ...overrides,
412
+ };
413
+ }
414
+ buildDefaultResult(bot, overrides = {}) {
415
+ return {
416
+ ok: true,
417
+ dryRunMessages: [],
418
+ source: null,
419
+ marketSource: null,
420
+ candleCount: 0,
421
+ analysisCandleCount: 0,
422
+ kibanaGapRepairCount: 0,
423
+ kibanaBackfillCount: 0,
424
+ unresolvedGapCount: 0,
425
+ nativeRecentTradeSequences: [],
426
+ nativeLastTradeTs: null,
427
+ nativeOverlapCount: null,
428
+ nativePagesFetched: null,
429
+ amaPrice: null,
430
+ deltaPercent: null,
431
+ thresholdPercent: null,
432
+ referencePrice: null,
433
+ amaComparison: [],
434
+ triggered: false,
435
+ triggerPath: null,
436
+ staleData: false,
437
+ staleAgeHours: null,
438
+ triggerCallbackError: null,
439
+ triggerSuppressedReason: null,
440
+ weights: null,
441
+ collateralRecommendation: null,
442
+ amaSlope: null,
443
+ amaSlopeDeltaPercent: null,
444
+ amaSlopeThresholdPercent: null,
445
+ rawKeepCount: 0,
446
+ analysisKeepCount: 0,
447
+ amaWarmupBars: 0,
448
+ poolId: null,
449
+ candleFile: null,
450
+ lastCandleTs: null,
451
+ rawLastCandleTs: null,
452
+ lastClosedCandleTs: null,
453
+ centerPrice: null,
454
+ amaConfig: null,
455
+ atr: null,
456
+ weightVariance: null,
457
+ pendingClosedCandle: false,
458
+ ...overrides,
459
+ };
460
+ }
461
+ resolveAmaSlopeDeltaThresholdPercent(cfg) {
462
+ const explicit = Number(cfg?.amaSlopeDeltaThresholdPercent);
463
+ if (Number.isFinite(explicit) && explicit > 0)
464
+ return explicit;
465
+ const factor = Number(cfg?.amaSlope?.deltaThresholdPct);
466
+ if (!Number.isFinite(factor) || factor <= 0)
467
+ return 0;
468
+ const maxSlopePct = Number(cfg?.amaSlope?.maxSlopePct);
469
+ if (!Number.isFinite(maxSlopePct) || maxSlopePct <= 0)
470
+ return 0;
471
+ return (factor / 100) * maxSlopePct;
472
+ }
473
+ buildAmaSlopeResetDetails(currentAmaSlope, previousAmaSlope, cfg) {
474
+ const thresholdPercent = this.resolveAmaSlopeDeltaThresholdPercent(cfg);
475
+ const currentSlopePct = Number(currentAmaSlope?.slopePct);
476
+ const previousSlopePct = Number(previousAmaSlope?.slopePct);
477
+ const currentReady = !!currentAmaSlope?.isReady && Number.isFinite(currentSlopePct);
478
+ const previousReady = !!previousAmaSlope && Number.isFinite(previousSlopePct);
479
+ const deltaPercent = currentReady && previousReady
480
+ ? Math.abs(currentSlopePct - previousSlopePct)
481
+ : null;
482
+ const thresholdCrossed = Number.isFinite(deltaPercent) && deltaPercent >= thresholdPercent;
483
+ return {
484
+ thresholdPercent,
485
+ currentSlopePct: Number.isFinite(currentSlopePct) ? currentSlopePct : null,
486
+ previousSlopePct: Number.isFinite(previousSlopePct) ? previousSlopePct : null,
487
+ deltaPercent,
488
+ thresholdCrossed,
489
+ shouldTrigger: thresholdCrossed,
490
+ };
491
+ }
492
+ normalizePersistedBotState(botState, lookbackBars) {
493
+ if (!botState || typeof botState !== 'object')
494
+ return {};
495
+ return normalizePersistedAmaSlopeDiagnostics(botState, lookbackBars);
496
+ }
497
+ extractPersistedDynamicGridState(snapshot, lookbackBars) {
498
+ if (!snapshot || typeof snapshot !== 'object')
499
+ return null;
500
+ const gridCenterPrice = Number(snapshot.gridCenterPrice ?? snapshot.centerPrice);
501
+ const amaCenterPrice = Number(snapshot.amaCenterPrice);
502
+ const gridPriceOffsetPct = Number(snapshot.gridPriceOffsetPct);
503
+ const normalized = normalizePersistedAmaSlopeDiagnostics({
504
+ amaSlopePercentMode: snapshot.amaSlopePercentMode,
505
+ amaSlope: snapshot.amaSlope,
506
+ gridRangeScalingAmaSlope: snapshot.gridRangeScalingAmaSlope,
507
+ amaSlopeDeltaPercent: snapshot.amaSlopeDeltaPercent,
508
+ amaSlopeThresholdPercent: snapshot.amaSlopeThresholdPercent,
509
+ }, lookbackBars);
510
+ const amaSlope = normalized?.amaSlope ?? null;
511
+ const gridRangeScalingAmaSlope = normalized?.gridRangeScalingAmaSlope ?? amaSlope;
512
+ return {
513
+ gridCenterPrice: Number.isFinite(gridCenterPrice) && gridCenterPrice > 0 ? gridCenterPrice : null,
514
+ centerPrice: Number.isFinite(gridCenterPrice) && gridCenterPrice > 0 ? gridCenterPrice : null,
515
+ amaCenterPrice: Number.isFinite(amaCenterPrice) && amaCenterPrice > 0 ? amaCenterPrice : null,
516
+ amaSlope,
517
+ gridRangeScalingAmaSlope,
518
+ amaSlopeDeltaPercent: normalized?.amaSlopeDeltaPercent ?? null,
519
+ amaSlopeThresholdPercent: normalized?.amaSlopeThresholdPercent ?? null,
520
+ amaSlopePercentMode: AMA_SLOPE_PERCENT_MODE_PER_BAR,
521
+ gridPriceOffsetPct: Number.isFinite(gridPriceOffsetPct) ? gridPriceOffsetPct : null,
522
+ lastGridResetAt: snapshot.lastGridResetAt,
523
+ lastGridResetSource: snapshot.lastGridResetSource,
524
+ };
525
+ }
526
+ _computeDynamicWeights(params) {
527
+ const { analysisCandles, closes, amaValues, amaWarmupBars, lookbackBars, botAma, weightVariance, amaPrice, nowIso, cfg, bot, ctx, deps, atrPeriod } = params;
528
+ const clipPercentile = cfg.clipPercentile ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_CLIP_PERCENTILE;
529
+ const nz = cfg.amaSlope?.neutralZonePct ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_NEUTRAL_ZONE_PCT;
530
+ const amaMaxS = cfg.amaSlope?.maxSlopePct ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT;
531
+ const kalMaxS = cfg.kalmanSlope?.maxSlopePct
532
+ ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_MAX_SLOPE_PCT;
533
+ const mo = cfg.maxSlopeOffset ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_ASYMMETRIC_OFFSET_CLAMP;
534
+ const volatilityClamp = normalizeMaxVolatilityOffset(cfg.maxVolatilityOffset);
535
+ const volatilityThreshold = normalizeVolatilityThreshold(cfg.volatilityThreshold);
536
+ const volatilityExponent = cfg.volatilityExponent ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_VOLATILITY_EXPONENT;
537
+ const volatilityScaleX = cfg.volatilityScaleX ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_VOLATILITY_SCALE_X_DEFAULT;
538
+ // Compute separate clip thresholds for AMA (slopes) and Kalman (velocities)
539
+ let amaClipThreshold = Infinity;
540
+ let kalClipThreshold = Infinity;
541
+ const amaSlopeReadyBars = Math.ceil(botAma.erPeriod) + lookbackBars;
542
+ if (clipPercentile > 0 && amaValues.length > amaSlopeReadyBars) {
543
+ // AMA clip threshold from slope distribution — skip the ER window,
544
+ // but do not require the full convergence-retention window.
545
+ const amaSlopes = [];
546
+ for (let i = amaSlopeReadyBars; i < amaValues.length; i++) {
547
+ const last = amaValues[i];
548
+ const past = amaValues[i - lookbackBars];
549
+ const slopePct = computeAverageAmaSlopePct(last, past, lookbackBars);
550
+ if (Number.isFinite(slopePct))
551
+ amaSlopes.push(Math.abs(slopePct));
552
+ }
553
+ if (amaSlopes.length > 0) {
554
+ const sorted = amaSlopes.sort((a, b) => a - b);
555
+ const idx = Math.min(Math.floor((100 - clipPercentile) / 100 * sorted.length), sorted.length - 1);
556
+ amaClipThreshold = sorted[idx];
557
+ }
558
+ }
559
+ const slopeCfg = {
560
+ ...(cfg.amaSlope || {}),
561
+ erPeriod: botAma.erPeriod,
562
+ slowPeriod: botAma.slowPeriod,
563
+ fastPeriod: botAma.fastPeriod,
564
+ maxSlopeOffset: cfg.maxSlopeOffset,
565
+ maxVolatilityOffset: volatilityClamp,
566
+ volatilityExponent,
567
+ volatilityScaleX,
568
+ volatilityThreshold,
569
+ neutralZonePct: nz,
570
+ clipPercentile,
571
+ clipThreshold: amaClipThreshold,
572
+ };
573
+ const slopeResult = computeAmaSlopeWeights(amaValues, weightVariance, slopeCfg);
574
+ // Kalman filter computation - collect per-bar results in single pass
575
+ const kalman = new KalmanTrendAnalyzer({
576
+ rNoise: cfg.kalman?.rNoise ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_R_NOISE_DEFAULT,
577
+ qTactical: cfg.kalman?.qTactical ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_Q_TACTICAL_DEFAULT,
578
+ qModal: cfg.kalman?.qModal ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_Q_MODAL_DEFAULT,
579
+ warmupBars: cfg.kalman?.warmupBars ?? 20,
580
+ });
581
+ const kalmanHistory = [];
582
+ for (const price of closes) {
583
+ const kr = kalman.update(price);
584
+ kalmanHistory.push(kr);
585
+ }
586
+ const kalmanResult = kalmanHistory[kalmanHistory.length - 1];
587
+ const kalmanWarmupBars = kalman.warmupBars ?? 20;
588
+ // Regime gate (Hurst + PE bilinear multiplier)
589
+ const regimeSensitivity = cfg.regimeSensitivity ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_REGIME_SENSITIVITY;
590
+ const absoluteThreshold = cfg.absoluteThreshold ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_ABSOLUTE_THRESHOLD_DEFAULT;
591
+ let regimeResult = null;
592
+ let regimeMultiplier = 1.0;
593
+ const regimeMultipliers = new Array(closes.length).fill(1.0);
594
+ if (regimeSensitivity > 0) {
595
+ regimeResult = computeRegimeMultiplier(closes, {
596
+ regimeSensitivity,
597
+ regimeTable: cfg.regimeTable,
598
+ hurstZoneBand: cfg.hurstZoneBand,
599
+ peNodes: cfg.peNodes,
600
+ });
601
+ regimeMultiplier = regimeResult.isReady && Math.abs(regimeResult.multiplier - 1.0) >= absoluteThreshold
602
+ ? regimeResult.multiplier
603
+ : 1.0;
604
+ if (Array.isArray(regimeResult.series) && regimeResult.series.length === closes.length) {
605
+ for (let i = 0; i < closes.length; i++) {
606
+ const rawMult = regimeResult.series[i];
607
+ regimeMultipliers[i] = Math.abs(rawMult - 1.0) >= absoluteThreshold ? rawMult : 1.0;
608
+ }
609
+ }
610
+ }
611
+ const alpha = cfg.alpha ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_ALPHA;
612
+ const dw = cfg.dw ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_DW;
613
+ const gain = cfg.gain ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_GAIN;
614
+ const kalmanSmoothPct = cfg.kalmanSmoothPct ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_SMOOTH_PCT_DEFAULT;
615
+ const kalmanDispScaleMult = cfg.kalmanDispScaleMult ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_DISP_SCALE_MULT_DEFAULT;
616
+ const kalmanDispThresholdMult = cfg.kalmanDispThresholdMult ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_DISP_THRESHOLD_MULT_DEFAULT;
617
+ const kalmanSmoothSpanPct = cfg.kalmanSmoothSpanPct ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_KALMAN_SMOOTH_SPAN_PCT_DEFAULT;
618
+ const signalConfirmBars = cfg.signalConfirmBars ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_SIGNAL_CONFIRM_BARS_DEFAULT;
619
+ const minOutputThreshold = cfg.minOutputThreshold
620
+ ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_ASYMMETRIC_TREND_THRESHOLD;
621
+ const dispScaleMinPct = cfg.dispScaleMinPct ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_DISP_SCALE_MIN_PCT;
622
+ const hasDirectionalOffset = mo > 0;
623
+ const useAmaBlend = hasDirectionalOffset && alpha !== 0;
624
+ const useKalmanBlend = hasDirectionalOffset && alpha !== 1;
625
+ const useNeutralZone = nz > 0;
626
+ const useClipThreshold = clipPercentile > 0;
627
+ const zeroOutputThreshold = minOutputThreshold === 0;
628
+ let kalmanSmoothedVelocityPct = new Array(kalmanHistory.length).fill(null);
629
+ let amaOffsets = new Array(closes.length).fill(0);
630
+ let kalmanOffsets = new Array(closes.length).fill(0);
631
+ if (useKalmanBlend) {
632
+ kalmanSmoothedVelocityPct = buildKalmanVelocitySeries(kalmanHistory, {
633
+ kalmanSmoothPct,
634
+ kalmanDispScaleMult,
635
+ kalmanDispThresholdMult,
636
+ kalmanSmoothSpanPct,
637
+ });
638
+ kalClipThreshold = useClipThreshold
639
+ ? computeAbsolutePercentileThreshold(kalmanSmoothedVelocityPct.slice(kalmanWarmupBars), clipPercentile, Infinity)
640
+ : Infinity;
641
+ for (let i = 0; i < kalmanHistory.length; i++) {
642
+ const kr = kalmanHistory[i];
643
+ const vp = kalmanSmoothedVelocityPct[i];
644
+ if (!kr.isReady || vp == null || kr.displacementRawPct == null)
645
+ continue;
646
+ const dp = kr.displacementRawPct;
647
+ const clippedV = Math.max(-kalClipThreshold, Math.min(kalClipThreshold, vp));
648
+ if (useNeutralZone && Math.abs(clippedV) < nz)
649
+ continue;
650
+ const dispScale = Math.max(1e-6, dispScaleMinPct);
651
+ const dispConf = Math.min(Math.abs(dp) / dispScale, 1.0);
652
+ const momAlign = Math.max(0, (clippedV * dp) / (Math.abs(clippedV) * Math.abs(dp) + 1e-10));
653
+ const composite = clippedV * (1 - dw + dw * dispConf * momAlign);
654
+ kalmanOffsets[i] = Math.max(-mo, Math.min(mo, (composite / kalMaxS) * mo));
655
+ }
656
+ }
657
+ if (useAmaBlend) {
658
+ for (let i = 0; i < closes.length; i++) {
659
+ if (!slopeResult.isReady || i < amaSlopeReadyBars)
660
+ continue;
661
+ const last = amaValues[i];
662
+ const past = amaValues[i - lookbackBars];
663
+ if (!Number.isFinite(last) || !Number.isFinite(past) || past === 0)
664
+ continue;
665
+ const sp = computeAverageAmaSlopePct(last, past, lookbackBars);
666
+ if (!Number.isFinite(sp))
667
+ continue;
668
+ const csp = Math.max(-amaClipThreshold, Math.min(amaClipThreshold, sp));
669
+ amaOffsets[i] = (!useNeutralZone || Math.abs(csp) >= nz)
670
+ ? Math.max(-mo, Math.min(mo, (csp / amaMaxS) * mo))
671
+ : 0;
672
+ }
673
+ }
674
+ const offsetClamp = cfg.maxSlopeOffset ?? MARKET_ADAPTER.DYNAMIC_WEIGHT_ASYMMETRIC_OFFSET_CLAMP;
675
+ const channelNorm = Math.max(Math.abs(offsetClamp), 1e-9);
676
+ const outputThreshold = minOutputThreshold;
677
+ const outputThresholdIsZero = zeroOutputThreshold;
678
+ const combinedOffSeries = new Array(closes.length).fill(0);
679
+ const gatedOffSeries = new Array(closes.length).fill(0);
680
+ for (let i = 0; i < closes.length; i++) {
681
+ let blendedOff;
682
+ if (useAmaBlend && useKalmanBlend) {
683
+ blendedOff = (alpha * (amaOffsets[i] / channelNorm) + (1 - alpha) * (kalmanOffsets[i] / channelNorm));
684
+ }
685
+ else if (useAmaBlend) {
686
+ blendedOff = amaOffsets[i] / channelNorm;
687
+ }
688
+ else if (useKalmanBlend) {
689
+ blendedOff = kalmanOffsets[i] / channelNorm;
690
+ }
691
+ else {
692
+ blendedOff = 0;
693
+ }
694
+ const regimeAdjusted = blendedOff * regimeMultipliers[i];
695
+ const gatedOff = outputThresholdIsZero
696
+ ? regimeAdjusted
697
+ : (Math.abs(regimeAdjusted) < outputThreshold ? 0 : regimeAdjusted);
698
+ const off = Math.max(-offsetClamp, Math.min(offsetClamp, gatedOff * gain));
699
+ gatedOffSeries[i] = gatedOff;
700
+ combinedOffSeries[i] = roundTo(off, 1000);
701
+ }
702
+ const confirmBars = Math.max(0, Math.min(5, Math.round(signalConfirmBars)));
703
+ let echoedOffSeries = new Array(closes.length).fill(0);
704
+ let echoedGatedOffSeries = new Array(closes.length).fill(0);
705
+ if (confirmBars === 0) {
706
+ echoedOffSeries = combinedOffSeries;
707
+ echoedGatedOffSeries = gatedOffSeries;
708
+ }
709
+ else {
710
+ let latchedSign = 0;
711
+ let pendingSign = 0;
712
+ let pendingCount = 0;
713
+ let latchedOff = 0;
714
+ let latchedGatedOff = 0;
715
+ for (let i = 0; i < closes.length; i++) {
716
+ const raw = combinedOffSeries[i];
717
+ const sign = raw > 0 ? 1 : raw < 0 ? -1 : 0;
718
+ if (sign === latchedSign) {
719
+ pendingSign = 0;
720
+ pendingCount = 0;
721
+ latchedOff = raw;
722
+ latchedGatedOff = gatedOffSeries[i];
723
+ }
724
+ else {
725
+ if (pendingSign !== sign) {
726
+ pendingSign = sign;
727
+ pendingCount = 1;
728
+ }
729
+ else {
730
+ pendingCount++;
731
+ }
732
+ if (pendingCount >= confirmBars) {
733
+ latchedSign = sign;
734
+ pendingSign = 0;
735
+ pendingCount = 0;
736
+ latchedOff = raw;
737
+ latchedGatedOff = gatedOffSeries[i];
738
+ }
739
+ }
740
+ echoedOffSeries[i] = latchedOff;
741
+ echoedGatedOffSeries[i] = latchedGatedOff;
742
+ }
743
+ }
744
+ const rawFinalOff = combinedOffSeries[combinedOffSeries.length - 1] ?? 0;
745
+ const rawFinalPreGainOff = gatedOffSeries[gatedOffSeries.length - 1] ?? 0;
746
+ const finalPreGainOff = echoedGatedOffSeries[echoedGatedOffSeries.length - 1] ?? rawFinalPreGainOff;
747
+ const finalOff = echoedOffSeries[echoedOffSeries.length - 1] ?? rawFinalOff;
748
+ const lastAmaOffset = useAmaBlend ? (amaOffsets[amaOffsets.length - 1] ?? 0) : 0;
749
+ const amaSlopeGated = slopeResult.isReady
750
+ ? roundTo(alpha * (lastAmaOffset / channelNorm) * gain * regimeMultiplier, 1000)
751
+ : 0;
752
+ const amaSlope = {
753
+ trend: slopeResult.trend,
754
+ confidence: slopeResult.confidence,
755
+ slopePct: slopeResult.slopePct,
756
+ slopeOffset: slopeResult.slopeOffset,
757
+ rawSlopeOffset: slopeResult.rawSlopeOffset,
758
+ amaSlopeGated,
759
+ regimeMultiplier,
760
+ symmetricDelta: slopeResult.symmetricDelta,
761
+ weightVariance,
762
+ isReady: slopeResult.isReady,
763
+ kalmanReady: kalmanResult?.isReady ?? false,
764
+ alpha,
765
+ dw,
766
+ gain,
767
+ atrPeriod: atrPeriod,
768
+ maxSlopeOffset: mo,
769
+ amaSlope: {
770
+ maxSlopePct: amaMaxS,
771
+ },
772
+ kalmanSlope: {
773
+ maxSlopePct: kalMaxS,
774
+ },
775
+ maxVolatilityOffset: volatilityClamp,
776
+ kalmanSmoothPct,
777
+ kalmanDispScaleMult,
778
+ kalmanDispThresholdMult,
779
+ kalmanSmoothSpanPct,
780
+ signalConfirmBars,
781
+ };
782
+ const staticSell = bot.weightDistribution.sell;
783
+ const staticBuy = bot.weightDistribution.buy;
784
+ const MIN_W = MARKET_ADAPTER.DYNAMIC_WEIGHT_MIN_WEIGHT;
785
+ const MAX_W = MARKET_ADAPTER.DYNAMIC_WEIGHT_MAX_WEIGHT;
786
+ const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
787
+ const belowMinOutputThreshold = Math.abs(finalPreGainOff) < outputThreshold;
788
+ const volPenalty = slopeResult.isReady ? (slopeResult.symmetricDelta ?? 0) : 0;
789
+ const trendOff = belowMinOutputThreshold ? 0 : finalOff;
790
+ const effectiveSell = roundTo(clamp(staticSell - trendOff + volPenalty, MIN_W, MAX_W), 100);
791
+ const effectiveBuy = roundTo(clamp(staticBuy + trendOff + volPenalty, MIN_W, MAX_W), 100);
792
+ const weights = {
793
+ sell: effectiveSell,
794
+ buy: effectiveBuy,
795
+ profile: !slopeResult.isReady ? 'static'
796
+ : trendOff !== 0 ? 'slope'
797
+ : volPenalty !== 0 ? 'volatility'
798
+ : 'flat',
799
+ meta: {
800
+ source: 'dynamic_weight',
801
+ staticSell,
802
+ staticBuy,
803
+ trend: slopeResult.trend,
804
+ confidence: slopeResult.confidence,
805
+ slopePct: slopeResult.slopePct,
806
+ slopeOffset: slopeResult.slopeOffset,
807
+ rawSlopeOffset: slopeResult.rawSlopeOffset,
808
+ amaSlopeGated,
809
+ regimeMultiplier,
810
+ regimeSensitivity,
811
+ absoluteThreshold,
812
+ volatilityPenalty: volPenalty,
813
+ alpha,
814
+ dw,
815
+ gain,
816
+ atrPeriod: atrPeriod,
817
+ maxSlopeOffset: mo,
818
+ maxAsymmetryFactor: (cfg.asymmetricBounds?.maxAsymmetryFactor != null)
819
+ ? cfg.asymmetricBounds.maxAsymmetryFactor
820
+ : null,
821
+ clipPercentile,
822
+ neutralZonePct: nz,
823
+ volatilityThreshold,
824
+ volatilityExponent,
825
+ volatilityScaleX,
826
+ amaSlope: {
827
+ maxSlopePct: amaMaxS,
828
+ },
829
+ kalmanSlope: {
830
+ maxSlopePct: kalMaxS,
831
+ },
832
+ maxVolatilityOffset: volatilityClamp,
833
+ kalmanSmoothPct,
834
+ kalmanDispScaleMult,
835
+ kalmanDispThresholdMult,
836
+ kalmanSmoothSpanPct,
837
+ signalConfirmBars,
838
+ amaChannelContribution: amaSlopeGated,
839
+ rawFinalOffset: rawFinalOff,
840
+ trendOffset: trendOff,
841
+ finalOffset: finalOff,
842
+ minOutputThreshold,
843
+ outputThreshold,
844
+ belowMinOutputThreshold,
845
+ kalmanReady: kalmanResult?.isReady ?? false,
846
+ isReady: slopeResult.isReady,
847
+ },
848
+ };
849
+ const dynamicWeightsPayload = {
850
+ effectiveWeights: { sell: effectiveSell, buy: effectiveBuy },
851
+ baseWeights: { sell: staticSell, buy: staticBuy },
852
+ slopeOffset: slopeResult.slopeOffset,
853
+ rawSlopeOffset: slopeResult.rawSlopeOffset,
854
+ amaSlopeGated,
855
+ volatilityPenalty: volPenalty,
856
+ finalOffset: finalOff,
857
+ alpha,
858
+ dw,
859
+ gain,
860
+ atrPeriod: atrPeriod,
861
+ maxSlopeOffset: mo,
862
+ amaSlope: {
863
+ maxSlopePct: amaMaxS,
864
+ },
865
+ kalmanSlope: {
866
+ maxSlopePct: kalMaxS,
867
+ },
868
+ clipPercentile,
869
+ neutralZonePct: nz,
870
+ volatilityThreshold,
871
+ volatilityExponent,
872
+ volatilityScaleX,
873
+ maxVolatilityOffset: volatilityClamp,
874
+ kalmanSmoothPct,
875
+ kalmanDispScaleMult,
876
+ kalmanDispThresholdMult,
877
+ kalmanSmoothSpanPct,
878
+ signalConfirmBars,
879
+ rawFinalOffset: rawFinalOff,
880
+ maxAsymmetryFactor: (cfg.asymmetricBounds?.maxAsymmetryFactor != null)
881
+ ? cfg.asymmetricBounds.maxAsymmetryFactor
882
+ : null,
883
+ amaChannelContribution: amaSlopeGated,
884
+ trend: slopeResult.trend,
885
+ confidence: slopeResult.confidence,
886
+ slopePct: slopeResult.slopePct,
887
+ regimeMultiplier,
888
+ regimeSensitivity,
889
+ absoluteThreshold,
890
+ minOutputThreshold,
891
+ outputThreshold,
892
+ belowMinOutputThreshold,
893
+ kalmanReady: kalmanResult?.isReady ?? false,
894
+ ...(regimeResult ? {
895
+ hurst: regimeResult.hurst,
896
+ pe: regimeResult.pe,
897
+ hurstRegime: regimeResult.hurstRegime,
898
+ peRegime: regimeResult.peRegime,
899
+ regimeReady: regimeResult.isReady,
900
+ } : {}),
901
+ isReady: slopeResult.isReady && (!belowMinOutputThreshold || volPenalty !== 0),
902
+ updatedAt: nowIso,
903
+ };
904
+ return { weights, dynamicWeightsPayload, slopeResult, regimeResult, kalmanResult, amaSlope };
905
+ }
906
+ async processBot(bot, state, cfg, contextCache, hooks = {}) {
907
+ const deps = this.deps;
908
+ const isDryRun = !!hooks.isDryRun;
909
+ const forceWhitelistAll = !!hooks.forceWhitelistAll;
910
+ let dryRunMessages = [];
911
+ if ((!bot.assetA && !bot.assetAId) || (!bot.assetB && !bot.assetBId)) {
912
+ return { ok: false, reason: 'missing asset pair' };
913
+ }
914
+ if (hasNumericStartPrice(bot?.startPrice)) {
915
+ const nowIso = new Date().toISOString();
916
+ const thresholdPercent = typeof deps.calculateBotThreshold === 'function'
917
+ ? deps.calculateBotThreshold(cfg)
918
+ : null;
919
+ state.bots = state.bots || {};
920
+ state.bots[bot.botKey] = this.buildDefaultBotState(bot, {
921
+ startPrice: bot.startPrice,
922
+ priceMode: 'fixed',
923
+ lastCycleSource: 'fixed-start-price',
924
+ lastCycleAt: nowIso,
925
+ lastTriggerSuppressedReason: 'fixed_start_price',
926
+ });
927
+ return this.buildDefaultResult(bot, {
928
+ dryRunMessages,
929
+ source: 'fixed-start-price',
930
+ thresholdPercent,
931
+ triggerSuppressedReason: 'fixed_start_price',
932
+ });
933
+ }
934
+ const contextSignature = this.buildBotContextSignature(bot);
935
+ const cached = contextCache.get(bot.botKey);
936
+ const cachedCtx = cached && typeof cached === 'object' && cached.ctx ? cached.ctx : cached;
937
+ const cachedSignature = cached && typeof cached === 'object' && cached.signature ? cached.signature : null;
938
+ let ctx = cachedCtx;
939
+ if (!ctx || cachedSignature !== contextSignature) {
940
+ ctx = await deps.resolveBotContext(bot);
941
+ contextCache.set(bot.botKey, {
942
+ signature: contextSignature,
943
+ ctx,
944
+ });
945
+ }
946
+ const botAma = deps.resolveAmaForBot(bot, ctx, cfg);
947
+ if (!botAma.enabled) {
948
+ return { ok: false, reason: 'ama disabled' };
949
+ }
950
+ const lookbackBars = normalizeAmaSlopeLookbackBars(cfg.amaSlope?.lookbackBars);
951
+ const filePath = deps.candleFileForBot(bot.botKey, cfg.intervalSeconds);
952
+ const existing = deps.loadJson(filePath, null);
953
+ const existingMeta = existing?.meta && typeof existing.meta === 'object' ? existing.meta : {};
954
+ let existingCandles = Array.isArray(existing?.candles) ? existing.candles : [];
955
+ const existingMarketSource = normalizeMarketSource(existingMeta.marketSource);
956
+ const marketSource = resolveMarketSourceForBot(bot) || 'pool';
957
+ const isBookSource = marketSource === 'book';
958
+ const kibanaRequestTimeoutMs = Number.isFinite(cfg.kibanaRequestTimeoutMs) && cfg.kibanaRequestTimeoutMs > 0
959
+ ? cfg.kibanaRequestTimeoutMs
960
+ : MARKET_ADAPTER.KIBANA_REQUEST_TIMEOUT_MS;
961
+ const fetchKibanaCandles = async (options = {}) => {
962
+ const kibanaOptions = {
963
+ timeout: kibanaRequestTimeoutMs,
964
+ ...options,
965
+ };
966
+ if (isBookSource) {
967
+ if (!deps.kibanaMarketSource || typeof deps.kibanaMarketSource.getMarketCandles !== 'function') {
968
+ throw new Error('orderbook candle source unavailable');
969
+ }
970
+ return deps.kibanaMarketSource.getMarketCandles(ctx.assetA, ctx.assetB, kibanaOptions);
971
+ }
972
+ if (!deps.kibanaSource || typeof deps.kibanaSource.getLpCandlesForPool !== 'function') {
973
+ throw new Error('liquidity pool candle source unavailable');
974
+ }
975
+ return deps.kibanaSource.getLpCandlesForPool(ctx.poolId, ctx.assetA, ctx.assetB, kibanaOptions);
976
+ };
977
+ const staleTailVerifiedRangeFromMeta = () => {
978
+ const startTs = Number(existingMeta.staleTailVerifiedStartTs);
979
+ const endTs = Number(existingMeta.staleTailVerifiedEndTs);
980
+ if (Number.isFinite(startTs) && Number.isFinite(endTs)) {
981
+ return { startTs, endTs };
982
+ }
983
+ return {};
984
+ };
985
+ const verifyAndPruneStaleTail = async (candles, threshold, verifiedRange = {}) => {
986
+ if (typeof deps.pruneStaleTail !== 'function')
987
+ return { candles };
988
+ const pruned = deps.pruneStaleTail(candles, threshold);
989
+ if (pruned.length === candles.length || candles.length === 0) {
990
+ return { candles: pruned };
991
+ }
992
+ // Use the shared detector to find the tail range without re-sorting
993
+ const detected = typeof deps.detectStaleTail === 'function'
994
+ ? deps.detectStaleTail(candles, threshold)
995
+ : null;
996
+ if (!detected)
997
+ return { candles: pruned };
998
+ const tailStartTs = detected.sorted[detected.sorted.length - detected.runLength][0];
999
+ const tailEndTs = detected.sorted[detected.sorted.length - 1][0];
1000
+ // Skip Kibana verification if this tail range was already confirmed flat
1001
+ // on a previous cycle (the current tail is contained within a verified range).
1002
+ const { startTs: vStart, endTs: vEnd } = verifiedRange;
1003
+ if (Number.isFinite(vStart) && (Number.isFinite(vEnd) || vEnd === Number.POSITIVE_INFINITY)
1004
+ && tailStartTs >= vStart && tailEndTs <= vEnd) {
1005
+ return { candles, keptStaleTailStartTs: tailStartTs, keptStaleTailEndTs: tailEndTs };
1006
+ }
1007
+ // Verify with Kibana: did the market actually trade during this period?
1008
+ try {
1009
+ const kibanaCandles = await deps.withRetries(() => fetchKibanaCandles({
1010
+ intervalSeconds: cfg.intervalSeconds,
1011
+ consolidateByTimestamp: true,
1012
+ fillGapsToRequestedRange: false,
1013
+ apiKey: null,
1014
+ timeRange: {
1015
+ gte: new Date(tailStartTs).toISOString(),
1016
+ lte: new Date(tailEndTs).toISOString(),
1017
+ },
1018
+ }), cfg.sourceRetries, cfg.retryDelayMs, 'kibana stale-tail verification failed');
1019
+ if (Array.isArray(kibanaCandles) && kibanaCandles.length > 0) {
1020
+ const hasKibanaActivity = kibanaCandles.some((c) => Array.isArray(c) && Number(c[5] || 0) > 0);
1021
+ if (!hasKibanaActivity) {
1022
+ // Kibana confirms flat/inactive: our data is genuine → keep it
1023
+ return { candles, keptStaleTailStartTs: tailStartTs, keptStaleTailEndTs: tailEndTs };
1024
+ }
1025
+ // Kibana shows real trades: our gap-fill is stale → prune it
1026
+ }
1027
+ // Kibana has no data for this period (or query failed) → prune
1028
+ return { candles: pruned };
1029
+ }
1030
+ catch (_) {
1031
+ // Kibana query failed: prune (existing behavior)
1032
+ return { candles: pruned };
1033
+ }
1034
+ };
1035
+ const applyStaleTailVerificationMeta = (verified) => {
1036
+ if (Number.isFinite(verified?.keptStaleTailStartTs) && Number.isFinite(verified?.keptStaleTailEndTs)) {
1037
+ existingMeta.staleTailVerifiedStartTs = verified.keptStaleTailStartTs;
1038
+ existingMeta.staleTailVerifiedEndTs = verified.keptStaleTailEndTs;
1039
+ return;
1040
+ }
1041
+ existingMeta.staleTailVerifiedStartTs = null;
1042
+ existingMeta.staleTailVerifiedEndTs = null;
1043
+ };
1044
+ // Prune stale trailing candles from a previous run before any processing.
1045
+ // Verifies with Kibana first to avoid removing genuinely flat market periods.
1046
+ if (existingCandles.length > 0) {
1047
+ const staleThreshold = Number.isFinite(cfg.staleTailThreshold)
1048
+ ? cfg.staleTailThreshold
1049
+ : MARKET_ADAPTER.STALE_TAIL_THRESHOLD_CANDLES;
1050
+ const verified = await verifyAndPruneStaleTail(existingCandles, staleThreshold, staleTailVerifiedRangeFromMeta());
1051
+ existingCandles = verified.candles;
1052
+ applyStaleTailVerificationMeta(verified);
1053
+ }
1054
+ const hasStoredPoolContext = existingMeta.pool != null && String(existingMeta.pool).trim() !== '';
1055
+ const sourceMismatch = (existingMarketSource && existingMarketSource !== marketSource)
1056
+ || (marketSource === 'book' && hasStoredPoolContext)
1057
+ || (marketSource === 'pool' && existingMarketSource === 'book');
1058
+ if (sourceMismatch) {
1059
+ existingCandles = [];
1060
+ existingMeta.nativeRecentTradeSequences = [];
1061
+ existingMeta.nativeLastTradeTs = null;
1062
+ existingMeta.staleTailVerifiedStartTs = null;
1063
+ existingMeta.staleTailVerifiedEndTs = null;
1064
+ }
1065
+ const needBootstrap = existingCandles.length === 0;
1066
+ const amaWarmupBars = getAmaWarmupBars(botAma.erPeriod, botAma.slowPeriod, lookbackBars, botAma.fastPeriod, botAma.erSmoothPeriod ?? 0);
1067
+ const analysisKeepCount = amaWarmupBars + 1;
1068
+ // Retain one extra raw candle so the closed-candle analysis window still keeps a
1069
+ // full warmup/history set when the newest bucket is the current in-progress bar.
1070
+ const rawKeepCount = analysisKeepCount + 1;
1071
+ const nowIso = new Date().toISOString();
1072
+ const botThreshold = deps.calculateBotThreshold(cfg);
1073
+ if (!Number.isFinite(botThreshold) || botThreshold <= 0) {
1074
+ throw new Error(`deltaThresholdPercent missing/invalid for bot ${bot.name}`);
1075
+ }
1076
+ const loadCandles = async () => {
1077
+ let nextCandles = existingCandles;
1078
+ let sourceLabel = 'native-incremental';
1079
+ let kibanaBootstrapEmpty = false;
1080
+ let nativeRecentTradeSequences = Array.isArray(existingMeta.nativeRecentTradeSequences)
1081
+ ? existingMeta.nativeRecentTradeSequences.slice()
1082
+ : [];
1083
+ let nativeLastTradeTs = Number.isFinite(existingMeta.nativeLastTradeTs)
1084
+ ? existingMeta.nativeLastTradeTs
1085
+ : null;
1086
+ let nativeOverlapCount = null;
1087
+ let nativePagesFetched = null;
1088
+ // Policy:
1089
+ // - Trust native "no trades" directly for bounded runs up to this threshold.
1090
+ // - Only escalate to Kibana verification once a no-trade run exceeds it.
1091
+ // - Reuse the same threshold when deciding whether an old internal hole is
1092
+ // small enough to synthesize after Kibana also returns no candles.
1093
+ const trustedNoTradeGapThresholdCandles = this.getTrustedNoTradeGapThresholdCandles(cfg);
1094
+ const logGapRepairEvent = (message, level = 'info') => {
1095
+ if (typeof deps.logger?.log === 'function') {
1096
+ deps.logger.log(message, level);
1097
+ }
1098
+ else if (level === 'warn' && typeof deps.logger?.warn === 'function') {
1099
+ deps.logger.warn(message);
1100
+ }
1101
+ else if (level === 'info' && typeof deps.logger?.info === 'function') {
1102
+ deps.logger.info(message);
1103
+ }
1104
+ else if (typeof deps.logger?.warn === 'function') {
1105
+ deps.logger.warn(message);
1106
+ }
1107
+ };
1108
+ const hasKibanaSource = isBookSource
1109
+ ? deps.kibanaMarketSource && typeof deps.kibanaMarketSource.getMarketCandles === 'function'
1110
+ : deps.kibanaSource && typeof deps.kibanaSource.getLpCandlesForPool === 'function';
1111
+ const verifyAndFillLongSilence = async (candles, lastTs, latestClosedBucketTs, sourceLabel, incomingCandles = []) => {
1112
+ const bucketMs = Number(cfg.intervalSeconds) * 1000;
1113
+ const silenceStartTs = Number(lastTs) + bucketMs;
1114
+ const earliestIncomingTs = (Array.isArray(incomingCandles) ? incomingCandles : [])
1115
+ .filter((c) => Array.isArray(c) && Number.isFinite(c[0]) && c[0] > lastTs)
1116
+ .sort((a, b) => a[0] - b[0])[0]?.[0];
1117
+ const silenceEndTs = Number.isFinite(earliestIncomingTs) && earliestIncomingTs > silenceStartTs
1118
+ ? earliestIncomingTs - bucketMs
1119
+ : Number(latestClosedBucketTs);
1120
+ if (!hasKibanaSource || typeof deps.fillCandleGaps !== 'function') {
1121
+ return { candles, sourceLabel };
1122
+ }
1123
+ if (!Number.isFinite(bucketMs) || bucketMs <= 0 || !Number.isFinite(silenceStartTs) || !Number.isFinite(silenceEndTs) || silenceEndTs < silenceStartTs) {
1124
+ return { candles, sourceLabel };
1125
+ }
1126
+ try {
1127
+ const kibanaSilenceCandles = await deps.withRetries(() => fetchKibanaCandles({
1128
+ intervalSeconds: cfg.intervalSeconds,
1129
+ consolidateByTimestamp: true,
1130
+ fillGapsToRequestedRange: false,
1131
+ apiKey: null,
1132
+ timeRange: {
1133
+ gte: new Date(silenceStartTs).toISOString(),
1134
+ lte: new Date(silenceEndTs).toISOString(),
1135
+ },
1136
+ }), cfg.sourceRetries, cfg.retryDelayMs, 'kibana long-silence verification failed');
1137
+ const hasKibanaActivity = Array.isArray(kibanaSilenceCandles) && kibanaSilenceCandles.some((c) => Array.isArray(c) && Number(c[5] || 0) > 0);
1138
+ if (hasKibanaActivity) {
1139
+ return {
1140
+ candles: deps.mergeCandles(candles, kibanaSilenceCandles, {
1141
+ onCollision: (existingCandle, incomingCandle) => this.buildIncrementalCandleCollision(existingCandle, incomingCandle),
1142
+ }),
1143
+ sourceLabel: `${sourceLabel}+kibana-silence-activity`,
1144
+ };
1145
+ }
1146
+ const previousCandle = candles
1147
+ .filter((c) => Array.isArray(c) && c[0] === lastTs)
1148
+ .slice(-1)[0];
1149
+ const filledSilence = previousCandle
1150
+ ? deps.fillCandleGaps([previousCandle, ...(Array.isArray(incomingCandles) ? incomingCandles : [])], cfg.intervalSeconds, lastTs, silenceEndTs)
1151
+ .filter((c) => Array.isArray(c) && c[0] > lastTs)
1152
+ : [];
1153
+ if (filledSilence.length === 0)
1154
+ return { candles, sourceLabel };
1155
+ existingMeta.staleTailVerifiedStartTs = silenceStartTs;
1156
+ existingMeta.staleTailVerifiedEndTs = silenceEndTs;
1157
+ const lastClose = previousCandle?.[4];
1158
+ const message = `[market_adapter] ${bot.botKey}: verified no trades from ${new Date(silenceStartTs).toISOString()} to ${new Date(silenceEndTs).toISOString()}; carrying flat close ${Number.isFinite(Number(lastClose)) ? Number(lastClose) : 'n/a'}`;
1159
+ if (typeof deps.logger?.log === 'function') {
1160
+ deps.logger.log(message, 'info');
1161
+ }
1162
+ else if (typeof deps.logger?.info === 'function') {
1163
+ deps.logger.info(message);
1164
+ }
1165
+ else if (typeof deps.logger?.warn === 'function') {
1166
+ deps.logger.warn(message);
1167
+ }
1168
+ return {
1169
+ candles: deps.mergeCandles(candles, filledSilence),
1170
+ sourceLabel: `${sourceLabel}+verified-silence`,
1171
+ };
1172
+ }
1173
+ catch (_) {
1174
+ // Verification failed: preserve stale-data protection.
1175
+ return { candles, sourceLabel };
1176
+ }
1177
+ };
1178
+ const fillBoundedTrailingClosedGap = (candles, latestClosedBucketTs, nowMs, maxNativeGapFill) => {
1179
+ const bucketMs = Number(cfg.intervalSeconds) * 1000;
1180
+ const latestKnownTs = Array.isArray(candles) && candles.length > 0
1181
+ ? candles[candles.length - 1]?.[0]
1182
+ : null;
1183
+ const trailingGapBuckets = Number.isFinite(bucketMs) && bucketMs > 0
1184
+ && Number.isFinite(latestClosedBucketTs) && Number.isFinite(latestKnownTs)
1185
+ && latestClosedBucketTs > latestKnownTs
1186
+ ? Math.round((latestClosedBucketTs - latestKnownTs) / bucketMs)
1187
+ : 0;
1188
+ if (trailingGapBuckets <= 0 || trailingGapBuckets > maxNativeGapFill) {
1189
+ return candles;
1190
+ }
1191
+ return this.fillNativeIncrementalClosedGaps(candles, latestKnownTs, cfg.intervalSeconds, nowMs);
1192
+ };
1193
+ if (isBookSource) {
1194
+ nativeRecentTradeSequences = [];
1195
+ nativeLastTradeTs = null;
1196
+ nativeOverlapCount = null;
1197
+ nativePagesFetched = null;
1198
+ const bucketMs = Number(cfg.intervalSeconds) * 1000;
1199
+ const nowMs = this.getNowMs();
1200
+ if (needBootstrap) {
1201
+ // Bootstrap: Kibana first (deep history), native as fallback
1202
+ const kibanaLookbackHours = Math.max(cfg.bootstrapLookbackHours, analysisKeepCount * 2);
1203
+ let kibanaCandles = null;
1204
+ try {
1205
+ kibanaCandles = await deps.withRetries(() => fetchKibanaCandles({
1206
+ intervalSeconds: cfg.intervalSeconds,
1207
+ lookbackHours: kibanaLookbackHours,
1208
+ consolidateByTimestamp: true,
1209
+ fillGapsToRequestedRange: false,
1210
+ apiKey: null,
1211
+ }), cfg.sourceRetries, cfg.retryDelayMs, 'kibana orderbook bootstrap failed');
1212
+ }
1213
+ catch (_) {
1214
+ if (typeof deps.logger?.warn === 'function')
1215
+ deps.logger.warn(`[market_adapter] ${ctx.botKey}: kibana orderbook bootstrap failed`);
1216
+ }
1217
+ if (Array.isArray(kibanaCandles) && kibanaCandles.length > 0) {
1218
+ nextCandles = kibanaCandles;
1219
+ sourceLabel = 'kibana-book-bootstrap';
1220
+ }
1221
+ else {
1222
+ // Fall back to native
1223
+ const nativeLookbackHours = Math.max(Number(cfg.bootstrapLookbackHours) || 0, Number(cfg.nativeBackfillHours) || 0, (analysisKeepCount * Math.max(Number(cfg.intervalSeconds) || 3600, 3600)) / 3600);
1224
+ const nativeStartMs = Math.max(0, nowMs - (nativeLookbackHours * 3600 * 1000));
1225
+ let nativeCandles = [];
1226
+ try {
1227
+ if (typeof deps.fetchNativeMarketHistorySince === 'function') {
1228
+ nativeCandles = await deps.withRetries(() => deps.fetchNativeMarketHistorySince(ctx.assetA, ctx.assetB, nativeStartMs, nowMs, cfg.intervalSeconds, { fillCandleGaps: deps.fillCandleGaps }), cfg.sourceRetries, cfg.retryDelayMs, 'native market history bootstrap failed');
1229
+ }
1230
+ }
1231
+ catch (_) {
1232
+ if (typeof deps.logger?.warn === 'function')
1233
+ deps.logger.warn(`[market_adapter] ${ctx.botKey}: native market history bootstrap failed`);
1234
+ }
1235
+ if (Array.isArray(nativeCandles) && nativeCandles.length > 0) {
1236
+ nextCandles = nativeCandles;
1237
+ sourceLabel = 'native-book-bootstrap';
1238
+ }
1239
+ else {
1240
+ throw new Error('both kibana and native orderbook bootstrap failed');
1241
+ }
1242
+ }
1243
+ }
1244
+ else {
1245
+ // Incremental: native fetch
1246
+ const nativeLookbackHours = Math.max(Number(cfg.bootstrapLookbackHours) || 0, Number(cfg.nativeBackfillHours) || 0, (analysisKeepCount * Math.max(Number(cfg.intervalSeconds) || 3600, 3600)) / 3600);
1247
+ const lastTs = nextCandles[nextCandles.length - 1]?.[0] || 0;
1248
+ const nativeStartMs = Math.max(0, lastTs - bucketMs);
1249
+ let nativeCandles = [];
1250
+ try {
1251
+ if (typeof deps.fetchNativeMarketHistorySince === 'function') {
1252
+ nativeCandles = await deps.withRetries(() => deps.fetchNativeMarketHistorySince(ctx.assetA, ctx.assetB, nativeStartMs, nowMs, cfg.intervalSeconds, { fillCandleGaps: deps.fillCandleGaps }), cfg.sourceRetries, cfg.retryDelayMs, 'native market history fetch failed');
1253
+ }
1254
+ }
1255
+ catch (_) {
1256
+ if (typeof deps.logger?.warn === 'function')
1257
+ deps.logger.warn(`[market_adapter] ${ctx.botKey}: native market history fetch failed`);
1258
+ }
1259
+ if (Array.isArray(nativeCandles) && nativeCandles.length > 0) {
1260
+ nextCandles = deps.mergeCandles(nextCandles, nativeCandles);
1261
+ sourceLabel = 'native-book-history';
1262
+ }
1263
+ else {
1264
+ sourceLabel = 'cached-book';
1265
+ }
1266
+ const currentBucketStartMs = Math.floor(Number(nowMs) / bucketMs) * bucketMs;
1267
+ const latestClosedBucketTs = currentBucketStartMs - bucketMs;
1268
+ const earliestIncomingTs = nativeCandles.length > 0 ? nativeCandles[0][0] : null;
1269
+ const gapEndTs = Number.isFinite(earliestIncomingTs) && earliestIncomingTs > lastTs
1270
+ ? earliestIncomingTs
1271
+ : latestClosedBucketTs + bucketMs;
1272
+ const gapBuckets = Number.isFinite(gapEndTs) && gapEndTs > lastTs
1273
+ ? Math.round((gapEndTs - lastTs) / bucketMs) - 1
1274
+ : 0;
1275
+ const maxNativeGapFill = Number.isFinite(cfg.maxNativeGapFillCandles)
1276
+ ? cfg.maxNativeGapFillCandles
1277
+ : MARKET_ADAPTER.STALE_TAIL_THRESHOLD_CANDLES;
1278
+ if (gapBuckets > maxNativeGapFill) {
1279
+ const verifiedSilence = await verifyAndFillLongSilence(nextCandles, lastTs, latestClosedBucketTs, sourceLabel, nativeCandles);
1280
+ nextCandles = verifiedSilence.candles;
1281
+ sourceLabel = verifiedSilence.sourceLabel;
1282
+ nextCandles = fillBoundedTrailingClosedGap(nextCandles, latestClosedBucketTs, nowMs, maxNativeGapFill);
1283
+ }
1284
+ else {
1285
+ nextCandles = fillBoundedTrailingClosedGap(nextCandles, latestClosedBucketTs, nowMs, maxNativeGapFill);
1286
+ }
1287
+ }
1288
+ if (nextCandles.length > 0) {
1289
+ const staleThreshold = Number.isFinite(cfg.staleTailThreshold)
1290
+ ? cfg.staleTailThreshold
1291
+ : MARKET_ADAPTER.STALE_TAIL_THRESHOLD_CANDLES;
1292
+ const verified = await verifyAndPruneStaleTail(nextCandles, staleThreshold, staleTailVerifiedRangeFromMeta());
1293
+ nextCandles = verified.candles;
1294
+ applyStaleTailVerificationMeta(verified);
1295
+ }
1296
+ }
1297
+ else if (needBootstrap) {
1298
+ const lookbackHours = Math.max(cfg.bootstrapLookbackHours, analysisKeepCount * 2);
1299
+ // ── Step 1: Kibana first (deep history, handles large candle requirements) ──
1300
+ let kibanaCandles = null;
1301
+ try {
1302
+ kibanaCandles = await deps.withRetries(() => fetchKibanaCandles({
1303
+ intervalSeconds: cfg.intervalSeconds,
1304
+ lookbackHours,
1305
+ consolidateByTimestamp: true,
1306
+ fillGapsToRequestedRange: false,
1307
+ apiKey: null,
1308
+ }), cfg.sourceRetries, cfg.retryDelayMs, 'kibana bootstrap failed');
1309
+ }
1310
+ catch (_) {
1311
+ kibanaCandles = null;
1312
+ }
1313
+ if (Array.isArray(kibanaCandles) && kibanaCandles.length >= amaWarmupBars) {
1314
+ nextCandles = kibanaCandles;
1315
+ sourceLabel = 'kibana-bootstrap';
1316
+ }
1317
+ else {
1318
+ // ── Step 2: Kibana insufficient → fall back to native ──
1319
+ let nativeCandles = [];
1320
+ try {
1321
+ const sinceMs = Date.now() - (lookbackHours * 3600 * 1000);
1322
+ const fetchResult = await deps.withRetries(() => deps.fetchNativeTradesSince(ctx.poolId, sinceMs, cfg.pageLimit, cfg.maxPages), cfg.sourceRetries, cfg.retryDelayMs, 'native bootstrap failed');
1323
+ const trades = Array.isArray(fetchResult?.trades) ? fetchResult.trades : [];
1324
+ if (fetchResult?.truncated && typeof deps.logger?.warn === 'function') {
1325
+ deps.logger.warn(`[market_adapter] ${bot.botKey}: native bootstrap history truncated (exhausted ${cfg.maxPages} pages)`);
1326
+ }
1327
+ if (trades.length > 0) {
1328
+ nativeRecentTradeSequences = this.getNativeRecentTradeSequences(trades);
1329
+ const latestTradeTs = Math.max(...trades.map((t) => Number(t?.tsMs)).filter(Number.isFinite));
1330
+ if (Number.isFinite(latestTradeTs))
1331
+ nativeLastTradeTs = latestTradeTs;
1332
+ }
1333
+ nativeCandles = deps.tradesToCandles(trades, ctx.assetA, ctx.assetB, cfg.intervalSeconds);
1334
+ if (nativeCandles.length > 0 && typeof deps.fillCandleGaps === 'function') {
1335
+ const eTs = nativeCandles[0][0];
1336
+ const lTs = nativeCandles[nativeCandles.length - 1][0];
1337
+ nativeCandles = deps.fillCandleGaps(nativeCandles, cfg.intervalSeconds, eTs, lTs);
1338
+ }
1339
+ }
1340
+ catch (_) {
1341
+ nativeCandles = [];
1342
+ }
1343
+ if (Array.isArray(kibanaCandles) && kibanaCandles.length > 0) {
1344
+ if (nativeCandles.length > 0) {
1345
+ // Stitch: Kibana older + Native recent (no overlap; native wins)
1346
+ const nativeOldestTs = nativeCandles[0][0];
1347
+ const kbnOlder = kibanaCandles.filter((c) => Array.isArray(c) && c[0] < nativeOldestTs);
1348
+ const stitched = [...kbnOlder, ...nativeCandles]
1349
+ .sort((a, b) => a[0] - b[0]);
1350
+ if (stitched.length > 1 && typeof deps.fillCandleGaps === 'function') {
1351
+ const sTs = stitched[0][0];
1352
+ const eTs = stitched[stitched.length - 1][0];
1353
+ nextCandles = deps.fillCandleGaps(stitched, cfg.intervalSeconds, sTs, eTs);
1354
+ }
1355
+ else {
1356
+ nextCandles = stitched;
1357
+ }
1358
+ sourceLabel = 'kibana+native-bootstrap';
1359
+ }
1360
+ else {
1361
+ nextCandles = kibanaCandles;
1362
+ sourceLabel = 'kibana-bootstrap';
1363
+ }
1364
+ }
1365
+ else if (nativeCandles.length > 0) {
1366
+ kibanaBootstrapEmpty = true;
1367
+ nextCandles = nativeCandles;
1368
+ sourceLabel = 'native-bootstrap';
1369
+ }
1370
+ else {
1371
+ kibanaBootstrapEmpty = true;
1372
+ }
1373
+ }
1374
+ }
1375
+ else {
1376
+ const lastTs = existingCandles[existingCandles.length - 1]?.[0] || 0;
1377
+ try {
1378
+ const knownSequences = new Set(nativeRecentTradeSequences.map((seq) => String(seq)));
1379
+ let fetchedTrades = [];
1380
+ let newTrades = [];
1381
+ let overlapUsed = false;
1382
+ if (knownSequences.size >= 2 && typeof deps.fetchNativeTradesUntilOverlap === 'function') {
1383
+ try {
1384
+ const overlapResult = await deps.withRetries(() => deps.fetchNativeTradesUntilOverlap(ctx.poolId, nativeRecentTradeSequences, 2, cfg.pageLimit, cfg.maxPages), cfg.sourceRetries, cfg.retryDelayMs, 'native incremental overlap fetch failed');
1385
+ if (overlapResult?.reachedOverlap === false) {
1386
+ if (typeof deps.logger?.warn === 'function') {
1387
+ deps.logger.warn(`[market_adapter] ${bot.botKey}: native overlap fetch exhausted (${cfg.maxPages} pages) without finding an overlap; falling back to time-based`);
1388
+ }
1389
+ }
1390
+ else {
1391
+ fetchedTrades = Array.isArray(overlapResult?.trades) ? overlapResult.trades : [];
1392
+ nativeOverlapCount = Number(overlapResult?.overlapCount || 0);
1393
+ nativePagesFetched = Number(overlapResult?.pages || 0);
1394
+ sourceLabel = 'native-incremental-overlap';
1395
+ newTrades = fetchedTrades.filter((trade) => {
1396
+ if (!Number.isFinite(Number(trade?.sequence)))
1397
+ return true;
1398
+ return !knownSequences.has(String(trade.sequence));
1399
+ });
1400
+ overlapUsed = true;
1401
+ }
1402
+ }
1403
+ catch (overlapErr) {
1404
+ if (typeof deps.logger?.log === 'function') {
1405
+ deps.logger.log(`[market_adapter] ${bot.botKey}: overlap fetch exhausted (${overlapErr.message}), falling back to time-based`, 'warn');
1406
+ }
1407
+ // Fall through to time-based path below
1408
+ }
1409
+ }
1410
+ if (!overlapUsed) {
1411
+ const sinceMs = lastTs - (cfg.nativeBackfillHours * 3600 * 1000);
1412
+ const fetchResult = await deps.withRetries(() => deps.fetchNativeTradesSince(ctx.poolId, sinceMs, cfg.pageLimit, cfg.maxPages), cfg.sourceRetries, cfg.retryDelayMs, 'native incremental fetch failed');
1413
+ fetchedTrades = Array.isArray(fetchResult?.trades) ? fetchResult.trades : [];
1414
+ if (fetchResult?.truncated && typeof deps.logger?.warn === 'function') {
1415
+ deps.logger.warn(`[market_adapter] ${bot.botKey}: native incremental history truncated (exhausted ${cfg.maxPages} pages)`);
1416
+ }
1417
+ newTrades = this.filterTimeBasedNativeNewTrades(fetchedTrades, knownSequences, nativeLastTradeTs, lastTs, cfg.intervalSeconds);
1418
+ sourceLabel = 'native-incremental-time';
1419
+ nativeOverlapCount = null;
1420
+ nativePagesFetched = fetchResult?.pages || null;
1421
+ }
1422
+ if (fetchedTrades.length > 0) {
1423
+ nativeRecentTradeSequences = this.getNativeRecentTradeSequences(fetchedTrades);
1424
+ const latestTradeTs = Math.max(...fetchedTrades.map((t) => Number(t?.tsMs)).filter(Number.isFinite));
1425
+ if (Number.isFinite(latestTradeTs))
1426
+ nativeLastTradeTs = latestTradeTs;
1427
+ }
1428
+ const incomingCandles = deps.tradesToCandles(newTrades, ctx.assetA, ctx.assetB, cfg.intervalSeconds);
1429
+ nextCandles = deps.mergeCandles(existingCandles, incomingCandles, {
1430
+ onCollision: (existingCandle, incomingCandle) => this.buildIncrementalCandleCollision(existingCandle, incomingCandle),
1431
+ });
1432
+ // Fill bounded no-trade gaps from native incremental fetch. Ordinary LP
1433
+ // inactivity should remain a continuous flat 1h series; very large gaps stay
1434
+ // visible for Kibana repair/stale-tail handling instead of carrying stale prices.
1435
+ const bucketMs = Number(cfg.intervalSeconds) * 1000;
1436
+ const nowMs = this.getNowMs();
1437
+ const currentBucketStartMs = Math.floor(Number(nowMs) / bucketMs) * bucketMs;
1438
+ const latestClosedBucketTs = currentBucketStartMs - bucketMs;
1439
+ const earliestIncomingTs = incomingCandles.length > 0 ? incomingCandles[0][0] : null;
1440
+ const gapEndTs = Number.isFinite(earliestIncomingTs) && earliestIncomingTs > lastTs
1441
+ ? earliestIncomingTs
1442
+ : latestClosedBucketTs + bucketMs;
1443
+ const gapBuckets = Number.isFinite(gapEndTs) && gapEndTs > lastTs
1444
+ ? Math.round((gapEndTs - lastTs) / bucketMs) - 1
1445
+ : 0;
1446
+ if (gapBuckets <= trustedNoTradeGapThresholdCandles) {
1447
+ nextCandles = this.fillNativeIncrementalClosedGaps(nextCandles, lastTs, cfg.intervalSeconds);
1448
+ }
1449
+ else {
1450
+ const verifiedSilence = await verifyAndFillLongSilence(nextCandles, lastTs, latestClosedBucketTs, sourceLabel, incomingCandles);
1451
+ nextCandles = verifiedSilence.candles;
1452
+ sourceLabel = verifiedSilence.sourceLabel;
1453
+ nextCandles = fillBoundedTrailingClosedGap(nextCandles, latestClosedBucketTs, nowMs, trustedNoTradeGapThresholdCandles);
1454
+ }
1455
+ }
1456
+ catch (err) {
1457
+ if (typeof deps.logger?.warn === 'function') {
1458
+ deps.logger.warn(`[market_adapter] Native fetch failed for ${bot.botKey}; continuing with cached candles (${err.message})`);
1459
+ }
1460
+ nativePagesFetched = 0;
1461
+ nativeOverlapCount = null;
1462
+ sourceLabel = 'cached-native-fetch-err';
1463
+ nextCandles = existingCandles;
1464
+ }
1465
+ // After incremental merge (or native fetch failure), prune any stale tail
1466
+ // that may have been carried forward when the pool had no activity.
1467
+ // Verifies with Kibana first to avoid removing genuinely flat market periods.
1468
+ const staleThreshold = Number.isFinite(cfg.staleTailThreshold)
1469
+ ? cfg.staleTailThreshold
1470
+ : MARKET_ADAPTER.STALE_TAIL_THRESHOLD_CANDLES;
1471
+ const verified = await verifyAndPruneStaleTail(nextCandles, staleThreshold, staleTailVerifiedRangeFromMeta());
1472
+ nextCandles = verified.candles;
1473
+ applyStaleTailVerificationMeta(verified);
1474
+ }
1475
+ let kibanaGapRepairTimestamps = [];
1476
+ let kibanaGapRepairAttempted = false;
1477
+ let gapAnalysis = typeof deps.detectMissingCandleTimestamps === 'function'
1478
+ ? deps.detectMissingCandleTimestamps(nextCandles, cfg.intervalSeconds)
1479
+ : { gapCount: 0, missingTimestamps: [] };
1480
+ // Step 1: Fill gaps within the trusted no-trade threshold directly (no Kibana needed).
1481
+ // The native incremental fetch already confirmed no trades for these timestamps.
1482
+ if (gapAnalysis.gapCount > 0) {
1483
+ const filledNoTrade = this.fillVerifiedInternalNoTradeGaps(nextCandles, gapAnalysis.missingTimestamps, cfg.intervalSeconds, trustedNoTradeGapThresholdCandles);
1484
+ if (filledNoTrade.filledTimestamps.length > 0) {
1485
+ nextCandles = filledNoTrade.candles;
1486
+ kibanaGapRepairTimestamps = filledNoTrade.filledTimestamps.slice();
1487
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: synthesized ${filledNoTrade.filledTimestamps.length} no-trade candle(s) within trusted threshold `
1488
+ + `[${filledNoTrade.filledTimestamps.map((ts) => new Date(ts).toISOString()).join(', ')}]`);
1489
+ }
1490
+ // Re-detect gaps — only beyond-threshold gaps remain
1491
+ gapAnalysis = typeof deps.detectMissingCandleTimestamps === 'function'
1492
+ ? deps.detectMissingCandleTimestamps(nextCandles, cfg.intervalSeconds)
1493
+ : { gapCount: 0, missingTimestamps: [] };
1494
+ }
1495
+ // Step 2: Only query Kibana for gaps beyond the trusted threshold
1496
+ if (gapAnalysis.gapCount > 0 && hasKibanaSource) {
1497
+ const timeRange = this.buildGapRepairTimeRange(gapAnalysis.missingTimestamps, cfg.intervalSeconds, this.getGapRepairMaxHours(cfg));
1498
+ if (timeRange) {
1499
+ kibanaGapRepairAttempted = true;
1500
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: ${gapAnalysis.gapCount} candle gap(s) beyond trusted threshold; `
1501
+ + `requesting Kibana repair for ${timeRange.gte} -> ${timeRange.lte} `
1502
+ + `missing=[${gapAnalysis.missingTimestamps.map((ts) => new Date(ts).toISOString()).join(', ')}]`);
1503
+ try {
1504
+ const kibanaGapCandles = await deps.withRetries(() => fetchKibanaCandles({
1505
+ intervalSeconds: cfg.intervalSeconds,
1506
+ consolidateByTimestamp: true,
1507
+ fillGapsToRequestedRange: false,
1508
+ apiKey: null,
1509
+ timeRange,
1510
+ }), cfg.sourceRetries, cfg.retryDelayMs, 'kibana gap repair failed');
1511
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: Kibana gap repair returned `
1512
+ + `${Array.isArray(kibanaGapCandles) ? kibanaGapCandles.length : 0} candle(s) `
1513
+ + `for ${timeRange.gte} -> ${timeRange.lte}`);
1514
+ if (Array.isArray(kibanaGapCandles) && kibanaGapCandles.length > 0) {
1515
+ const beforeTimestamps = new Set(nextCandles.map((c) => c[0]));
1516
+ nextCandles = deps.mergeCandles(nextCandles, kibanaGapCandles);
1517
+ const afterTimestamps = new Set(nextCandles.map((c) => c[0]));
1518
+ const kibanaPatchedTs = gapAnalysis.missingTimestamps.filter((ts) => !beforeTimestamps.has(ts) && afterTimestamps.has(ts));
1519
+ kibanaGapRepairTimestamps = [...kibanaGapRepairTimestamps, ...kibanaPatchedTs];
1520
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: Kibana gap repair patched ${kibanaPatchedTs.length}/${gapAnalysis.gapCount} gap(s)`
1521
+ + (kibanaPatchedTs.length > 0
1522
+ ? ` [${kibanaPatchedTs.map((ts) => new Date(ts).toISOString()).join(', ')}]`
1523
+ : ''));
1524
+ }
1525
+ else {
1526
+ // Kibana returned no data — that IS verification: no trades happened.
1527
+ // Synthesize no-trade candles for all gaps within the queried window.
1528
+ const verifiedGapTimestamps = this.getMissingTimestampsWithinTimeRange(gapAnalysis.missingTimestamps, timeRange);
1529
+ const verifiedNoTrade = this.fillVerifiedInternalNoTradeGaps(nextCandles, verifiedGapTimestamps, cfg.intervalSeconds, verifiedGapTimestamps.length);
1530
+ if (verifiedNoTrade.filledTimestamps.length > 0) {
1531
+ nextCandles = verifiedNoTrade.candles;
1532
+ kibanaGapRepairTimestamps = [...kibanaGapRepairTimestamps, ...verifiedNoTrade.filledTimestamps];
1533
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: synthesized ${verifiedNoTrade.filledTimestamps.length} no-trade candle(s) after empty Kibana repair `
1534
+ + `[${verifiedNoTrade.filledTimestamps.map((ts) => new Date(ts).toISOString()).join(', ')}]`);
1535
+ }
1536
+ else {
1537
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: Kibana empty but could not synthesize gaps (missing neighbors)`, 'warn');
1538
+ }
1539
+ }
1540
+ }
1541
+ catch (err) {
1542
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: Kibana gap repair failed (${err.message || String(err)})`, 'warn');
1543
+ }
1544
+ }
1545
+ gapAnalysis = typeof deps.detectMissingCandleTimestamps === 'function'
1546
+ ? deps.detectMissingCandleTimestamps(nextCandles, cfg.intervalSeconds)
1547
+ : { gapCount: 0, missingTimestamps: [] };
1548
+ if (gapAnalysis.gapCount > 0) {
1549
+ logGapRepairEvent(`[market_adapter] ${bot.botKey}: ${gapAnalysis.gapCount} candle gap(s) still unresolved after Kibana repair `
1550
+ + `[${gapAnalysis.missingTimestamps.map((ts) => new Date(ts).toISOString()).join(', ')}]`, 'warn');
1551
+ }
1552
+ }
1553
+ // ── Historical backfill: if candle count is still insufficient for AMA warmup,
1554
+ // fetch older history from Kibana using a targeted timeRange. This handles
1555
+ // cases where native bootstrap fell short or the file was truncated.
1556
+ const candleShortfall = rawKeepCount - nextCandles.length;
1557
+ let kibanaBackfillCount = 0;
1558
+ const needsHistoricalBackfill = !kibanaGapRepairAttempted || gapAnalysis.gapCount > 0;
1559
+ if (!kibanaBootstrapEmpty && needsHistoricalBackfill && candleShortfall > 0 && nextCandles.length > 0 && hasKibanaSource) {
1560
+ const oldestTs = nextCandles[0][0];
1561
+ const shortfallMs = candleShortfall * cfg.intervalSeconds * 1000;
1562
+ const bufferMs = 24 * 3600 * 1000; // 24h buffer
1563
+ const backfillStartMs = Math.max(0, oldestTs - shortfallMs - bufferMs);
1564
+ const backfillEndMs = oldestTs + cfg.intervalSeconds * 1000;
1565
+ try {
1566
+ const historicalCandles = await deps.withRetries(() => fetchKibanaCandles({
1567
+ intervalSeconds: cfg.intervalSeconds,
1568
+ consolidateByTimestamp: true,
1569
+ fillGapsToRequestedRange: false,
1570
+ apiKey: null,
1571
+ timeRange: {
1572
+ gte: new Date(backfillStartMs).toISOString(),
1573
+ lte: new Date(backfillEndMs).toISOString(),
1574
+ },
1575
+ }), cfg.sourceRetries, cfg.retryDelayMs, 'kibana historical backfill failed');
1576
+ if (Array.isArray(historicalCandles) && historicalCandles.length > 0) {
1577
+ nextCandles = deps.mergeCandles(nextCandles, historicalCandles);
1578
+ kibanaBackfillCount = historicalCandles.length;
1579
+ sourceLabel = `${sourceLabel}+kibana-backfill`;
1580
+ }
1581
+ }
1582
+ catch (_) {
1583
+ if (typeof deps.logger?.warn === 'function')
1584
+ deps.logger.warn(`[market_adapter] ${ctx.botKey}: kibana historical backfill failed`);
1585
+ }
1586
+ }
1587
+ nextCandles = deps.pruneCandles(nextCandles, rawKeepCount);
1588
+ const retainedTimestamps = new Set(nextCandles.map((c) => c[0]));
1589
+ const kibanaGapRepairCount = kibanaGapRepairTimestamps.filter((ts) => retainedTimestamps.has(ts)).length;
1590
+ const retainedGapAnalysis = typeof deps.detectMissingCandleTimestamps === 'function'
1591
+ ? deps.detectMissingCandleTimestamps(nextCandles, cfg.intervalSeconds)
1592
+ : { gapCount: 0, missingTimestamps: [] };
1593
+ return {
1594
+ nextCandles,
1595
+ sourceLabel,
1596
+ kibanaGapRepairCount,
1597
+ kibanaBackfillCount,
1598
+ unresolvedGapCount: retainedGapAnalysis.gapCount,
1599
+ nativeRecentTradeSequences,
1600
+ nativeLastTradeTs,
1601
+ nativeOverlapCount,
1602
+ nativePagesFetched,
1603
+ };
1604
+ };
1605
+ let closedCandles = [];
1606
+ let currentBucketStartMs = null;
1607
+ let rawLastCandle = [0, 0, 0, 0, 0];
1608
+ let rawLastCandleTs = null;
1609
+ let latestClosedCandle = null;
1610
+ let lastClosedCandleTs = null;
1611
+ let botState = {};
1612
+ let previousClosedCandleTs = 0;
1613
+ let hasNewClosedCandle = false;
1614
+ let consumedClosedCandleTs = null;
1615
+ let nextCandles = existingCandles;
1616
+ let sourceLabel = 'native-incremental';
1617
+ let kibanaGapRepairCount = 0;
1618
+ let kibanaBackfillCount = 0;
1619
+ let unresolvedGapCount = 0;
1620
+ let nativeRecentTradeSequences = [];
1621
+ let nativeLastTradeTs = null;
1622
+ let nativeOverlapCount = null;
1623
+ let nativePagesFetched = null;
1624
+ const loadResult = await loadCandles();
1625
+ nextCandles = loadResult.nextCandles;
1626
+ sourceLabel = loadResult.sourceLabel;
1627
+ kibanaGapRepairCount = loadResult.kibanaGapRepairCount;
1628
+ kibanaBackfillCount = loadResult.kibanaBackfillCount || 0;
1629
+ unresolvedGapCount = loadResult.unresolvedGapCount;
1630
+ nativeRecentTradeSequences = Array.isArray(loadResult.nativeRecentTradeSequences) ? loadResult.nativeRecentTradeSequences : [];
1631
+ nativeLastTradeTs = Number.isFinite(loadResult.nativeLastTradeTs) ? loadResult.nativeLastTradeTs : null;
1632
+ nativeOverlapCount = Number.isFinite(loadResult.nativeOverlapCount) ? loadResult.nativeOverlapCount : null;
1633
+ nativePagesFetched = Number.isFinite(loadResult.nativePagesFetched) ? loadResult.nativePagesFetched : null;
1634
+ const nowMs = this.getNowMs();
1635
+ ({ closedCandles, currentBucketStartMs } = this.selectClosedCandles(nextCandles, cfg.intervalSeconds, nowMs));
1636
+ rawLastCandle = nextCandles[nextCandles.length - 1] || [0, 0, 0, 0, 0];
1637
+ rawLastCandleTs = rawLastCandle[0] || null;
1638
+ latestClosedCandle = closedCandles[closedCandles.length - 1] || null;
1639
+ lastClosedCandleTs = latestClosedCandle ? latestClosedCandle[0] : null;
1640
+ botState = this.normalizePersistedBotState(state.bots[bot.botKey], lookbackBars);
1641
+ if (sourceMismatch) {
1642
+ botState = {};
1643
+ }
1644
+ const stateGridCenterPrice = Number(botState.gridCenterPrice ?? botState.centerPrice);
1645
+ if (Number.isFinite(stateGridCenterPrice) && stateGridCenterPrice > 0) {
1646
+ botState.gridCenterPrice = stateGridCenterPrice;
1647
+ botState.centerPrice = stateGridCenterPrice;
1648
+ }
1649
+ const dynGridPath = deps.path.join(deps.root, 'profiles', 'orders', `${bot.botKey}.dynamicgrid.json`);
1650
+ const persistedDynamicGridState = typeof deps.loadJson === 'function'
1651
+ ? this.extractPersistedDynamicGridState(deps.loadJson(dynGridPath, null), lookbackBars)
1652
+ : null;
1653
+ if (persistedDynamicGridState) {
1654
+ const persistedResetMs = Date.parse(String(persistedDynamicGridState.lastGridResetAt || ''));
1655
+ const stateResetMs = Date.parse(String(botState.lastGridResetAt || ''));
1656
+ const persistedResetIsNewer = Number.isFinite(persistedResetMs)
1657
+ && (!Number.isFinite(stateResetMs) || persistedResetMs >= stateResetMs);
1658
+ if ((persistedResetIsNewer || !(Number(botState.gridCenterPrice ?? botState.centerPrice) > 0))
1659
+ && persistedDynamicGridState.gridCenterPrice) {
1660
+ botState.gridCenterPrice = persistedDynamicGridState.gridCenterPrice;
1661
+ botState.centerPrice = persistedDynamicGridState.gridCenterPrice;
1662
+ }
1663
+ if (persistedResetIsNewer) {
1664
+ botState.lastGridResetAt = persistedDynamicGridState.lastGridResetAt;
1665
+ if (persistedDynamicGridState.lastGridResetSource) {
1666
+ botState.lastGridResetSource = persistedDynamicGridState.lastGridResetSource;
1667
+ }
1668
+ }
1669
+ if (!(Number(botState.amaCenterPrice) > 0) && persistedDynamicGridState.amaCenterPrice) {
1670
+ botState.amaCenterPrice = persistedDynamicGridState.amaCenterPrice;
1671
+ }
1672
+ if (!botState.amaSlope && persistedDynamicGridState.amaSlope) {
1673
+ botState.amaSlope = persistedDynamicGridState.amaSlope;
1674
+ }
1675
+ if (!botState.gridRangeScalingAmaSlope && persistedDynamicGridState.gridRangeScalingAmaSlope) {
1676
+ botState.gridRangeScalingAmaSlope = persistedDynamicGridState.gridRangeScalingAmaSlope;
1677
+ }
1678
+ if (botState.amaSlopeDeltaPercent == null && persistedDynamicGridState.amaSlopeDeltaPercent != null) {
1679
+ botState.amaSlopeDeltaPercent = persistedDynamicGridState.amaSlopeDeltaPercent;
1680
+ }
1681
+ if (botState.amaSlopeThresholdPercent == null && persistedDynamicGridState.amaSlopeThresholdPercent != null) {
1682
+ botState.amaSlopeThresholdPercent = persistedDynamicGridState.amaSlopeThresholdPercent;
1683
+ }
1684
+ }
1685
+ const previousAmaSlope = botState.amaSlope || null;
1686
+ const previousGridResetAmaSlope = botState.gridRangeScalingAmaSlope || previousAmaSlope;
1687
+ delete botState.gridPriceOffsetPct;
1688
+ delete botState.gridPriceOffsetClampToBounds;
1689
+ previousClosedCandleTs = Number(botState.lastClosedCandleTs || 0);
1690
+ consumedClosedCandleTs = Number.isFinite(previousClosedCandleTs) && previousClosedCandleTs > 0
1691
+ ? previousClosedCandleTs
1692
+ : null;
1693
+ hasNewClosedCandle = Number.isFinite(lastClosedCandleTs) && lastClosedCandleTs > previousClosedCandleTs;
1694
+ const candlePayload = {
1695
+ meta: {
1696
+ updatedAt: nowIso,
1697
+ source: sourceLabel,
1698
+ pool: ctx.poolId,
1699
+ marketSource,
1700
+ assetA: ctx.assetA,
1701
+ assetB: ctx.assetB,
1702
+ intervalSeconds: cfg.intervalSeconds,
1703
+ candleCount: nextCandles.length,
1704
+ analysisCandleCount: closedCandles.length,
1705
+ rawKeepCount,
1706
+ analysisKeepCount,
1707
+ amaWarmupBars,
1708
+ currentBucketStartMs,
1709
+ lastClosedCandleTs,
1710
+ rawLastCandleTs,
1711
+ kibanaGapRepairCount,
1712
+ kibanaBackfillCount,
1713
+ unresolvedGapCount,
1714
+ nativeRecentTradeSequences,
1715
+ nativeLastTradeTs,
1716
+ nativeOverlapCount,
1717
+ nativePagesFetched,
1718
+ staleTailVerifiedStartTs: Number.isFinite(existingMeta.staleTailVerifiedStartTs)
1719
+ ? existingMeta.staleTailVerifiedStartTs : null,
1720
+ staleTailVerifiedEndTs: Number.isFinite(existingMeta.staleTailVerifiedEndTs)
1721
+ ? existingMeta.staleTailVerifiedEndTs : null,
1722
+ format: '[timestamp_ms, open, high, low, close, volume_A]',
1723
+ },
1724
+ candles: nextCandles,
1725
+ };
1726
+ deps.saveJson(filePath, candlePayload);
1727
+ if (!hasNewClosedCandle) {
1728
+ const { staleData, staleAgeHours } = deps.computeCandleStaleness(lastClosedCandleTs, cfg.maxStaleHours);
1729
+ const pendingClosedCandle = !staleData;
1730
+ const triggerSuppressedReason = staleData
1731
+ ? 'stale_candle_data'
1732
+ : 'waiting_for_new_closed_candle';
1733
+ state.bots[bot.botKey] = {
1734
+ ...botState,
1735
+ botName: bot.name,
1736
+ botKey: bot.botKey,
1737
+ poolId: ctx.poolId,
1738
+ marketSource,
1739
+ candleFile: deps.path.relative(deps.root, filePath),
1740
+ candleCount: nextCandles.length,
1741
+ analysisCandleCount: closedCandles.length,
1742
+ rawKeepCount,
1743
+ analysisKeepCount,
1744
+ amaWarmupBars,
1745
+ kibanaGapRepairCount,
1746
+ kibanaBackfillCount,
1747
+ unresolvedGapCount,
1748
+ nativeRecentTradeSequences,
1749
+ nativeLastTradeTs,
1750
+ nativeOverlapCount,
1751
+ nativePagesFetched,
1752
+ lastCandleTs: rawLastCandleTs,
1753
+ rawLastCandleTs,
1754
+ lastClosedCandleTs: consumedClosedCandleTs,
1755
+ lastCycleSource: sourceLabel,
1756
+ lastCycleAt: nowIso,
1757
+ staleData,
1758
+ staleAgeHours,
1759
+ pendingClosedCandle,
1760
+ lastTriggerSuppressedReason: triggerSuppressedReason,
1761
+ };
1762
+ return this.buildDefaultResult(bot, {
1763
+ dryRunMessages,
1764
+ source: sourceLabel,
1765
+ marketSource,
1766
+ candleCount: nextCandles.length,
1767
+ analysisCandleCount: closedCandles.length,
1768
+ rawKeepCount,
1769
+ analysisKeepCount,
1770
+ amaWarmupBars,
1771
+ kibanaGapRepairCount,
1772
+ kibanaBackfillCount,
1773
+ unresolvedGapCount,
1774
+ nativeRecentTradeSequences,
1775
+ nativeLastTradeTs,
1776
+ nativeOverlapCount,
1777
+ nativePagesFetched,
1778
+ thresholdPercent: botThreshold,
1779
+ staleData,
1780
+ staleAgeHours,
1781
+ triggerSuppressedReason,
1782
+ poolId: ctx.poolId,
1783
+ candleFile: deps.path.relative(deps.root, filePath),
1784
+ lastCandleTs: rawLastCandleTs,
1785
+ rawLastCandleTs,
1786
+ lastClosedCandleTs: consumedClosedCandleTs,
1787
+ amaConfig: {
1788
+ erPeriod: botAma.erPeriod,
1789
+ fastPeriod: botAma.fastPeriod,
1790
+ slowPeriod: botAma.slowPeriod,
1791
+ erSmoothPeriod: botAma.erSmoothPeriod ?? 0,
1792
+ },
1793
+ pendingClosedCandle,
1794
+ });
1795
+ }
1796
+ // ------------------ MARKET ADAPTER STRATEGIES ------------------
1797
+ // 1. AMA series and closes — used for price reference and signal computation
1798
+ const analysisCandles = closedCandles;
1799
+ const closes = analysisCandles.map((c) => Number(c[4])).filter((v) => Number.isFinite(v) && v > 0);
1800
+ const amaValues = calculateAMA(closes, botAma);
1801
+ // amaPrice is the last value of the full AMA series
1802
+ const amaPrice = amaValues[amaValues.length - 1];
1803
+ const lastCandle = latestClosedCandle || [0, 0, 0, 0, 0];
1804
+ // 2. ATR — used only for the symmetric volatility shift. The asymmetrical
1805
+ // trend/Kalman branch stays ATR-free to match the research HTML.
1806
+ const atrPeriod = normalizeAtrPeriod(cfg.atrPeriod);
1807
+ const atr = calculateATR(analysisCandles, atrPeriod);
1808
+ const warn = (message) => {
1809
+ if (typeof deps.logger?.log === 'function') {
1810
+ deps.logger.log(message, 'warn');
1811
+ }
1812
+ else if (typeof deps.logger?.warn === 'function') {
1813
+ deps.logger.warn(message);
1814
+ }
1815
+ else {
1816
+ marketAdapterServiceLogger.warn(message);
1817
+ }
1818
+ };
1819
+ if (!Number.isFinite(atr)) {
1820
+ warn(`ATR calculation failed for ${bot.botKey}; disabling volatility penalty for this cycle.`);
1821
+ }
1822
+ const weightVariance = Number.isFinite(atr) && amaPrice > 0 ? (atr / amaPrice) : 0;
1823
+ // 3. Signal computation — dynamic weights use AMA + Kalman + regime gate,
1824
+ // with ATR applied only as a separate symmetric penalty. Range scaling
1825
+ // reuses the AMA slope signal, but buy/sell dynamic weights are only
1826
+ // exposed, persisted, or applied when dynamicWeight is whitelisted.
1827
+ const isAmaWhitelisted = forceWhitelistAll || (typeof deps.isBotWhitelisted === 'function'
1828
+ ? deps.isBotWhitelisted(bot.botKey)
1829
+ : true);
1830
+ const isDynamicWeightFlagWhitelisted = forceWhitelistAll || (typeof deps.isBotDynamicWeightWhitelisted === 'function'
1831
+ && deps.isBotDynamicWeightWhitelisted(bot.botKey));
1832
+ const isDynamicWeightWhitelisted = isAmaWhitelisted && isDynamicWeightFlagWhitelisted;
1833
+ const isGridRangeScalingWhitelisted = forceWhitelistAll
1834
+ || (typeof deps.isBotGridRangeScalingWhitelisted === 'function'
1835
+ && deps.isBotGridRangeScalingWhitelisted(bot.botKey))
1836
+ || (typeof deps.isBotAsymmetricBoundsWhitelisted === 'function'
1837
+ && deps.isBotAsymmetricBoundsWhitelisted(bot.botKey));
1838
+ const isAsymmetricBoundsWhitelisted = isGridRangeScalingWhitelisted;
1839
+ const hasExplicitBaseWeights = Number.isFinite(bot.weightDistribution?.sell)
1840
+ && Number.isFinite(bot.weightDistribution?.buy);
1841
+ const isAmaGridBot = /^ama(?:[1-4])?$/i.test(String(bot?.gridPrice || '').trim());
1842
+ const shouldComputeDynamicWeightSignal = isAmaGridBot && hasExplicitBaseWeights
1843
+ && (isDynamicWeightWhitelisted || isGridRangeScalingWhitelisted);
1844
+ const canExposeDynamicWeights = isDynamicWeightWhitelisted && shouldComputeDynamicWeightSignal;
1845
+ const canApplyDynamicWeights = canExposeDynamicWeights;
1846
+ if (!hasExplicitBaseWeights && isAmaGridBot && (isDynamicWeightWhitelisted || isGridRangeScalingWhitelisted)) {
1847
+ warn(`${bot.botKey} is missing explicit weightDistribution; skipping dynamic volatility weights for this cycle.`);
1848
+ }
1849
+ let slopeResult = null;
1850
+ let amaSlope = null;
1851
+ let weights = null;
1852
+ let dynamicWeightsPayload = null;
1853
+ if (shouldComputeDynamicWeightSignal) {
1854
+ const dwResult = this._computeDynamicWeights({
1855
+ analysisCandles,
1856
+ closes,
1857
+ amaValues,
1858
+ amaWarmupBars,
1859
+ lookbackBars,
1860
+ botAma,
1861
+ weightVariance,
1862
+ amaPrice,
1863
+ nowIso,
1864
+ cfg,
1865
+ bot,
1866
+ ctx,
1867
+ deps,
1868
+ atrPeriod,
1869
+ });
1870
+ slopeResult = dwResult.slopeResult;
1871
+ amaSlope = dwResult.amaSlope;
1872
+ weights = dwResult.weights;
1873
+ dynamicWeightsPayload = dwResult.dynamicWeightsPayload;
1874
+ }
1875
+ const amaSlopeResetDetails = this.buildAmaSlopeResetDetails(amaSlope, previousGridResetAmaSlope, cfg);
1876
+ const amaSlopeDeltaPercent = amaSlopeResetDetails.deltaPercent;
1877
+ const amaSlopeThresholdPercent = amaSlopeResetDetails.thresholdPercent;
1878
+ // 4. Advisory collateral-ratio hint only; execution is owned by the debt runtime.
1879
+ const collateralRecommendation = canExposeDynamicWeights ? adjustCollateralRatio(slopeResult, 1.5, 2.0) : null;
1880
+ const amaComparison = deps.calcAmaComparison(analysisCandles, bot, ctx);
1881
+ const closedCandleTs = lastCandle[0] || null;
1882
+ const { staleData, staleAgeHours } = deps.computeCandleStaleness(closedCandleTs, cfg.maxStaleHours);
1883
+ const referencePrice = amaPrice;
1884
+ const clampedCenterPrice = this.clampGridPriceToBounds(referencePrice, referencePrice, bot);
1885
+ const centerPrice = Number.isFinite(clampedCenterPrice) && clampedCenterPrice > 0
1886
+ ? clampedCenterPrice
1887
+ : referencePrice;
1888
+ const gridPriceOffsetPlan = computeGridPriceOffsetPlan(bot, amaSlope);
1889
+ // Compute asymmetry metrics for grid range scaling independently of
1890
+ // dynamicWeight flag. Uses dynamicWeightsPayload when available, falls
1891
+ // back to amaSlope so that asymmetricBounds: true works regardless of
1892
+ // whether dynamicWeight: true is set.
1893
+ const asymmetrySource = dynamicWeightsPayload || amaSlope;
1894
+ const asymmetryMetrics = isAsymmetricBoundsWhitelisted && asymmetrySource
1895
+ ? this.computeAppliedAsymmetryMetrics(bot, centerPrice, asymmetrySource)
1896
+ : {
1897
+ rawAsymmetryFactor: null,
1898
+ appliedAsymmetryFactor: null,
1899
+ maxAsymmetryFactor: null,
1900
+ };
1901
+ if (dynamicWeightsPayload) {
1902
+ Object.assign(dynamicWeightsPayload, asymmetryMetrics);
1903
+ if (weights?.meta) {
1904
+ Object.assign(weights.meta, asymmetryMetrics);
1905
+ }
1906
+ }
1907
+ const dynamicSnapshotPayload = canExposeDynamicWeights
1908
+ ? dynamicWeightsPayload
1909
+ : null;
1910
+ let triggered = false;
1911
+ let triggerPath = null;
1912
+ let deltaPercent = null;
1913
+ let triggerCallbackError = null;
1914
+ let triggerSuppressedReason = null;
1915
+ let snapshotPersistedThisCycle = false;
1916
+ let previousCenterPrice = Number(botState.centerPrice || 0);
1917
+ const hasUnresolvedCandleGaps = Number.isFinite(unresolvedGapCount) && unresolvedGapCount > 0;
1918
+ if (hasUnresolvedCandleGaps) {
1919
+ triggerSuppressedReason = 'unresolved_candle_gaps';
1920
+ }
1921
+ const buildDynamicGridOptions = (options = {}) => {
1922
+ const payload = {
1923
+ gridCenterPrice: options.gridCenterPrice ?? null, // explicit baseline if provided
1924
+ amaCenterPrice: amaPrice,
1925
+ amaSlope: options.amaSlope ?? amaSlope,
1926
+ gridRangeScalingAmaSlope: options.gridRangeScalingAmaSlope ?? (amaSlope || previousGridResetAmaSlope || null),
1927
+ amaSlopeDeltaPercent,
1928
+ amaSlopeThresholdPercent,
1929
+ observedLastGridResetAt: botState.lastGridResetAt,
1930
+ ...(options.previousCenterPrice !== undefined
1931
+ ? { previousCenterPrice: options.previousCenterPrice }
1932
+ : {}),
1933
+ ...(dynamicSnapshotPayload
1934
+ ? { dynamicWeights: dynamicSnapshotPayload }
1935
+ : {}),
1936
+ };
1937
+ if (isGridRangeScalingWhitelisted) {
1938
+ payload.gridPriceOffsetPct = options.gridPriceOffsetPct ?? gridPriceOffsetPlan.gridPriceOffsetPct;
1939
+ // Persist computed asymmetry metrics at root level so display
1940
+ // tools can render the grid range scaling percentage even when
1941
+ // dynamicWeight is not enabled for this bot.
1942
+ if (Number.isFinite(asymmetryMetrics.appliedAsymmetryFactor)) {
1943
+ const asymTrend = (dynamicWeightsPayload || amaSlope)?.trend;
1944
+ if (asymTrend === 'UP' || asymTrend === 'DOWN') {
1945
+ payload.asymmetricBounds = {
1946
+ rawAsymmetryFactor: asymmetryMetrics.rawAsymmetryFactor,
1947
+ appliedAsymmetryFactor: asymmetryMetrics.appliedAsymmetryFactor,
1948
+ trend: asymTrend,
1949
+ };
1950
+ }
1951
+ }
1952
+ }
1953
+ return payload;
1954
+ };
1955
+ const persistDynamicGridSnapshot = (snapshotCenterPrice, options = {}) => {
1956
+ if (!isDryRun && typeof deps.writeBotDynamicGrid === 'function') {
1957
+ return deps.writeBotDynamicGrid(bot.botKey, snapshotCenterPrice, buildDynamicGridOptions(options)) !== false;
1958
+ }
1959
+ if (isDryRun) {
1960
+ dryRunMessages.push(`[DRY RUN] Would write dynamic grid for ${bot.botKey}: ${snapshotCenterPrice}`);
1961
+ }
1962
+ return true;
1963
+ };
1964
+ const advanceTriggeredBotState = (newCenterPrice, options = {}) => {
1965
+ snapshotPersistedThisCycle = true;
1966
+ botState.gridCenterPrice = newCenterPrice;
1967
+ botState.centerPrice = newCenterPrice;
1968
+ botState.amaCenterPrice = amaPrice;
1969
+ botState.amaSlope = options.amaSlope ?? (amaSlope || previousAmaSlope || null);
1970
+ botState.gridRangeScalingAmaSlope = options.gridRangeScalingAmaSlope ?? (amaSlope || previousGridResetAmaSlope || null);
1971
+ botState.amaSlopeDeltaPercent = Number.isFinite(amaSlopeDeltaPercent)
1972
+ ? amaSlopeDeltaPercent
1973
+ : botState.amaSlopeDeltaPercent ?? null;
1974
+ botState.amaSlopeThresholdPercent = amaSlopeThresholdPercent;
1975
+ botState.amaSlopePercentMode = AMA_SLOPE_PERCENT_MODE_PER_BAR;
1976
+ botState.triggerCount = Number(botState.triggerCount || 0) + 1;
1977
+ if (canApplyDynamicWeights && dynamicWeightsPayload) {
1978
+ botState.effectiveWeights = dynamicWeightsPayload.effectiveWeights || null;
1979
+ }
1980
+ triggered = true;
1981
+ };
1982
+ const writeTriggerAndNotify = async ({ triggerPayload, hookPayload, dryRunMessage }) => {
1983
+ if (!isDryRun) {
1984
+ triggerPath = deps.writeGridResetTrigger(bot, triggerPayload);
1985
+ }
1986
+ else {
1987
+ dryRunMessages.push(dryRunMessage);
1988
+ }
1989
+ if (typeof hooks.onTrigger === 'function') {
1990
+ try {
1991
+ await hooks.onTrigger({
1992
+ bot,
1993
+ botKey: bot.botKey,
1994
+ botName: bot.name,
1995
+ poolId: ctx.poolId,
1996
+ ...hookPayload,
1997
+ triggerPath,
1998
+ marketSource,
1999
+ });
2000
+ }
2001
+ catch (err) {
2002
+ triggerCallbackError = err.message;
2003
+ }
2004
+ }
2005
+ };
2006
+ if (!staleData && !hasUnresolvedCandleGaps && Number.isFinite(referencePrice) && referencePrice > 0) {
2007
+ // Bootstrap when market-adapter state is missing (e.g. after clearing)
2008
+ // or when a bot runs for the first time.
2009
+ if (!Number.isFinite(previousCenterPrice) || previousCenterPrice <= 0) {
2010
+ const bootstrapCenterPrice = Number.isFinite(centerPrice) && centerPrice > 0
2011
+ ? centerPrice
2012
+ : referencePrice;
2013
+ const amaCenterPersisted = persistDynamicGridSnapshot(bootstrapCenterPrice, {
2014
+ amaSlope: amaSlope || previousAmaSlope || null,
2015
+ gridPriceOffsetPct: gridPriceOffsetPlan.gridPriceOffsetPct,
2016
+ });
2017
+ if (amaCenterPersisted) {
2018
+ advanceTriggeredBotState(bootstrapCenterPrice, {
2019
+ amaSlope: amaSlope || previousAmaSlope || null,
2020
+ gridPriceOffsetPct: gridPriceOffsetPlan.gridPriceOffsetPct,
2021
+ });
2022
+ // First-ever bootstrap — no previous center anywhere.
2023
+ // Create a trigger so the bot recalibrates to the new center.
2024
+ await writeTriggerAndNotify({
2025
+ triggerPayload: {
2026
+ reason: 'market_adapter_bootstrap',
2027
+ newCenterPrice: bootstrapCenterPrice,
2028
+ referencePrice,
2029
+ amaCenterPrice: amaPrice,
2030
+ amaSlope: amaSlope || previousAmaSlope || null,
2031
+ amaSlopeDeltaPercent,
2032
+ amaSlopeThresholdPercent,
2033
+ poolId: ctx.poolId,
2034
+ marketSource,
2035
+ },
2036
+ hookPayload: {
2037
+ thresholdPercent: botThreshold,
2038
+ deltaPercent: null,
2039
+ previousCenterPrice: undefined,
2040
+ newCenterPrice: bootstrapCenterPrice,
2041
+ referencePrice,
2042
+ amaCenterPrice: amaPrice,
2043
+ },
2044
+ dryRunMessage: `[DRY RUN] Would write grid reset trigger for ${bot.botKey} (bootstrap)`,
2045
+ });
2046
+ }
2047
+ else {
2048
+ triggerSuppressedReason = 'ama_center_persist_failed';
2049
+ }
2050
+ }
2051
+ // Unified delta check — runs when previousCenterPrice is known
2052
+ // (either from state or recovered from .dynamicgrid.json).
2053
+ if (!triggered && Number.isFinite(previousCenterPrice) && previousCenterPrice > 0) {
2054
+ deltaPercent = Math.abs((centerPrice - previousCenterPrice) / previousCenterPrice) * 100;
2055
+ if (deltaPercent >= botThreshold) {
2056
+ const amaCenterPersisted = persistDynamicGridSnapshot(centerPrice, {
2057
+ previousCenterPrice,
2058
+ gridPriceOffsetPct: gridPriceOffsetPlan.gridPriceOffsetPct,
2059
+ });
2060
+ if (!amaCenterPersisted) {
2061
+ triggerSuppressedReason = 'ama_center_persist_failed';
2062
+ }
2063
+ else {
2064
+ advanceTriggeredBotState(centerPrice);
2065
+ await writeTriggerAndNotify({
2066
+ triggerPayload: {
2067
+ reason: 'market_adapter_delta_threshold',
2068
+ thresholdPercent: botThreshold,
2069
+ deltaPercent,
2070
+ previousCenterPrice,
2071
+ newCenterPrice: centerPrice,
2072
+ referencePrice,
2073
+ amaCenterPrice: amaPrice,
2074
+ amaSlope,
2075
+ amaSlopeDeltaPercent,
2076
+ amaSlopeThresholdPercent,
2077
+ poolId: ctx.poolId,
2078
+ marketSource,
2079
+ },
2080
+ hookPayload: {
2081
+ thresholdPercent: botThreshold,
2082
+ deltaPercent,
2083
+ previousCenterPrice,
2084
+ newCenterPrice: centerPrice,
2085
+ referencePrice,
2086
+ amaCenterPrice: amaPrice,
2087
+ },
2088
+ dryRunMessage: `[DRY RUN] Would write grid reset trigger for ${bot.botKey}`,
2089
+ });
2090
+ }
2091
+ }
2092
+ }
2093
+ if (!triggered && !triggerSuppressedReason && isGridRangeScalingWhitelisted && amaSlopeResetDetails.shouldTrigger) {
2094
+ const amaSlopePersisted = persistDynamicGridSnapshot(centerPrice);
2095
+ if (!amaSlopePersisted) {
2096
+ triggerSuppressedReason = 'ama_slope_persist_failed';
2097
+ }
2098
+ else {
2099
+ advanceTriggeredBotState(centerPrice);
2100
+ await writeTriggerAndNotify({
2101
+ triggerPayload: {
2102
+ reason: 'market_adapter_ama_slope_delta_threshold',
2103
+ thresholdPercent: amaSlopeThresholdPercent,
2104
+ deltaPercent: amaSlopeDeltaPercent,
2105
+ previousAmaSlope,
2106
+ previousGridResetAmaSlope,
2107
+ amaSlope,
2108
+ amaSlopeDeltaPercent,
2109
+ amaSlopeThresholdPercent,
2110
+ previousCenterPrice,
2111
+ newCenterPrice: centerPrice,
2112
+ referencePrice,
2113
+ amaCenterPrice: amaPrice,
2114
+ poolId: ctx.poolId,
2115
+ marketSource,
2116
+ },
2117
+ hookPayload: {
2118
+ thresholdPercent: amaSlopeThresholdPercent,
2119
+ deltaPercent: amaSlopeDeltaPercent,
2120
+ previousAmaSlope,
2121
+ previousGridResetAmaSlope,
2122
+ amaSlope,
2123
+ amaSlopeDeltaPercent,
2124
+ amaSlopeThresholdPercent,
2125
+ previousCenterPrice,
2126
+ newCenterPrice: centerPrice,
2127
+ referencePrice,
2128
+ amaCenterPrice: amaPrice,
2129
+ },
2130
+ dryRunMessage: `[DRY RUN] Would write grid reset trigger for ${bot.botKey} (AMA slope)`,
2131
+ });
2132
+ }
2133
+ }
2134
+ }
2135
+ const acceptedGridCenterPrice = Number(botState.gridCenterPrice ?? botState.centerPrice);
2136
+ const persistedCenterPrice = acceptedGridCenterPrice > 0
2137
+ ? acceptedGridCenterPrice
2138
+ : undefined;
2139
+ // Successful AMA cycles refresh dynamicgrid.json even when no grid reset
2140
+ // is needed. Dynamic-weight payloads still honor the existing whitelist:
2141
+ // buildDynamicGridOptions includes them only for live dynamic weights or
2142
+ // range-scaling diagnostics, and effectiveWeights only advance when live
2143
+ // dynamic weights are explicitly whitelisted.
2144
+ if (!snapshotPersistedThisCycle && !triggered && !triggerSuppressedReason && !isDryRun && !staleData
2145
+ && persistedCenterPrice > 0
2146
+ && typeof deps.writeBotDynamicGrid === 'function') {
2147
+ const dynamicGridPersisted = persistDynamicGridSnapshot(persistedCenterPrice, {
2148
+ amaSlope: amaSlope || previousAmaSlope || null,
2149
+ gridRangeScalingAmaSlope: botState.gridRangeScalingAmaSlope || previousGridResetAmaSlope || null,
2150
+ gridPriceOffsetPct: gridPriceOffsetPlan.gridPriceOffsetPct,
2151
+ });
2152
+ if (dynamicGridPersisted) {
2153
+ botState.amaCenterPrice = amaPrice;
2154
+ botState.amaSlope = amaSlope || previousAmaSlope || null;
2155
+ botState.amaSlopeDeltaPercent = Number.isFinite(amaSlopeDeltaPercent)
2156
+ ? amaSlopeDeltaPercent
2157
+ : botState.amaSlopeDeltaPercent ?? null;
2158
+ botState.amaSlopeThresholdPercent = amaSlopeThresholdPercent;
2159
+ botState.amaSlopePercentMode = AMA_SLOPE_PERCENT_MODE_PER_BAR;
2160
+ if (canApplyDynamicWeights && dynamicWeightsPayload) {
2161
+ botState.effectiveWeights = dynamicWeightsPayload.effectiveWeights || null;
2162
+ }
2163
+ snapshotPersistedThisCycle = true;
2164
+ }
2165
+ else if (!triggerSuppressedReason) {
2166
+ triggerSuppressedReason = canApplyDynamicWeights
2167
+ ? 'dynamic_weight_persist_failed'
2168
+ : 'ama_center_persist_failed';
2169
+ }
2170
+ }
2171
+ if (Number.isFinite(lastClosedCandleTs) && lastClosedCandleTs > 0
2172
+ && (!hasNewClosedCandle || !MarketAdapterService.isRetryableClosedCandleFailure(triggerSuppressedReason))) {
2173
+ consumedClosedCandleTs = lastClosedCandleTs;
2174
+ }
2175
+ const preserveRetryBaseline = hasNewClosedCandle
2176
+ && MarketAdapterService.isRetryableClosedCandleFailure(triggerSuppressedReason);
2177
+ const stateAmaSlope = preserveRetryBaseline
2178
+ ? (botState.amaSlope || previousAmaSlope || null)
2179
+ : (amaSlope || botState.amaSlope || null);
2180
+ const stateAmaSlopeDeltaPercent = preserveRetryBaseline
2181
+ ? (botState.amaSlopeDeltaPercent ?? null)
2182
+ : (Number.isFinite(amaSlopeDeltaPercent)
2183
+ ? amaSlopeDeltaPercent
2184
+ : botState.amaSlopeDeltaPercent ?? null);
2185
+ const stateAmaSlopeThresholdPercent = preserveRetryBaseline
2186
+ ? (botState.amaSlopeThresholdPercent ?? amaSlopeThresholdPercent ?? null)
2187
+ : (amaSlopeThresholdPercent ?? botState.amaSlopeThresholdPercent ?? null);
2188
+ const stateGridRangeScalingAmaSlope = preserveRetryBaseline
2189
+ ? (botState.gridRangeScalingAmaSlope || previousGridResetAmaSlope || null)
2190
+ : (botState.gridRangeScalingAmaSlope || previousGridResetAmaSlope || null);
2191
+ state.bots[bot.botKey] = {
2192
+ ...botState,
2193
+ botName: bot.name,
2194
+ botKey: bot.botKey,
2195
+ poolId: ctx.poolId,
2196
+ marketSource,
2197
+ candleFile: deps.path.relative(deps.root, filePath),
2198
+ candleCount: nextCandles.length,
2199
+ analysisCandleCount: analysisCandles.length,
2200
+ rawKeepCount,
2201
+ analysisKeepCount,
2202
+ amaWarmupBars,
2203
+ kibanaGapRepairCount,
2204
+ kibanaBackfillCount,
2205
+ unresolvedGapCount,
2206
+ nativeRecentTradeSequences,
2207
+ nativeLastTradeTs,
2208
+ nativeOverlapCount,
2209
+ nativePagesFetched,
2210
+ lastCandleTs: rawLastCandleTs,
2211
+ rawLastCandleTs,
2212
+ lastClosedCandleTs: consumedClosedCandleTs,
2213
+ lastAmaPrice: amaPrice,
2214
+ amaCenterPrice: Number(botState.amaCenterPrice || 0) > 0
2215
+ ? Number(botState.amaCenterPrice)
2216
+ : undefined,
2217
+ gridCenterPrice: acceptedGridCenterPrice > 0
2218
+ ? acceptedGridCenterPrice
2219
+ : persistedCenterPrice,
2220
+ centerPrice: acceptedGridCenterPrice > 0
2221
+ ? acceptedGridCenterPrice
2222
+ : persistedCenterPrice,
2223
+ amaConfig: {
2224
+ erPeriod: botAma.erPeriod,
2225
+ fastPeriod: botAma.fastPeriod,
2226
+ slowPeriod: botAma.slowPeriod,
2227
+ erSmoothPeriod: botAma.erSmoothPeriod ?? 0,
2228
+ },
2229
+ amaComparison,
2230
+ lastDeltaPercent: deltaPercent,
2231
+ thresholdPercent: botThreshold,
2232
+ referencePrice,
2233
+ lastCycleSource: sourceLabel,
2234
+ lastCycleAt: nowIso,
2235
+ staleData,
2236
+ staleAgeHours,
2237
+ lastTriggerFile: triggerPath || botState.lastTriggerFile || null,
2238
+ lastTriggerSuppressedReason: triggerSuppressedReason || null,
2239
+ weights: canExposeDynamicWeights ? weights : null,
2240
+ collateralRecommendation,
2241
+ atr,
2242
+ weightVariance,
2243
+ amaSlope: stateAmaSlope,
2244
+ gridRangeScalingAmaSlope: stateGridRangeScalingAmaSlope,
2245
+ amaSlopeDeltaPercent: stateAmaSlopeDeltaPercent,
2246
+ amaSlopeThresholdPercent: stateAmaSlopeThresholdPercent,
2247
+ amaSlopePercentMode: AMA_SLOPE_PERCENT_MODE_PER_BAR,
2248
+ effectiveWeights: (canApplyDynamicWeights && (snapshotPersistedThisCycle || isDryRun) && dynamicWeightsPayload?.effectiveWeights)
2249
+ ? dynamicWeightsPayload.effectiveWeights
2250
+ : (botState.effectiveWeights || null),
2251
+ dynamicWeightWhitelisted: isDynamicWeightWhitelisted,
2252
+ gridRangeScalingWhitelisted: isGridRangeScalingWhitelisted,
2253
+ dynamicWeightReady: canExposeDynamicWeights ? (dynamicWeightsPayload?.isReady ?? false) : false,
2254
+ dynamicWeightProfile: canExposeDynamicWeights ? (weights?.profile || null) : null,
2255
+ dynamicWeightApplied: snapshotPersistedThisCycle && canApplyDynamicWeights,
2256
+ hasExplicitBaseWeights,
2257
+ pendingClosedCandle: false,
2258
+ };
2259
+ return {
2260
+ ok: true,
2261
+ dryRunMessages,
2262
+ source: sourceLabel,
2263
+ marketSource,
2264
+ intervalSeconds: cfg.intervalSeconds,
2265
+ candleCount: nextCandles.length,
2266
+ analysisCandleCount: analysisCandles.length,
2267
+ rawKeepCount,
2268
+ analysisKeepCount,
2269
+ amaWarmupBars,
2270
+ kibanaGapRepairCount,
2271
+ kibanaBackfillCount,
2272
+ unresolvedGapCount,
2273
+ nativeRecentTradeSequences,
2274
+ nativeLastTradeTs,
2275
+ nativeOverlapCount,
2276
+ nativePagesFetched,
2277
+ amaPrice,
2278
+ previousCenterPrice,
2279
+ deltaPercent,
2280
+ thresholdPercent: botThreshold,
2281
+ referencePrice,
2282
+ amaComparison,
2283
+ triggered,
2284
+ triggerPath,
2285
+ staleData,
2286
+ staleAgeHours,
2287
+ triggerCallbackError,
2288
+ triggerSuppressedReason,
2289
+ weights: canExposeDynamicWeights ? weights : null,
2290
+ collateralRecommendation,
2291
+ amaSlope: amaSlope || botState.amaSlope || null,
2292
+ amaSlopeDeltaPercent: Number.isFinite(amaSlopeDeltaPercent)
2293
+ ? amaSlopeDeltaPercent
2294
+ : botState.amaSlopeDeltaPercent ?? null,
2295
+ amaSlopeThresholdPercent: amaSlopeThresholdPercent ?? botState.amaSlopeThresholdPercent ?? null,
2296
+ dynamicWeightWhitelisted: isDynamicWeightWhitelisted,
2297
+ gridRangeScalingWhitelisted: isGridRangeScalingWhitelisted,
2298
+ dynamicWeightReady: canExposeDynamicWeights ? (dynamicWeightsPayload?.isReady ?? false) : false,
2299
+ dynamicWeightProfile: canExposeDynamicWeights ? (weights?.profile || null) : null,
2300
+ dynamicWeightApplied: snapshotPersistedThisCycle && canApplyDynamicWeights,
2301
+ hasExplicitBaseWeights,
2302
+ poolId: ctx.poolId,
2303
+ candleFile: deps.path.relative(deps.root, filePath),
2304
+ lastCandleTs: rawLastCandleTs,
2305
+ rawLastCandleTs,
2306
+ lastClosedCandleTs: consumedClosedCandleTs,
2307
+ lastClosedCandleClose: Number(latestClosedCandle?.[4]),
2308
+ centerPrice,
2309
+ amaConfig: {
2310
+ erPeriod: botAma.erPeriod,
2311
+ fastPeriod: botAma.fastPeriod,
2312
+ slowPeriod: botAma.slowPeriod,
2313
+ erSmoothPeriod: botAma.erSmoothPeriod ?? 0,
2314
+ },
2315
+ atr,
2316
+ weightVariance,
2317
+ pendingClosedCandle: false,
2318
+ };
2319
+ }
2320
+ }
2321
+ module.exports = {
2322
+ MarketAdapterService,
2323
+ AMA_SLOPE_PERCENT_MODE_PER_BAR,
2324
+ normalizeAmaSlopePercentMode,
2325
+ normalizeAmaSlopeLookbackBars,
2326
+ convertSlopePercentToPerBar,
2327
+ };
2328
+ //# sourceMappingURL=market_adapter_service.js.map