dexbot 1.4.21 → 1.4.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (601) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +8 -5
  3. package/analysis/ama_fitting/analyze_ama_price_changes.ts +7 -3
  4. package/analysis/ama_fitting/analyze_lambda_vs_slow.ts +6 -6
  5. package/analysis/ama_fitting/calibrate_convergence_er.ts +7 -5
  6. package/analysis/ama_fitting/fetch_lp_candles.ts +8 -3
  7. package/analysis/ama_fitting/generate_unified_comparison_chart.ts +30 -67
  8. package/analysis/ama_fitting/optimizer_high_resolution.ts +26 -17
  9. package/analysis/ama_fitting/package.json +1 -1
  10. package/analysis/analyze_derivatives.ts +4 -4
  11. package/analysis/analyze_dynamic_weight.ts +21 -5
  12. package/analysis/analyze_kalman.ts +54 -25
  13. package/analysis/analyze_regime.ts +2 -2
  14. package/analysis/analyze_regime_windows.ts +27 -19
  15. package/analysis/analyze_risk_profile.ts +3 -3
  16. package/analysis/analyze_trade_heatmap.ts +3 -3
  17. package/analysis/analyze_volatility.ts +2 -2
  18. package/analysis/bot_fitting/README.md +93 -19
  19. package/analysis/bot_fitting/backtest_ama_sweep.ts +316 -199
  20. package/analysis/bot_fitting/backtest_bot_fitting.ts +520 -85
  21. package/analysis/bot_fitting/shared_utils.ts +15 -9
  22. package/analysis/bot_key_utils.ts +13 -6
  23. package/analysis/bot_usage/discover_bot_accounts.ts +5 -3
  24. package/analysis/bot_usage/kibana_bot_queries.ts +1 -1
  25. package/analysis/chart_css.ts +6 -4
  26. package/analysis/chart_ui.ts +0 -1
  27. package/analysis/chart_utils.ts +11 -2
  28. package/analysis/derivative_chart_generator.ts +2 -2
  29. package/analysis/price_sources.ts +8 -2
  30. package/analysis/resolve_source.ts +1 -1
  31. package/analysis/trade_profitability.ts +61 -28
  32. package/analysis/tradingview/README.md +24 -4
  33. package/analysis/tradingview/analyze_tradingview.ts +1 -1
  34. package/analysis/tradingview/tradingview_uplot_chart_generator.ts +356 -86
  35. package/analysis/trend_detection/DYNAMIC_WEIGHT_RESEARCH.md +1 -1
  36. package/analysis/trend_detection/derivative_analyzer.ts +12 -3
  37. package/analysis/trend_detection/dynamic_weight_chart_generator.ts +25 -27
  38. package/analysis/trend_detection/hurst_analyzer.ts +1 -1
  39. package/analysis/trend_detection/kalman_chart_generator.ts +42 -14
  40. package/analysis/trend_detection/package.json +1 -1
  41. package/analysis/trend_detection/regime_chart_generator.ts +39 -19
  42. package/analysis/trend_detection/tests/test_kalman_trend.ts +1 -1
  43. package/analysis/trend_detection/tests/test_kalman_velocity_smoothing.ts +1 -1
  44. package/analysis/trend_detection/volatility_chart_generator.ts +1 -1
  45. package/claw/index.ts +1 -1
  46. package/claw/modules/claw_bridge.ts +3 -3
  47. package/claw/modules/claw_catalog.ts +1 -1
  48. package/claw/modules/claw_launcher.ts +1 -1
  49. package/claw/modules/claw_skill_md.ts +7 -10
  50. package/claw/modules/credit_runtime_adapter.ts +1 -1
  51. package/claw/modules/decision_loop.ts +5 -9
  52. package/claw/modules/dexbot_bridge.ts +7 -7
  53. package/claw/modules/feed_price_source.ts +1 -1
  54. package/claw/modules/kibana_price_source.ts +1 -1
  55. package/claw/modules/launcher_mode_detector.ts +1 -1
  56. package/claw/modules/launcher_paths.ts +1 -1
  57. package/claw/modules/position_discovery.ts +1 -1
  58. package/claw/modules/position_health.ts +1 -1
  59. package/claw/package.json +3 -3
  60. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  61. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  62. package/claw/skills/launcher-ops/references/launcher-workflow.md +2 -2
  63. package/claw/tests/package.json +1 -1
  64. package/claw/tests/test_claw_bridge.ts +45 -21
  65. package/claw/tests/test_claw_catalog_and_credentials.ts +8 -4
  66. package/claw/tests/test_claw_chain_layer.ts +39 -19
  67. package/claw/tests/test_claw_data_flow.ts +28 -20
  68. package/claw/tests/test_claw_domain_logic.ts +25 -19
  69. package/claw/tests/test_claw_manifest_and_matrix.ts +23 -3
  70. package/claw/tests/test_claw_mcp_transport.ts +13 -8
  71. package/claw/tests/test_claw_regressions.ts +254 -128
  72. package/claw/tests/test_claw_skill_generation.ts +1 -1
  73. package/claw/tests/test_nullclaw_tmp_integration.ts +2 -3
  74. package/claw/tests/test_position_health.ts +1 -77
  75. package/claw/tests/test_position_manager.ts +20 -18
  76. package/claw/tests/test_position_manager_watch_health.ts +39 -43
  77. package/claw/tests/test_short_mpa_strategy.ts +20 -17
  78. package/claw/tsconfig.json +4 -3
  79. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js +7 -3
  80. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js.map +1 -1
  81. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +6 -6
  82. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -1
  83. package/dist/analysis/ama_fitting/calibrate_convergence_er.js +7 -5
  84. package/dist/analysis/ama_fitting/calibrate_convergence_er.js.map +1 -1
  85. package/dist/analysis/ama_fitting/fetch_lp_candles.js +8 -3
  86. package/dist/analysis/ama_fitting/fetch_lp_candles.js.map +1 -1
  87. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts +2 -2
  88. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts.map +1 -1
  89. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js +31 -75
  90. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js.map +1 -1
  91. package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts.map +1 -1
  92. package/dist/analysis/ama_fitting/optimizer_high_resolution.js +25 -17
  93. package/dist/analysis/ama_fitting/optimizer_high_resolution.js.map +1 -1
  94. package/dist/analysis/analyze_derivatives.d.ts +0 -18
  95. package/dist/analysis/analyze_derivatives.d.ts.map +1 -1
  96. package/dist/analysis/analyze_derivatives.js +4 -4
  97. package/dist/analysis/analyze_derivatives.js.map +1 -1
  98. package/dist/analysis/analyze_dynamic_weight.d.ts +0 -11
  99. package/dist/analysis/analyze_dynamic_weight.d.ts.map +1 -1
  100. package/dist/analysis/analyze_dynamic_weight.js +19 -5
  101. package/dist/analysis/analyze_dynamic_weight.js.map +1 -1
  102. package/dist/analysis/analyze_kalman.d.ts +0 -10
  103. package/dist/analysis/analyze_kalman.d.ts.map +1 -1
  104. package/dist/analysis/analyze_kalman.js +51 -26
  105. package/dist/analysis/analyze_kalman.js.map +1 -1
  106. package/dist/analysis/analyze_regime.d.ts +0 -17
  107. package/dist/analysis/analyze_regime.d.ts.map +1 -1
  108. package/dist/analysis/analyze_regime.js +2 -2
  109. package/dist/analysis/analyze_regime.js.map +1 -1
  110. package/dist/analysis/analyze_regime_windows.d.ts +0 -16
  111. package/dist/analysis/analyze_regime_windows.d.ts.map +1 -1
  112. package/dist/analysis/analyze_regime_windows.js +28 -21
  113. package/dist/analysis/analyze_regime_windows.js.map +1 -1
  114. package/dist/analysis/analyze_risk_profile.js +3 -3
  115. package/dist/analysis/analyze_risk_profile.js.map +1 -1
  116. package/dist/analysis/analyze_trade_heatmap.js +3 -3
  117. package/dist/analysis/analyze_trade_heatmap.js.map +1 -1
  118. package/dist/analysis/analyze_volatility.d.ts +0 -19
  119. package/dist/analysis/analyze_volatility.d.ts.map +1 -1
  120. package/dist/analysis/analyze_volatility.js +2 -2
  121. package/dist/analysis/analyze_volatility.js.map +1 -1
  122. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts +30 -14
  123. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts.map +1 -1
  124. package/dist/analysis/bot_fitting/backtest_ama_sweep.js +330 -193
  125. package/dist/analysis/bot_fitting/backtest_ama_sweep.js.map +1 -1
  126. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts +136 -1
  127. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts.map +1 -1
  128. package/dist/analysis/bot_fitting/backtest_bot_fitting.js +527 -83
  129. package/dist/analysis/bot_fitting/backtest_bot_fitting.js.map +1 -1
  130. package/dist/analysis/bot_fitting/shared_utils.d.ts +1 -1
  131. package/dist/analysis/bot_fitting/shared_utils.d.ts.map +1 -1
  132. package/dist/analysis/bot_fitting/shared_utils.js +14 -8
  133. package/dist/analysis/bot_fitting/shared_utils.js.map +1 -1
  134. package/dist/analysis/bot_key_utils.d.ts.map +1 -1
  135. package/dist/analysis/bot_key_utils.js +13 -6
  136. package/dist/analysis/bot_key_utils.js.map +1 -1
  137. package/dist/analysis/bot_usage/discover_bot_accounts.js +5 -3
  138. package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -1
  139. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
  140. package/dist/analysis/bot_usage/kibana_bot_queries.js +1 -1
  141. package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
  142. package/dist/analysis/chart_css.d.ts +3 -1
  143. package/dist/analysis/chart_css.d.ts.map +1 -1
  144. package/dist/analysis/chart_css.js +6 -3
  145. package/dist/analysis/chart_css.js.map +1 -1
  146. package/dist/analysis/chart_ui.d.ts.map +1 -1
  147. package/dist/analysis/chart_ui.js.map +1 -1
  148. package/dist/analysis/chart_utils.d.ts.map +1 -1
  149. package/dist/analysis/chart_utils.js +15 -2
  150. package/dist/analysis/chart_utils.js.map +1 -1
  151. package/dist/analysis/derivative_chart_generator.js +2 -2
  152. package/dist/analysis/derivative_chart_generator.js.map +1 -1
  153. package/dist/analysis/price_sources.d.ts +1 -0
  154. package/dist/analysis/price_sources.d.ts.map +1 -1
  155. package/dist/analysis/price_sources.js +8 -2
  156. package/dist/analysis/price_sources.js.map +1 -1
  157. package/dist/analysis/resolve_source.d.ts.map +1 -1
  158. package/dist/analysis/resolve_source.js +1 -1
  159. package/dist/analysis/resolve_source.js.map +1 -1
  160. package/dist/analysis/trade_profitability.d.ts.map +1 -1
  161. package/dist/analysis/trade_profitability.js +58 -29
  162. package/dist/analysis/trade_profitability.js.map +1 -1
  163. package/dist/analysis/tradingview/analyze_tradingview.js +1 -1
  164. package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -1
  165. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -1
  166. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +356 -86
  167. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -1
  168. package/dist/analysis/trend_detection/derivative_analyzer.d.ts +1 -0
  169. package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +1 -1
  170. package/dist/analysis/trend_detection/derivative_analyzer.js +12 -3
  171. package/dist/analysis/trend_detection/derivative_analyzer.js.map +1 -1
  172. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -1
  173. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +26 -27
  174. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -1
  175. package/dist/analysis/trend_detection/hurst_analyzer.d.ts +1 -1
  176. package/dist/analysis/trend_detection/hurst_analyzer.d.ts.map +1 -1
  177. package/dist/analysis/trend_detection/hurst_analyzer.js +1 -1
  178. package/dist/analysis/trend_detection/hurst_analyzer.js.map +1 -1
  179. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -1
  180. package/dist/analysis/trend_detection/kalman_chart_generator.js +41 -13
  181. package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -1
  182. package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -1
  183. package/dist/analysis/trend_detection/regime_chart_generator.js +38 -19
  184. package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -1
  185. package/dist/analysis/trend_detection/tests/test_kalman_trend.js +1 -1
  186. package/dist/analysis/trend_detection/tests/test_kalman_trend.js.map +1 -1
  187. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js +1 -1
  188. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js.map +1 -1
  189. package/dist/analysis/trend_detection/volatility_chart_generator.js +1 -1
  190. package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -1
  191. package/dist/bot.js +1 -1
  192. package/dist/bot.js.map +1 -1
  193. package/dist/credential-daemon.d.ts +1 -1
  194. package/dist/credential-daemon.js +1 -1
  195. package/dist/dexbot.d.ts.map +1 -1
  196. package/dist/dexbot.js +7 -8
  197. package/dist/dexbot.js.map +1 -1
  198. package/dist/market_adapter/ama_signal_runner.js +3 -1
  199. package/dist/market_adapter/ama_signal_runner.js.map +1 -1
  200. package/dist/market_adapter/candle_utils.d.ts +1 -3
  201. package/dist/market_adapter/candle_utils.d.ts.map +1 -1
  202. package/dist/market_adapter/candle_utils.js +1 -11
  203. package/dist/market_adapter/candle_utils.js.map +1 -1
  204. package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -1
  205. package/dist/market_adapter/core/asymmetric_bounds.js +33 -30
  206. package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -1
  207. package/dist/market_adapter/core/config_normalizers.d.ts.map +1 -1
  208. package/dist/market_adapter/core/config_normalizers.js +10 -1
  209. package/dist/market_adapter/core/config_normalizers.js.map +1 -1
  210. package/dist/market_adapter/core/kibana_candles.d.ts +18 -42
  211. package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -1
  212. package/dist/market_adapter/core/kibana_candles.js +101 -7
  213. package/dist/market_adapter/core/kibana_candles.js.map +1 -1
  214. package/dist/market_adapter/core/kibana_client.d.ts +0 -14
  215. package/dist/market_adapter/core/kibana_client.d.ts.map +1 -1
  216. package/dist/market_adapter/core/kibana_client.js +39 -6
  217. package/dist/market_adapter/core/kibana_client.js.map +1 -1
  218. package/dist/market_adapter/core/kibana_market_candles.d.ts +0 -27
  219. package/dist/market_adapter/core/kibana_market_candles.d.ts.map +1 -1
  220. package/dist/market_adapter/core/kibana_market_candles.js +1 -1
  221. package/dist/market_adapter/core/kibana_market_candles.js.map +1 -1
  222. package/dist/market_adapter/core/market_adapter_service.d.ts +21 -11
  223. package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -1
  224. package/dist/market_adapter/core/market_adapter_service.js +95 -37
  225. package/dist/market_adapter/core/market_adapter_service.js.map +1 -1
  226. package/dist/market_adapter/core/signals/hurst_analyzer.d.ts +10 -1
  227. package/dist/market_adapter/core/signals/hurst_analyzer.d.ts.map +1 -1
  228. package/dist/market_adapter/core/signals/hurst_analyzer.js +28 -17
  229. package/dist/market_adapter/core/signals/hurst_analyzer.js.map +1 -1
  230. package/dist/market_adapter/core/signals/kalman_trend_analyzer.d.ts +5 -0
  231. package/dist/market_adapter/core/signals/kalman_trend_analyzer.d.ts.map +1 -1
  232. package/dist/market_adapter/core/signals/kalman_trend_analyzer.js +24 -24
  233. package/dist/market_adapter/core/signals/kalman_trend_analyzer.js.map +1 -1
  234. package/dist/market_adapter/core/signals/kalman_velocity_smoothing.d.ts.map +1 -1
  235. package/dist/market_adapter/core/signals/kalman_velocity_smoothing.js +5 -1
  236. package/dist/market_adapter/core/signals/kalman_velocity_smoothing.js.map +1 -1
  237. package/dist/market_adapter/core/signals/permutation_entropy_analyzer.d.ts.map +1 -1
  238. package/dist/market_adapter/core/signals/permutation_entropy_analyzer.js +20 -3
  239. package/dist/market_adapter/core/signals/permutation_entropy_analyzer.js.map +1 -1
  240. package/dist/market_adapter/core/strategies/ama.js +1 -1
  241. package/dist/market_adapter/core/strategies/ama.js.map +1 -1
  242. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts +2 -2
  243. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts.map +1 -1
  244. package/dist/market_adapter/core/strategies/ama_slope_model.js +16 -4
  245. package/dist/market_adapter/core/strategies/ama_slope_model.js.map +1 -1
  246. package/dist/market_adapter/core/strategies/atr/calculator.d.ts +4 -3
  247. package/dist/market_adapter/core/strategies/atr/calculator.d.ts.map +1 -1
  248. package/dist/market_adapter/core/strategies/atr/calculator.js +16 -8
  249. package/dist/market_adapter/core/strategies/atr/calculator.js.map +1 -1
  250. package/dist/market_adapter/core/strategies/collateral_manager.d.ts.map +1 -1
  251. package/dist/market_adapter/core/strategies/collateral_manager.js +8 -3
  252. package/dist/market_adapter/core/strategies/collateral_manager.js.map +1 -1
  253. package/dist/market_adapter/core/strategies/dynamic_weight_series.d.ts +40 -1
  254. package/dist/market_adapter/core/strategies/dynamic_weight_series.d.ts.map +1 -1
  255. package/dist/market_adapter/core/strategies/dynamic_weight_series.js +116 -2
  256. package/dist/market_adapter/core/strategies/dynamic_weight_series.js.map +1 -1
  257. package/dist/market_adapter/core/strategies/regime_gate.d.ts.map +1 -1
  258. package/dist/market_adapter/core/strategies/regime_gate.js +26 -20
  259. package/dist/market_adapter/core/strategies/regime_gate.js.map +1 -1
  260. package/dist/market_adapter/core/strategies/volatility_shift.d.ts.map +1 -1
  261. package/dist/market_adapter/core/strategies/volatility_shift.js +3 -0
  262. package/dist/market_adapter/core/strategies/volatility_shift.js.map +1 -1
  263. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +69 -55
  264. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -1
  265. package/dist/market_adapter/inputs/fetch_lp_data.d.ts +0 -26
  266. package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
  267. package/dist/market_adapter/inputs/fetch_lp_data.js +67 -14
  268. package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
  269. package/dist/market_adapter/inputs/kibana_source.d.ts +4 -28
  270. package/dist/market_adapter/inputs/kibana_source.d.ts.map +1 -1
  271. package/dist/market_adapter/inputs/kibana_source.js +5 -2
  272. package/dist/market_adapter/inputs/kibana_source.js.map +1 -1
  273. package/dist/market_adapter/lp_chart_core.js +1 -1
  274. package/dist/market_adapter/lp_chart_core.js.map +1 -1
  275. package/dist/market_adapter/lp_chart_runner.d.ts +10 -1
  276. package/dist/market_adapter/lp_chart_runner.d.ts.map +1 -1
  277. package/dist/market_adapter/lp_chart_runner.js +2 -2
  278. package/dist/market_adapter/lp_chart_runner.js.map +1 -1
  279. package/dist/market_adapter/lp_chart_strategy_loader.js +1 -1
  280. package/dist/market_adapter/lp_chart_strategy_loader.js.map +1 -1
  281. package/dist/market_adapter/market_adapter.d.ts +1 -2
  282. package/dist/market_adapter/market_adapter.d.ts.map +1 -1
  283. package/dist/market_adapter/market_adapter.js +9 -8
  284. package/dist/market_adapter/market_adapter.js.map +1 -1
  285. package/dist/market_adapter/test_helpers.d.ts +3 -3
  286. package/dist/market_adapter/test_helpers.d.ts.map +1 -1
  287. package/dist/market_adapter/test_helpers.js +3 -3
  288. package/dist/market_adapter/test_helpers.js.map +1 -1
  289. package/dist/market_adapter/utils/adapter_client.js +1 -1
  290. package/dist/market_adapter/utils/adapter_client.js.map +1 -1
  291. package/dist/market_adapter/utils/atomic_write.js +1 -1
  292. package/dist/market_adapter/utils/atomic_write.js.map +1 -1
  293. package/dist/market_adapter/utils/chain.d.ts +0 -2
  294. package/dist/market_adapter/utils/chain.d.ts.map +1 -1
  295. package/dist/market_adapter/utils/chain.js +2 -3
  296. package/dist/market_adapter/utils/chain.js.map +1 -1
  297. package/dist/market_adapter/utils/data_discovery.d.ts.map +1 -1
  298. package/dist/market_adapter/utils/data_discovery.js +24 -8
  299. package/dist/market_adapter/utils/data_discovery.js.map +1 -1
  300. package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts.map +1 -1
  301. package/dist/market_adapter/utils/dynamic_grid_snapshot.js +2 -5
  302. package/dist/market_adapter/utils/dynamic_grid_snapshot.js.map +1 -1
  303. package/dist/market_adapter/utils/file_lock.d.ts +4 -1
  304. package/dist/market_adapter/utils/file_lock.d.ts.map +1 -1
  305. package/dist/market_adapter/utils/file_lock.js +89 -25
  306. package/dist/market_adapter/utils/file_lock.js.map +1 -1
  307. package/dist/market_adapter/utils/native_history.d.ts.map +1 -1
  308. package/dist/market_adapter/utils/native_history.js +5 -1
  309. package/dist/market_adapter/utils/native_history.js.map +1 -1
  310. package/dist/modules/account_bots.d.ts.map +1 -1
  311. package/dist/modules/account_bots.js +47 -12
  312. package/dist/modules/account_bots.js.map +1 -1
  313. package/dist/modules/authority_resolver.js +1 -1
  314. package/dist/modules/authority_resolver.js.map +1 -1
  315. package/dist/modules/bitshares-native/chain_client.js +1 -1
  316. package/dist/modules/bitshares-native/chain_client.js.map +1 -1
  317. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts +0 -12
  318. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -1
  319. package/dist/modules/bitshares-native/crypto/ecc.browser.js +1 -1
  320. package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -1
  321. package/dist/modules/bitshares-native/crypto/ecc_selector.js +1 -1
  322. package/dist/modules/bitshares-native/crypto/ecc_selector.js.map +1 -1
  323. package/dist/modules/bitshares-native/index.d.ts.map +1 -1
  324. package/dist/modules/bitshares-native/index.js +1 -1
  325. package/dist/modules/bitshares-native/index.js.map +1 -1
  326. package/dist/modules/bitshares-native/lru_cache.js +1 -1
  327. package/dist/modules/bitshares-native/lru_cache.js.map +1 -1
  328. package/dist/modules/bitshares-native/resolvers.d.ts +0 -2
  329. package/dist/modules/bitshares-native/resolvers.d.ts.map +1 -1
  330. package/dist/modules/bitshares-native/resolvers.js +18 -4
  331. package/dist/modules/bitshares-native/resolvers.js.map +1 -1
  332. package/dist/modules/bitshares-native/serial/chain_constants.js +1 -1
  333. package/dist/modules/bitshares-native/serial/chain_constants.js.map +1 -1
  334. package/dist/modules/bitshares-native/serial/index.d.ts.map +1 -1
  335. package/dist/modules/bitshares-native/serial/index.js +1 -1
  336. package/dist/modules/bitshares-native/serial/index.js.map +1 -1
  337. package/dist/modules/bitshares-native/serial/operations.js +1 -1
  338. package/dist/modules/bitshares-native/serial/operations.js.map +1 -1
  339. package/dist/modules/bitshares-native/serial/serializer.js +1 -1
  340. package/dist/modules/bitshares-native/serial/serializer.js.map +1 -1
  341. package/dist/modules/bitshares-native/serial/types.js +1 -1
  342. package/dist/modules/bitshares-native/serial/types.js.map +1 -1
  343. package/dist/modules/bitshares-native/signing_client.js +2 -2
  344. package/dist/modules/bitshares-native/signing_client.js.map +1 -1
  345. package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
  346. package/dist/modules/bitshares-native/subscriptions.js +5 -14
  347. package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
  348. package/dist/modules/bitshares-native/transport.js +1 -1
  349. package/dist/modules/bitshares-native/transport.js.map +1 -1
  350. package/dist/modules/bitshares-native/tx/builder.js +1 -1
  351. package/dist/modules/bitshares-native/tx/builder.js.map +1 -1
  352. package/dist/modules/bitshares-native/tx/tx_cache.d.ts +3 -2
  353. package/dist/modules/bitshares-native/tx/tx_cache.d.ts.map +1 -1
  354. package/dist/modules/bitshares-native/tx/tx_cache.js +7 -4
  355. package/dist/modules/bitshares-native/tx/tx_cache.js.map +1 -1
  356. package/dist/modules/bitshares_client.js +10 -10
  357. package/dist/modules/bitshares_client.js.map +1 -1
  358. package/dist/modules/bot_settings.d.ts.map +1 -1
  359. package/dist/modules/bot_settings.js +11 -18
  360. package/dist/modules/bot_settings.js.map +1 -1
  361. package/dist/modules/bots_file_lock.d.ts +1 -1
  362. package/dist/modules/bots_file_lock.js +1 -1
  363. package/dist/modules/broadcast_failure.d.ts +13 -0
  364. package/dist/modules/broadcast_failure.d.ts.map +1 -1
  365. package/dist/modules/broadcast_failure.js +0 -13
  366. package/dist/modules/broadcast_failure.js.map +1 -1
  367. package/dist/modules/chain_orders.d.ts.map +1 -1
  368. package/dist/modules/chain_orders.js +15 -17
  369. package/dist/modules/chain_orders.js.map +1 -1
  370. package/dist/modules/config.js +1 -1
  371. package/dist/modules/config.js.map +1 -1
  372. package/dist/modules/constants.d.ts +21 -20
  373. package/dist/modules/constants.d.ts.map +1 -1
  374. package/dist/modules/constants.js +60 -63
  375. package/dist/modules/constants.js.map +1 -1
  376. package/dist/modules/cr_planner.d.ts +1 -7
  377. package/dist/modules/cr_planner.d.ts.map +1 -1
  378. package/dist/modules/cr_planner.js +3 -77
  379. package/dist/modules/cr_planner.js.map +1 -1
  380. package/dist/modules/credential_policy.d.ts +0 -4
  381. package/dist/modules/credential_policy.d.ts.map +1 -1
  382. package/dist/modules/credential_policy.js +17 -2
  383. package/dist/modules/credential_policy.js.map +1 -1
  384. package/dist/modules/credit_runtime.d.ts +0 -7
  385. package/dist/modules/credit_runtime.d.ts.map +1 -1
  386. package/dist/modules/credit_runtime.js +21 -60
  387. package/dist/modules/credit_runtime.js.map +1 -1
  388. package/dist/modules/crypto/index.d.ts +0 -2
  389. package/dist/modules/crypto/index.d.ts.map +1 -1
  390. package/dist/modules/crypto/index.js +0 -2
  391. package/dist/modules/crypto/index.js.map +1 -1
  392. package/dist/modules/crypto/sync.js.map +1 -1
  393. package/dist/modules/daemon_node_health.js +1 -1
  394. package/dist/modules/daemon_node_health.js.map +1 -1
  395. package/dist/modules/dexbot_class.d.ts +8 -134
  396. package/dist/modules/dexbot_class.d.ts.map +1 -1
  397. package/dist/modules/dexbot_class.js +36 -197
  398. package/dist/modules/dexbot_class.js.map +1 -1
  399. package/dist/modules/dexbot_credential_client.js +1 -1
  400. package/dist/modules/dexbot_credential_client.js.map +1 -1
  401. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
  402. package/dist/modules/dexbot_fill_runtime.js +3 -6
  403. package/dist/modules/dexbot_fill_runtime.js.map +1 -1
  404. package/dist/modules/dexbot_maintenance_runtime.d.ts +6 -34
  405. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  406. package/dist/modules/dexbot_maintenance_runtime.js +27 -19
  407. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  408. package/dist/modules/dexbot_startup_runtime.d.ts.map +1 -1
  409. package/dist/modules/dexbot_startup_runtime.js +31 -15
  410. package/dist/modules/dexbot_startup_runtime.js.map +1 -1
  411. package/dist/modules/dexbot_state_recovery.d.ts +2 -2
  412. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  413. package/dist/modules/dexbot_state_recovery.js +9 -6
  414. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  415. package/dist/modules/fund_registry.d.ts.map +1 -1
  416. package/dist/modules/fund_registry.js +19 -4
  417. package/dist/modules/fund_registry.js.map +1 -1
  418. package/dist/modules/general_settings.d.ts +1 -2
  419. package/dist/modules/general_settings.d.ts.map +1 -1
  420. package/dist/modules/general_settings.js +1 -2
  421. package/dist/modules/general_settings.js.map +1 -1
  422. package/dist/modules/graceful_shutdown.d.ts.map +1 -1
  423. package/dist/modules/graceful_shutdown.js +10 -1
  424. package/dist/modules/graceful_shutdown.js.map +1 -1
  425. package/dist/modules/grid_price_source.d.ts +19 -0
  426. package/dist/modules/grid_price_source.d.ts.map +1 -0
  427. package/dist/modules/grid_price_source.js +23 -0
  428. package/dist/modules/grid_price_source.js.map +1 -0
  429. package/dist/modules/key_store.d.ts +0 -5
  430. package/dist/modules/key_store.d.ts.map +1 -1
  431. package/dist/modules/key_store.js +1 -14
  432. package/dist/modules/key_store.js.map +1 -1
  433. package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
  434. package/dist/modules/launcher/bot_supervisor.js +9 -19
  435. package/dist/modules/launcher/bot_supervisor.js.map +1 -1
  436. package/dist/modules/launcher/child_env.d.ts +1 -2
  437. package/dist/modules/launcher/child_env.d.ts.map +1 -1
  438. package/dist/modules/launcher/child_env.js +1 -1
  439. package/dist/modules/launcher/child_env.js.map +1 -1
  440. package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -1
  441. package/dist/modules/launcher/foreign_cred_daemon.js +2 -14
  442. package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -1
  443. package/dist/modules/launcher/market_adapter_runtime.d.ts +1 -1
  444. package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -1
  445. package/dist/modules/launcher/market_adapter_runtime.js +3 -14
  446. package/dist/modules/launcher/market_adapter_runtime.js.map +1 -1
  447. package/dist/modules/launcher/market_adapter_watchdog.d.ts.map +1 -1
  448. package/dist/modules/launcher/market_adapter_watchdog.js +3 -2
  449. package/dist/modules/launcher/market_adapter_watchdog.js.map +1 -1
  450. package/dist/modules/launcher/monolithic_runtime.d.ts +0 -1
  451. package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
  452. package/dist/modules/launcher/monolithic_runtime.js +3 -18
  453. package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
  454. package/dist/modules/launcher/runtime_entry.d.ts.map +1 -1
  455. package/dist/modules/launcher/runtime_entry.js +4 -5
  456. package/dist/modules/launcher/runtime_entry.js.map +1 -1
  457. package/dist/modules/launcher/status_reporting.js +1 -1
  458. package/dist/modules/launcher/status_reporting.js.map +1 -1
  459. package/dist/modules/launcher/supervisor_control.js +1 -1
  460. package/dist/modules/launcher/supervisor_control.js.map +1 -1
  461. package/dist/modules/market_adapter_whitelist.d.ts +2 -2
  462. package/dist/modules/market_adapter_whitelist.d.ts.map +1 -1
  463. package/dist/modules/market_adapter_whitelist.js +12 -6
  464. package/dist/modules/market_adapter_whitelist.js.map +1 -1
  465. package/dist/modules/node_failure_ledger.d.ts +23 -0
  466. package/dist/modules/node_failure_ledger.d.ts.map +1 -1
  467. package/dist/modules/node_failure_ledger.js +0 -23
  468. package/dist/modules/node_failure_ledger.js.map +1 -1
  469. package/dist/modules/node_manager.d.ts.map +1 -1
  470. package/dist/modules/node_manager.js +3 -3
  471. package/dist/modules/node_manager.js.map +1 -1
  472. package/dist/modules/order/accounting.js +1 -1
  473. package/dist/modules/order/accounting.js.map +1 -1
  474. package/dist/modules/order/async_lock.d.ts +6 -0
  475. package/dist/modules/order/async_lock.d.ts.map +1 -1
  476. package/dist/modules/order/async_lock.js +11 -0
  477. package/dist/modules/order/async_lock.js.map +1 -1
  478. package/dist/modules/order/export.d.ts +1 -1
  479. package/dist/modules/order/export.js +10 -10
  480. package/dist/modules/order/export.js.map +1 -1
  481. package/dist/modules/order/format.d.ts +1 -0
  482. package/dist/modules/order/format.d.ts.map +1 -1
  483. package/dist/modules/order/format.js +0 -6
  484. package/dist/modules/order/format.js.map +1 -1
  485. package/dist/modules/order/grid.d.ts +2 -12
  486. package/dist/modules/order/grid.d.ts.map +1 -1
  487. package/dist/modules/order/grid.js +40 -27
  488. package/dist/modules/order/grid.js.map +1 -1
  489. package/dist/modules/order/grid_reconcile.d.ts +1 -1
  490. package/dist/modules/order/grid_reconcile.js +1 -1
  491. package/dist/modules/order/grid_reconcile_internal.js +1 -1
  492. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  493. package/dist/modules/order/logger.d.ts +3 -0
  494. package/dist/modules/order/logger.d.ts.map +1 -1
  495. package/dist/modules/order/logger.js +22 -13
  496. package/dist/modules/order/logger.js.map +1 -1
  497. package/dist/modules/order/logger_state.d.ts +4 -9
  498. package/dist/modules/order/logger_state.d.ts.map +1 -1
  499. package/dist/modules/order/logger_state.js +5 -14
  500. package/dist/modules/order/logger_state.js.map +1 -1
  501. package/dist/modules/order/manager.d.ts +1 -4
  502. package/dist/modules/order/manager.d.ts.map +1 -1
  503. package/dist/modules/order/manager.js +34 -33
  504. package/dist/modules/order/manager.js.map +1 -1
  505. package/dist/modules/order/processed_fill_store.d.ts.map +1 -1
  506. package/dist/modules/order/processed_fill_store.js +9 -3
  507. package/dist/modules/order/processed_fill_store.js.map +1 -1
  508. package/dist/modules/order/strategy.d.ts +6 -4
  509. package/dist/modules/order/strategy.d.ts.map +1 -1
  510. package/dist/modules/order/strategy.js +6 -4
  511. package/dist/modules/order/strategy.js.map +1 -1
  512. package/dist/modules/order/sync_engine.js +7 -7
  513. package/dist/modules/order/sync_engine.js.map +1 -1
  514. package/dist/modules/order/utils/math.d.ts +29 -3
  515. package/dist/modules/order/utils/math.d.ts.map +1 -1
  516. package/dist/modules/order/utils/math.js +54 -10
  517. package/dist/modules/order/utils/math.js.map +1 -1
  518. package/dist/modules/order/utils/order.d.ts.map +1 -1
  519. package/dist/modules/order/utils/order.js +14 -2
  520. package/dist/modules/order/utils/order.js.map +1 -1
  521. package/dist/modules/order/utils/system.d.ts +3 -8
  522. package/dist/modules/order/utils/system.d.ts.map +1 -1
  523. package/dist/modules/order/utils/system.js +14 -4
  524. package/dist/modules/order/utils/system.js.map +1 -1
  525. package/dist/modules/order/utils/validate.js +1 -1
  526. package/dist/modules/order/utils/validate.js.map +1 -1
  527. package/dist/modules/path_api.js +1 -1
  528. package/dist/modules/path_api.js.map +1 -1
  529. package/dist/modules/paths.d.ts +1 -7
  530. package/dist/modules/paths.d.ts.map +1 -1
  531. package/dist/modules/paths.js +1 -1
  532. package/dist/modules/paths.js.map +1 -1
  533. package/dist/modules/process_discovery.d.ts +3 -0
  534. package/dist/modules/process_discovery.d.ts.map +1 -1
  535. package/dist/modules/process_discovery.js +15 -1
  536. package/dist/modules/process_discovery.js.map +1 -1
  537. package/dist/modules/runtime_settings.js +1 -1
  538. package/dist/modules/runtime_settings.js.map +1 -1
  539. package/dist/modules/settings_merge.d.ts +5 -1
  540. package/dist/modules/settings_merge.d.ts.map +1 -1
  541. package/dist/modules/settings_merge.js +14 -3
  542. package/dist/modules/settings_merge.js.map +1 -1
  543. package/dist/modules/socket_json_client.d.ts.map +1 -1
  544. package/dist/modules/socket_json_client.js +12 -2
  545. package/dist/modules/socket_json_client.js.map +1 -1
  546. package/dist/modules/storage/browser_adapter.d.ts.map +1 -1
  547. package/dist/modules/storage/browser_adapter.js +5 -3
  548. package/dist/modules/storage/browser_adapter.js.map +1 -1
  549. package/dist/modules/storage/index.d.ts +1 -1
  550. package/dist/modules/storage/index.js +1 -1
  551. package/dist/modules/types.d.ts +1 -1
  552. package/dist/modules/types.d.ts.map +1 -1
  553. package/dist/modules/validate_profiles.d.ts.map +1 -1
  554. package/dist/modules/validate_profiles.js +6 -8
  555. package/dist/modules/validate_profiles.js.map +1 -1
  556. package/dist/pm2.d.ts.map +1 -1
  557. package/dist/pm2.js +2 -3
  558. package/dist/pm2.js.map +1 -1
  559. package/dist/scripts/analyze-git.d.ts +1 -1
  560. package/dist/scripts/analyze-git.js +1 -1
  561. package/dist/scripts/analyze-orders.d.ts.map +1 -1
  562. package/dist/scripts/analyze-orders.js +61 -18
  563. package/dist/scripts/analyze-orders.js.map +1 -1
  564. package/dist/scripts/diagnose-pool-history.js +1 -1
  565. package/dist/scripts/divergence-calc.d.ts +1 -1
  566. package/dist/scripts/divergence-calc.js +3 -3
  567. package/dist/scripts/native_release_gates.js +4 -2
  568. package/dist/scripts/native_release_gates.js.map +1 -1
  569. package/dist/scripts/print_grid.d.ts +1 -1
  570. package/dist/scripts/print_grid.js +1 -1
  571. package/dist/scripts/run-tests.js +38 -19
  572. package/dist/scripts/run-tests.js.map +1 -1
  573. package/dist/scripts/runner.d.ts +3 -5
  574. package/dist/scripts/runner.d.ts.map +1 -1
  575. package/dist/scripts/runner.js +3 -5
  576. package/dist/scripts/runner.js.map +1 -1
  577. package/dist/scripts/sync-version.d.ts +2 -2
  578. package/dist/scripts/sync-version.js +7 -4
  579. package/dist/scripts/sync-version.js.map +1 -1
  580. package/dist/scripts/update.d.ts +1 -1
  581. package/dist/scripts/update.js +1 -1
  582. package/dist/scripts/validate_bots.d.ts +1 -1
  583. package/dist/scripts/validate_bots.js +1 -1
  584. package/dist/scripts/verify-browser-bundle.d.ts +1 -1
  585. package/dist/scripts/verify-browser-bundle.js +1 -1
  586. package/dist/unlock.js +4 -4
  587. package/dist/unlock.js.map +1 -1
  588. package/docs/BITSHARES_ONBOARDING.md +15 -10
  589. package/docs/COPY_ON_WRITE_MASTER_PLAN.md +9 -9
  590. package/docs/DEXBOT_COMPARISON.md +19 -19
  591. package/docs/EVOLUTION.md +18 -44
  592. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
  593. package/docs/GRID_RECALCULATION.md +1 -1
  594. package/docs/README.md +3 -3
  595. package/docs/architecture.md +5 -5
  596. package/docs/developer_guide.md +6 -6
  597. package/market_adapter/README.md +13 -8
  598. package/package.json +22 -14
  599. package/scripts/README.md +33 -7
  600. package/scripts/clean-dist.js +10 -2
  601. package/tests/README.md +4 -3
@@ -1,17 +1,65 @@
1
+ 'use strict';
1
2
  import fs from 'node:fs';
2
3
  import path from 'node:path';
3
- import { calculateAMA } from '../../market_adapter/core/strategies/ama.js';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { calculateAMA, getAmaWarmupBars } from '../../market_adapter/core/strategies/ama.js';
6
+ import { computeAverageAmaSlopePct } from '../../market_adapter/core/strategies/ama_slope_model.js';
4
7
  import { range } from '../math_utils.js';
5
8
  import { parseListOrRange, loadLpData, fmt } from './shared_utils.js';
6
9
  import { getStorage } from '../../modules/storage/index.js';
7
10
  import { PATHS } from '../../modules/paths.js';
11
+ import { GRID_LIMITS, MARKET_ADAPTER } from '../../modules/constants.js';
8
12
  const { ensureDir, readJSON, writeJSON } = getStorage();
9
13
 
10
- 'use strict';
11
14
 
12
- const DEFAULT_ACTIVE_ORDERS = 5;
15
+ // Default mirrors production sizing: createOrderGrid sizes EVERY rail slot in
16
+ // bounds (modules/order/grid.ts), it does not cap per side. Infinity = all
17
+ // slots; --active-orders N restores a bounded run.
18
+ const DEFAULT_ACTIVE_ORDERS = Infinity;
13
19
  const DEFAULT_FEE_ROUNDTRIP_PCT = 0.20;
14
- const DEFAULT_MIN_SPREAD_FACTOR = 2.1;
20
+
21
+ // BTS operation fees — same model as backtest_ama_sweep. Every placement
22
+ // (initial grid, reset rebuild, per-cycle slot refill) pays a maker create
23
+ // fee, every cancel pays a cancel fee. Fees are converted into percentage
24
+ // points against --bts-fee-capital so they deduct from net capture in the
25
+ // same units as the rest of the accounting.
26
+ const DEFAULT_BTS_CREATE_FEE = 0.48260;
27
+ const DEFAULT_BTS_CANCEL_FEE = 0.00482;
28
+ const DEFAULT_BTS_MAKER_CREATE_FACTOR = 0.10;
29
+ const DEFAULT_TX_FEE_PRICE = 1.0;
30
+ const DEFAULT_BTS_FEE_CAPITAL = 10000;
31
+
32
+ // ── Production grid-reset triggers (MARKET_ADAPTER, modules/constants.ts) ────
33
+ //
34
+ // Trigger A — AMA delta: the live adapter records the AMA center and writes a
35
+ // recalculate.<botKey>.trigger when AMA moves ±AMA_DELTA_THRESHOLD_PERCENT
36
+ // from that recorded center; the center re-records only when a trigger fires
37
+ // (ratchet semantics — see analyze_ama_price_changes.trackRepositions).
38
+ // --reposition-pct overrides the default.
39
+ const DEFAULT_REPOSITION_PCT = MARKET_ADAPTER.AMA_DELTA_THRESHOLD_PERCENT;
40
+ //
41
+ // Trigger B — AMA slope delta, plus the grid price offset, are BOTH gated in
42
+ // production behind the per-bot asymmetricBounds whitelist
43
+ // (market_adapter_service.ts: isGridRangeScalingWhitelisted consumes both
44
+ // shouldTrigger at the slope-reset write and computeGridPriceOffsetPlan via
45
+ // README "asymmetricBounds whitelist also enables gridPriceOffsetPct").
46
+ // The sim therefore exposes one switch, default OFF (typical non-whitelisted
47
+ // bot): --asymmetric-bounds enables slope-delta resets AND slope-ratio offset.
48
+ // Trigger B fires when |slopePct_now − baseline| reaches
49
+ // (AMA_SLOPE_DELTA_THRESHOLD_PERCENT / 100) × DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT,
50
+ // where slopePct is the average per-bar AMA change over
51
+ // DYNAMIC_WEIGHT_AMA_LOOKBACK_BARS (computeAverageAmaSlopePct) and the
52
+ // baseline mirrors botState.gridRangeScalingAmaSlope (re-seeded every reset).
53
+ const SLOPE_TRIGGER_FACTOR = MARKET_ADAPTER.AMA_SLOPE_DELTA_THRESHOLD_PERCENT;
54
+ const SLOPE_MAX_PCT = MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT;
55
+ const SLOPE_LOOKBACK_BARS = MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_LOOKBACK_BARS;
56
+ const SLOPE_NEUTRAL_ZONE_PCT = MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_NEUTRAL_ZONE_PCT;
57
+
58
+ // Spread-gap floor knobs (GRID_LIMITS): the effective target spread is
59
+ // clamped up to incrementPercent × MIN_SPREAD_FACTOR and the gap is never
60
+ // narrower than MIN_SPREAD_ORDERS slots — mirrored from calculateGapSlots
61
+ // (modules/order/utils/math.ts).
62
+ const DEFAULT_MIN_SPREAD_FACTOR = GRID_LIMITS.MIN_SPREAD_FACTOR;
15
63
 
16
64
  // Inventory-risk penalty weights (in score points)
17
65
  const RISK_W_DURATION = 1.0; // avg open bars
@@ -34,6 +82,13 @@ function parseArgs() {
34
82
  activeOrders: number;
35
83
  feeRoundtripPct: number;
36
84
  minSpreadFactor: number;
85
+ repositionPct: number;
86
+ asymmetricBounds: boolean;
87
+ btsCreateFee: number;
88
+ btsCancelFee: number;
89
+ makerCreateFactor: number;
90
+ txFeePrice: number;
91
+ btsFeeCapital: number;
37
92
  riskWDuration: number;
38
93
  riskWPeakOpen: number;
39
94
  riskWImbalance: number;
@@ -47,6 +102,13 @@ function parseArgs() {
47
102
  activeOrders: DEFAULT_ACTIVE_ORDERS,
48
103
  feeRoundtripPct: DEFAULT_FEE_ROUNDTRIP_PCT,
49
104
  minSpreadFactor: DEFAULT_MIN_SPREAD_FACTOR,
105
+ repositionPct: DEFAULT_REPOSITION_PCT,
106
+ asymmetricBounds: false,
107
+ btsCreateFee: DEFAULT_BTS_CREATE_FEE,
108
+ btsCancelFee: DEFAULT_BTS_CANCEL_FEE,
109
+ makerCreateFactor: DEFAULT_BTS_MAKER_CREATE_FACTOR,
110
+ txFeePrice: DEFAULT_TX_FEE_PRICE,
111
+ btsFeeCapital: DEFAULT_BTS_FEE_CAPITAL,
50
112
  riskWDuration: RISK_W_DURATION,
51
113
  riskWPeakOpen: RISK_W_PEAK_OPEN,
52
114
  riskWImbalance: RISK_W_IMBALANCE,
@@ -55,6 +117,9 @@ function parseArgs() {
55
117
 
56
118
  for (let i = 0; i < args.length; i++) {
57
119
  const arg = args[i];
120
+ // Valueless boolean flag — must be handled before the value lookup
121
+ // below (and before the trailing-arg `if (!val) continue` guard).
122
+ if (arg === '--asymmetric-bounds') { out.asymmetricBounds = true; continue; }
58
123
  const val = args[i + 1];
59
124
  if (!val) continue;
60
125
  switch (arg) {
@@ -90,6 +155,30 @@ function parseArgs() {
90
155
  out.minSpreadFactor = Number(val);
91
156
  i++;
92
157
  break;
158
+ case '--reposition-pct':
159
+ out.repositionPct = Number(val);
160
+ i++;
161
+ break;
162
+ case '--bts-create-fee':
163
+ out.btsCreateFee = Number(val);
164
+ i++;
165
+ break;
166
+ case '--bts-cancel-fee':
167
+ out.btsCancelFee = Number(val);
168
+ i++;
169
+ break;
170
+ case '--maker-create-factor':
171
+ out.makerCreateFactor = Number(val);
172
+ i++;
173
+ break;
174
+ case '--tx-fee-price':
175
+ out.txFeePrice = Number(val);
176
+ i++;
177
+ break;
178
+ case '--bts-fee-capital':
179
+ out.btsFeeCapital = Number(val);
180
+ i++;
181
+ break;
93
182
  case '--risk-duration':
94
183
  out.riskWDuration = Number(val);
95
184
  i++;
@@ -178,111 +267,398 @@ function loadAmaStrategies(resultsPath: string) {
178
267
  return out;
179
268
  }
180
269
 
181
- function levelGrossSpreadPct(levelRatio: number) {
182
- return (((1 + levelRatio) / (1 - levelRatio)) - 1) * 100;
270
+ /**
271
+ * Spread-gap width in slots — direct port of calculateGapSlots
272
+ * (modules/order/utils/math.ts:1228). The effective target spread is clamped
273
+ * up to incrementPercent × MIN_SPREAD_FACTOR and the gap never drops below
274
+ * MIN_SPREAD_ORDERS slots. `incrementPercent`/`targetSpreadPercent` in PERCENT
275
+ * units, matching the production signature.
276
+ */
277
+ function computeGapSlots(incrementPercent: number, targetSpreadPercent: number) {
278
+ const step = 1 + (incrementPercent / 100);
279
+ const minSpreadPercent = incrementPercent * GRID_LIMITS.MIN_SPREAD_FACTOR;
280
+ const effectiveTargetSpread = Math.max(targetSpreadPercent || 0, minSpreadPercent);
281
+ const requiredSteps = Math.ceil(Math.log(1 + (effectiveTargetSpread / 100)) / Math.log(step));
282
+ return Math.max(GRID_LIMITS.MIN_SPREAD_ORDERS, requiredSteps - 1);
283
+ }
284
+
285
+ /**
286
+ * Slope-ratio grid price offset — port of computeGridPriceOffsetPlan
287
+ * (market_adapter/core/market_adapter_service.ts:92-119) combined with the
288
+ * trend/slopeRatio semantics of computeAmaSlopeWeights
289
+ * (market_adapter/core/strategies/ama_slope_model.ts):
290
+ *
291
+ * maxGridPriceOffsetPct = targetSpreadPercent / 2
292
+ * slopeRatio = min(|slopePct| / DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT, 1)
293
+ * direction = sign(slopePct) (0 inside the neutral zone)
294
+ * offsetPct = direction × slopeRatio × maxGridPriceOffsetPct
295
+ *
296
+ * Production applies this to startPrice (grid.ts:982: startPrice × (1 +
297
+ * offset/100)) only for asymmetricBounds-whitelisted bots; the sim applies it
298
+ * to the placement center on every grid build when asymmetricBounds is on.
299
+ */
300
+ function computeGridPriceOffsetPct(slopePct: number, targetSpreadPercent: number) {
301
+ if (!Number.isFinite(slopePct)) return 0;
302
+ if (Math.abs(slopePct) <= SLOPE_NEUTRAL_ZONE_PCT) return 0;
303
+ const slopeRatio = Math.min(Math.abs(slopePct) / SLOPE_MAX_PCT, 1);
304
+ const maxGridPriceOffsetPct = targetSpreadPercent / 2;
305
+ const direction = slopePct > 0 ? 1 : -1;
306
+ const offsetPct = direction * slopeRatio * maxGridPriceOffsetPct;
307
+ return Math.round(offsetPct * 1e6) / 1e6;
308
+ }
309
+
310
+ /**
311
+ * Build a persistent grid with FIXED chain prices from a master rail —
312
+ * port of createOrderGrid (modules/order/grid.ts:374-505):
313
+ *
314
+ * - Master rail: geometric progression starting at √(1±inc) × center and
315
+ * expanding by (1±inc) steps outward, bounded by
316
+ * [center/maxMinRatio, center×maxMinRatio] (grid.ts:428-444).
317
+ * - Spread gap: gapSlots empty slots centered on the center price
318
+ * (calculateIdealBoundary: boundaryIdx = splitIdx − floor(gap/2) − 1;
319
+ * sells start at boundaryIdx + gapSlots + 1).
320
+ * - By default every rail slot in bounds is sized (activeOrders = Infinity,
321
+ * matching production); an explicit cap keeps only the slots nearest the
322
+ * gap on each side.
323
+ *
324
+ * Order prices are placement-time constants — exactly like on-chain orders,
325
+ * they do NOT follow AMA after placement.
326
+ */
327
+ function buildProductionGrid(center: number, spreadPct: number, incrementPctFrac: number, maxMinRatio: number, activeOrders: number) {
328
+ const stepUp = 1 + incrementPctFrac;
329
+ const stepDown = 1 - incrementPctFrac;
330
+ const minBound = center / maxMinRatio;
331
+ const maxBound = center * maxMinRatio;
332
+
333
+ const rail: number[] = [];
334
+ let p = center * Math.sqrt(stepUp);
335
+ while (p <= maxBound) { rail.push(p); p *= stepUp; }
336
+ p = center * Math.sqrt(stepDown);
337
+ while (p >= minBound) { rail.push(p); p *= stepDown; }
338
+ rail.sort((a, b) => a - b);
339
+ if (rail.length === 0) return { buys: [], sells: [], rail: [], buySliceStart: 0, sellStartIdx: 0 };
340
+
341
+ const gapSlots = computeGapSlots(incrementPctFrac * 100, spreadPct);
342
+ let splitIdx = rail.findIndex((v) => v >= center);
343
+ if (splitIdx === -1) splitIdx = rail.length;
344
+ const buySpread = Math.floor(gapSlots / 2);
345
+ const boundaryIdx = Math.max(0, Math.min(rail.length - 1, splitIdx - buySpread - 1));
346
+ const sellStartIdx = boundaryIdx + gapSlots + 1;
347
+
348
+ // Buys closest to the gap = highest priced below boundary; sells closest =
349
+ // lowest priced above sellStartIdx. Rail + slice offsets are returned so
350
+ // callers can map each placed slot onto its MASTER-RAIL index (rotation
351
+ // walks adjacent rail nodes, exactly like the live anchor-&-refill hop).
352
+ const buySliceStart = Math.max(0, boundaryIdx - activeOrders + 1);
353
+ const sellsArr = rail.slice(sellStartIdx, sellStartIdx + activeOrders);
354
+ return {
355
+ buys: rail.slice(buySliceStart, boundaryIdx + 1),
356
+ sells: sellsArr,
357
+ rail,
358
+ buySliceStart,
359
+ sellStartIdx,
360
+ };
183
361
  }
184
362
 
363
+ /**
364
+ * Slot-rotation simulation with FIXED chain prices, mirroring production
365
+ * anchor-&-refill (strategy.ts) on top of the createOrderGrid geometry:
366
+ *
367
+ * - Initial grid built once at the post-warmup AMA (createOrderGrid geometry
368
+ * via buildProductionGrid); order prices never follow AMA afterwards. When
369
+ * asymmetricBounds is enabled the placement center is shifted by the
370
+ * slope-ratio offset (#3) exactly like the live startPrice offset.
371
+ * - Rotation economics (#1/#2/#15): a FILLED BUY at rail node k instantly
372
+ * re-offers that base at the ADJACENT MASTER-RAIL NODE k+1; when that
373
+ * refill sells, the rotation books exactly one rail hop minus the
374
+ * round-trip fee, and the freed quote re-bids the node k-1 below. Slots
375
+ * cycle on small oscillations between resets — the earnings model of a
376
+ * refilled live slot, not a cross-gap differential.
377
+ * - Inventory-funded selling: an INITIAL (unlinked) sell can only execute
378
+ * against held inventory — it books its proceeds against the weighted
379
+ * average entry of the position (the live bot funds sells from real
380
+ * balances; unfundable sells stay open and retry, never shorting). Base
381
+ * bought in the SAME bar does not fund unlinked sales until the next bar
382
+ * (live balance updates propagate on the next maintenance tick).
383
+ * - Same-bar guard: newly armed/refill orders carry a one-bar cooldown, so
384
+ * a slot that just cycled cannot fill again inside the completion bar.
385
+ * - Grid reset fires on EITHER production trigger:
386
+ * (A) |AMA − recordedCenter| / recordedCenter ≥ repositionThresholdPct
387
+ * (ratchet: recordedCenter updates only when the trigger fires),
388
+ * (B) slope-delta reset, ONLY when asymmetricBounds (whitelist gate):
389
+ * |slopePct_now − slopeBaseline| ≥ slopeDeltaThresholdPct
390
+ * (= AMA_SLOPE_DELTA_THRESHOLD_PERCENT/100 ×
391
+ * DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT), where slopePct is the
392
+ * DYNAMIC_WEIGHT_AMA_LOOKBACK_BARS average-slope series and
393
+ * slopeBaseline mirrors botState.gridRangeScalingAmaSlope — seeded
394
+ * at start and re-seeded on every reset.
395
+ * On reset ALL unfilled orders are canceled (counted); bought-and-held
396
+ * base REMAINS as inventory (live resync never market-sells) and merges
397
+ * into the weighted-average-entry pool; a fresh grid is placed at the
398
+ * new center.
399
+ * - Scoring uses REALIZED rotations and op fees ONLY. The end-of-run
400
+ * inventory position (units × mark-vs-avg-entry) is reported as an
401
+ * informational field — it is real carried risk, but including unrealized
402
+ * bag marks in the ranking objective let trend-following combos dump
403
+ * phantom paper profit into the score.
404
+ * - BTS operation fees (#9): every order placement (initial grid + every
405
+ * armed refill/rebid) pays makerCreateFee × btsCreateFee, every reset
406
+ * cancel pays btsCancelFee; totals are converted to percentage points
407
+ * against the --bts-fee-capital reference and deducted from net capture.
408
+ *
409
+ * Differences from production (by design, documented):
410
+ * #6 bar-granularity triggers and reset-vs-fill ordering (OHLC sim);
411
+ * #7 all-or-nothing fills on raw hi/lo touch (no partial fills/dust/queue);
412
+ * #8 unit sizes / percentage-point accounting instead of capital-weighted
413
+ * dynamic sizing;
414
+ * #10 pool-price fills (no book depth/slippage);
415
+ * #11 no consolidation/dust-cancel/COW/collision mechanics.
416
+ */
185
417
  function simulateForParams(candles: any, amaValues: any, params: any) {
186
- const { spreadPct, incrementPct, maxMinRatio, activeOrders, feeRoundtripPct, risk } = params;
187
- const skip = Math.max(20, Math.floor(candles.length * 0.1));
188
- const levels = Array.from({ length: activeOrders }, (_, i) => incrementPct * (i + 1));
418
+ const { spreadPct, incrementPct, maxMinRatio, activeOrders, feeRoundtripPct,
419
+ repositionThresholdPct, asymmetricBounds, risk,
420
+ btsCreateFee, btsCancelFee, makerCreateFactor, txFeePrice, btsFeeCapital } = params;
421
+ // Warmup follows production AMA seeding/convergence (getAmaWarmupBars)
422
+ // instead of an arbitrary fraction of the dataset, so simulations never
423
+ // start on SMA-warmup values when the fitted ER period is large.
424
+ const skip = Math.min(params.warmupBars, Math.max(0, candles.length - 2));
425
+ const singleLegFeePct = feeRoundtripPct / 2;
426
+ const slopeDeltaThresholdPct = (SLOPE_TRIGGER_FACTOR / 100) * SLOPE_MAX_PCT;
427
+ const makerCreateFeeBts = btsCreateFee * makerCreateFactor;
428
+ const stepUpFrac = 1 + incrementPct; // one-rail-step rotation distance
429
+
430
+ // First tradable bar: need a finite positive AMA to anchor the chain.
431
+ let startIdx = Math.min(skip, candles.length - 1);
432
+ let gridCenter = Number.NaN;
433
+ for (let j = startIdx; j < candles.length; j++) {
434
+ const v = amaValues[j];
435
+ if (Number.isFinite(v) && v > 0) { gridCenter = v; startIdx = j; break; }
436
+ }
437
+
438
+ // Production AMA slope series (%/bar averaged over the lookback window).
439
+ // Evaluated over full history like the live adapter (only bar-index guards).
440
+ const slopeAt: (number | null)[] = new Array(candles.length).fill(null);
441
+ for (let j = SLOPE_LOOKBACK_BARS; j < candles.length; j++) {
442
+ const s = computeAverageAmaSlopePct(amaValues[j], amaValues[j - SLOPE_LOOKBACK_BARS], SLOPE_LOOKBACK_BARS);
443
+ if (s != null && Number.isFinite(s)) slopeAt[j] = s;
444
+ }
189
445
 
190
- const openBuy = new Map(); // lvl -> openedAtBarIdx
191
- const openSell = new Map(); // lvl -> openedAtBarIdx
446
+ // Open orders keyed by running id: { side, price, linkedBuyPrice,
447
+ // linkedEntryBar, cooldownUntil }. linkedBuyPrice != null marks an armed
448
+ // refill sell created by a specific filled buy (one-increment rotation).
449
+ const orders = new Map<number, any>();
450
+ let nextOrderId = 0;
451
+ // Bought-and-held base across the whole run (weighted-average entry
452
+ // pool). Never negative — sells without inventory stay pending.
453
+ const inv = { units: 0, cost: 0 };
454
+
455
+ let btsFeesBts = 0;
456
+ let offsetAppliedCount = 0;
457
+ // Master rail of the CURRENT epoch — rotation hops read adjacent nodes.
458
+ let activeRail: number[] = [];
459
+
460
+ const placeInitialGrid = (center: number, slopePct: number | null) => {
461
+ const offsetPct = (asymmetricBounds && slopePct != null)
462
+ ? computeGridPriceOffsetPct(slopePct, spreadPct)
463
+ : 0;
464
+ if (offsetPct !== 0) offsetAppliedCount++;
465
+ const effCenter = center * (1 + offsetPct / 100);
466
+ const built = buildProductionGrid(effCenter, spreadPct, incrementPct, maxMinRatio, activeOrders);
467
+ activeRail = built.rail;
468
+ orders.clear();
469
+ // Rail indices matter: rotation re-offers/re-bids walk ADJACENT
470
+ // master-rail nodes (buy at node k arms a sell at node k+1; the freed
471
+ // quote re-bids node k-1), preserving the exact production hop sizes.
472
+ built.buys.forEach((price, i) => orders.set(nextOrderId++, {
473
+ side: 'buy', price, railIdx: built.buySliceStart + i,
474
+ linkedBuyPrice: null, linkedEntryBar: -1, cooldownUntil: -1,
475
+ }));
476
+ built.sells.forEach((price, i) => orders.set(nextOrderId++, {
477
+ side: 'sell', price, railIdx: built.sellStartIdx + i,
478
+ linkedBuyPrice: null, linkedEntryBar: -1, cooldownUntil: -1,
479
+ }));
480
+ btsFeesBts += (built.buys.length + built.sells.length) * makerCreateFeeBts;
481
+ };
192
482
 
193
483
  let touchedOrders = 0;
194
- let matchedPairs = 0;
484
+ let cyclesTotal = 0;
485
+ let rotationCount = 0; // linked ping-pong rotations (buy → refill sell)
486
+ let inventorySaleCount = 0; // unlinked sells executed against held bags
195
487
  let canceledOnReposition = 0;
488
+ let repositionCount = 0;
489
+ let driftTriggerCount = 0;
490
+ let slopeTriggerCount = 0;
196
491
  let totalGrossCapturePct = 0;
197
492
  let totalNetCapturePct = 0;
198
- let activeLevelTouches = 0;
199
493
  let peakOpenOrders = 0;
200
494
  let imbalanceSum = 0;
201
495
  let imbalanceSamples = 0;
202
496
  let matchedOpenDurationBars = 0;
203
497
 
204
- for (let i = skip + 1; i < candles.length; i++) {
498
+ const invAvgEntry = () => (inv.units > 0 ? inv.cost / inv.units : 0);
499
+
500
+ if (Number.isFinite(gridCenter)) placeInitialGrid(gridCenter, slopeAt[startIdx]);
501
+
502
+ // Slope-delta baseline: mirrors botState.gridRangeScalingAmaSlope —
503
+ // seeded at bootstrap (first cycle) and re-seeded to the current slope on
504
+ // EVERY grid reset (advanceTriggeredBotState), so trigger B measures how
505
+ // far slope has moved SINCE THE LAST RESET, not bar-over-bar jitter.
506
+ let slopeBaseline: number | null = null;
507
+ for (let j = startIdx + 1; j < candles.length; j++) {
508
+ if (slopeAt[j] != null) { slopeBaseline = slopeAt[j]; break; }
509
+ }
510
+
511
+ for (let i = startIdx + 1; i < candles.length; i++) {
205
512
  const ama = amaValues[i];
206
- const prevAma = amaValues[i - 1];
207
513
  const hi = candles[i].high;
208
514
  const lo = candles[i].low;
209
515
 
210
- if (Math.abs(ama - prevAma) / prevAma > incrementPct) {
211
- canceledOnReposition += openBuy.size + openSell.size;
212
- openBuy.clear();
213
- openSell.clear();
516
+ // ── Grid-reset check: trigger A (AMA delta) always; trigger B
517
+ // (slope delta) only under the asymmetricBounds whitelist gate.
518
+ let shouldReset = false;
519
+ if (Number.isFinite(ama) && ama > 0) {
520
+ const driftPct = (Math.abs(ama - gridCenter) / gridCenter) * 100;
521
+ if (driftPct >= repositionThresholdPct) { shouldReset = true; driftTriggerCount++; }
522
+ }
523
+ if (!shouldReset && asymmetricBounds && slopeBaseline != null && slopeAt[i] != null) {
524
+ const slopeDeltaPct = Math.abs(slopeAt[i]! - slopeBaseline);
525
+ if (slopeDeltaPct >= slopeDeltaThresholdPct) { shouldReset = true; slopeTriggerCount++; }
526
+ }
527
+
528
+ if (shouldReset && Number.isFinite(ama) && ama > 0) {
529
+ canceledOnReposition += orders.size;
530
+ btsFeesBts += orders.size * btsCancelFee;
531
+ orders.clear(); // inventory survives — resync never market-sells
532
+ repositionCount++;
533
+ gridCenter = ama; // ratchet the recorded center to the current AMA
534
+ // Re-seed the slope baseline with the current slope (falls back to
535
+ // the previous baseline when slope is not ready — same as
536
+ // advanceTriggeredBotState's `|| previous` chain).
537
+ if (slopeAt[i] != null) slopeBaseline = slopeAt[i];
538
+ placeInitialGrid(gridCenter, slopeAt[i]);
214
539
  }
215
540
 
216
- const currentOpen = openBuy.size + openSell.size;
541
+ const currentOpen = orders.size;
217
542
  if (currentOpen > peakOpenOrders) peakOpenOrders = currentOpen;
218
- imbalanceSum += Math.abs(openBuy.size - openSell.size);
543
+ let buyCount = 0;
544
+ for (const [, o] of orders) { if (o.side === 'buy') buyCount++; }
545
+ imbalanceSum += Math.abs(buyCount - (orders.size - buyCount));
219
546
  imbalanceSamples++;
220
547
 
221
- const minBound = ama / maxMinRatio;
222
- const maxBound = ama * maxMinRatio;
223
-
224
- for (let lvl = 0; lvl < levels.length; lvl++) {
225
- const r = levels[lvl];
226
- if (r >= 0.95) continue;
227
- const buyPrice = ama * (1 - r);
228
- const sellPrice = ama * (1 + r);
229
-
230
- if (buyPrice < minBound || sellPrice > maxBound) continue;
231
-
232
- const grossPct = levelGrossSpreadPct(r);
233
- if (grossPct < spreadPct) continue;
234
-
235
- const touchBuy = lo <= buyPrice;
236
- const touchSell = hi >= sellPrice;
237
- if (!touchBuy && !touchSell) continue;
238
-
239
- activeLevelTouches++;
240
-
241
- if (touchBuy && !openBuy.has(lvl)) {
242
- touchedOrders++;
243
- if (openSell.has(lvl)) {
244
- const openedAt = openSell.get(lvl);
245
- matchedOpenDurationBars += (i - openedAt);
246
- matchedPairs++;
247
- openSell.delete(lvl);
248
- totalGrossCapturePct += grossPct;
249
- } else {
250
- openBuy.set(lvl, i);
251
- }
252
- }
548
+ // ── Fill detection against FIXED chain prices ───────────────────
549
+ const filledBuys: { id: number; order: any }[] = [];
550
+ const filledSells: { id: number; order: any }[] = [];
551
+ for (const [id, o] of orders) {
552
+ if (i < o.cooldownUntil) continue;
553
+ if (o.side === 'buy' && lo <= o.price) filledBuys.push({ id, order: o });
554
+ else if (o.side === 'sell' && hi >= o.price) filledSells.push({ id, order: o });
555
+ }
556
+ touchedOrders += filledBuys.length + filledSells.length;
557
+ // Base held BEFORE this bar's intakes — an unlinked sell may only
558
+ // dispose against pre-existing funds (live balance updates propagate
559
+ // on the next maintenance tick; same-bar funding is not assumed).
560
+ const invAtBarStart = inv.units;
561
+
562
+ // ── Buy intakes first: base enters inventory, refill armed at the
563
+ // ADJACENT MASTER-RAIL NODE above (anchor-&-refill hop; cooldown
564
+ // blocks same-bar recycles).
565
+ for (const f of filledBuys) {
566
+ orders.delete(f.id);
567
+ inv.units += 1;
568
+ inv.cost += f.order.price;
569
+ const upIdx = (f.order.railIdx ?? -1) + 1;
570
+ const refillPrice = activeRail[upIdx] ?? f.order.price * stepUpFrac;
571
+ orders.set(nextOrderId++, {
572
+ side: 'sell',
573
+ price: refillPrice,
574
+ railIdx: upIdx,
575
+ linkedBuyPrice: f.order.price,
576
+ linkedEntryBar: i,
577
+ cooldownUntil: i + 1,
578
+ });
579
+ btsFeesBts += makerCreateFeeBts;
580
+ }
253
581
 
254
- if (touchSell && !openSell.has(lvl)) {
255
- touchedOrders++;
256
- if (openBuy.has(lvl)) {
257
- const openedAt = openBuy.get(lvl);
258
- matchedOpenDurationBars += (i - openedAt);
259
- matchedPairs++;
260
- openBuy.delete(lvl);
261
- totalGrossCapturePct += grossPct;
262
- } else {
263
- openSell.set(lvl, i);
264
- }
582
+ // ── Sell disposals: linked refills book the one-rail-hop rotation;
583
+ // unlinked (initial-grid) sells need held inventory — no shorting.
584
+ // Linked refills resolve FIRST so same-bar cross-gap inventory
585
+ // sales against a fresh bag cannot happen.
586
+ filledSells.sort((a, b) => ((a.order.linkedBuyPrice != null ? 0 : 1) - (b.order.linkedBuyPrice != null ? 0 : 1)));
587
+ let disposables = invAtBarStart;
588
+ for (const f of filledSells) {
589
+ const o = f.order;
590
+ if (o.linkedBuyPrice != null) {
591
+ const grossPct = (o.price / o.linkedBuyPrice - 1) * 100;
592
+ totalGrossCapturePct += grossPct;
593
+ totalNetCapturePct += grossPct - feeRoundtripPct;
594
+ cyclesTotal++;
595
+ rotationCount++;
596
+ matchedOpenDurationBars += Math.abs(i - o.linkedEntryBar);
597
+ // Dispose the unit its own rotation bought.
598
+ const applied = Math.min(1, inv.units);
599
+ inv.cost -= applied * invAvgEntry();
600
+ inv.units -= applied;
601
+ // Its disposal also drains the pre-bar funding budget —
602
+ // otherwise later unlinked sales could overspend stock.
603
+ disposables -= applied;
604
+ // Freed quote re-bids the ADJACENT RAIL NODE below.
605
+ const downIdx = (o.railIdx ?? 0) - 1;
606
+ const rebidPrice = activeRail[downIdx] ?? o.price / stepUpFrac;
607
+ orders.delete(f.id);
608
+ orders.set(nextOrderId++, {
609
+ side: 'buy',
610
+ price: rebidPrice,
611
+ railIdx: downIdx,
612
+ linkedBuyPrice: null,
613
+ linkedEntryBar: -1,
614
+ cooldownUntil: i + 1,
615
+ });
616
+ btsFeesBts += makerCreateFeeBts;
617
+ } else if (disposables >= 1) {
618
+ const avgEntry = invAvgEntry();
619
+ const grossPct = (o.price / avgEntry - 1) * 100;
620
+ totalGrossCapturePct += grossPct;
621
+ totalNetCapturePct += grossPct - feeRoundtripPct;
622
+ cyclesTotal++;
623
+ inventorySaleCount++;
624
+ inv.cost -= avgEntry; // remove that unit at pool-average cost
625
+ inv.units -= 1;
626
+ disposables -= 1;
627
+ orders.delete(f.id); // sold bag is gone; slot not re-armed
628
+ } else {
629
+ // Unfundable (no base to sell): stays open, retries next bar.
630
+ o.cooldownUntil = i + 1;
265
631
  }
266
632
  }
267
633
  }
268
634
 
269
- // Profit model requested: totalProfit = fills * (spread - increment)
270
- // (all values in percentage points)
271
- const incrementPctPoints = incrementPct * 100;
272
- const netPerFillPct = Math.max(0, spreadPct - incrementPctPoints - feeRoundtripPct);
273
- totalNetCapturePct = matchedPairs * netPerFillPct;
274
-
275
- const fillEfficiency = touchedOrders > 0 ? (matchedPairs / touchedOrders) * 100 : 0;
276
- const avgNetPerPair = matchedPairs > 0 ? totalNetCapturePct / matchedPairs : 0;
277
- const utilization = (activeLevelTouches / Math.max(1, (candles.length - skip))) * 100;
278
- const baseScore = totalNetCapturePct * (fillEfficiency / 100);
279
- const avgOpenDurationBars = matchedPairs > 0 ? (matchedOpenDurationBars / matchedPairs) : 0;
635
+ // ── End-of-run inventory mark (informational, NOT in score) ────────
636
+ // Bought-and-held base is real carried risk, but unrealized bag marks are
637
+ // excluded from ranking so trend-following combos can't dump phantom
638
+ // paper profit into the objective. Marked once at the final close with a
639
+ // single-leg exit fee per unit.
640
+ const lastClose = candles.length > 0 ? candles[candles.length - 1].close : NaN;
641
+ const inventoryUnits = inv.units;
642
+ const inventoryAvgEntry = invAvgEntry();
643
+ const inventoryNetPts = inventoryUnits > 0 && Number.isFinite(lastClose) && lastClose > 0
644
+ ? (((lastClose / inventoryAvgEntry) - 1) * 100 - singleLegFeePct) * inventoryUnits
645
+ : 0;
646
+
647
+ // BTS operation fees → percentage points against the reference capital.
648
+ const btsFeePts = (btsFeesBts * txFeePrice) / Math.max(1, btsFeeCapital) * 100;
649
+ const totalNetCaptureAfterFeesPct = totalNetCapturePct - btsFeePts;
650
+
651
+ const fillEfficiency = touchedOrders > 0 ? (cyclesTotal / touchedOrders) * 100 : 0;
652
+ const avgNetPerPair = cyclesTotal > 0 ? totalNetCapturePct / cyclesTotal : 0;
653
+ const utilization = (touchedOrders / Math.max(1, candles.length - skip)) * 100;
654
+ const avgOpenDurationBars = rotationCount > 0 ? (matchedOpenDurationBars / rotationCount) : 0;
280
655
  const avgImbalance = imbalanceSamples > 0 ? (imbalanceSum / imbalanceSamples) : 0;
281
656
  const riskPenalty =
282
657
  (avgOpenDurationBars * risk.duration) +
283
658
  (peakOpenOrders * risk.peakOpen) +
284
659
  (avgImbalance * risk.imbalance) +
285
660
  (canceledOnReposition * risk.cancel);
661
+ const baseScore = totalNetCaptureAfterFeesPct * (fillEfficiency / 100);
286
662
  const score = baseScore - riskPenalty;
287
663
 
288
664
  return {
@@ -290,12 +666,26 @@ function simulateForParams(candles: any, amaValues: any, params: any) {
290
666
  incrementPct,
291
667
  maxMinRatio,
292
668
  touchedOrders,
293
- matchedPairs,
669
+ matchedPairs: cyclesTotal,
670
+ cyclesTotal,
671
+ rotationCount,
672
+ inventorySaleCount,
673
+ avgCyclesPerSlot: cyclesTotal / Math.max(1, peakOpenOrders),
294
674
  fillEfficiency,
295
675
  totalGrossCapturePct,
296
676
  totalNetCapturePct,
677
+ btsFeePts,
678
+ btsFeesBts,
679
+ totalNetCaptureAfterFeesPct,
297
680
  avgNetPerPair,
298
681
  canceledOnReposition,
682
+ repositionCount,
683
+ driftTriggerCount,
684
+ slopeTriggerCount,
685
+ inventoryUnits,
686
+ inventoryAvgEntry,
687
+ inventoryNetPts,
688
+ offsetAppliedCount,
299
689
  avgOpenDurationBars,
300
690
  peakOpenOrders,
301
691
  avgImbalance,
@@ -324,6 +714,10 @@ function run() {
324
714
  const closes = candles.map((c: any) => c.close);
325
715
  const strategies = loadAmaStrategies(cfg.resultsPath!);
326
716
 
717
+ if (!Number.isFinite(cfg.repositionPct) || cfg.repositionPct <= 0) {
718
+ throw new Error(`Invalid reposition threshold: ${cfg.repositionPct}`);
719
+ }
720
+
327
721
  const totalCombos = cfg.spreadValues.length * cfg.incrementValues.length * cfg.ratioValues.length;
328
722
 
329
723
  console.log('================================================================================');
@@ -334,9 +728,13 @@ function run() {
334
728
  console.log(` Spread grid: ${cfg.spreadValues[0]}..${cfg.spreadValues[cfg.spreadValues.length - 1]}% (${cfg.spreadValues.length})`);
335
729
  console.log(` Increment: ${cfg.incrementValues[0]}..${cfg.incrementValues[cfg.incrementValues.length - 1]}% (${cfg.incrementValues.length})`);
336
730
  console.log(` Max/Min ratio:${cfg.ratioValues[0]}..${cfg.ratioValues[cfg.ratioValues.length - 1]} (${cfg.ratioValues.length})`);
337
- console.log(` Active orders:${cfg.activeOrders} per side`);
731
+ console.log(` Active orders:${Number.isFinite(cfg.activeOrders) ? cfg.activeOrders : 'all'} per side (Infinity = every rail slot, matching production)`);
338
732
  console.log(` Fee RT: ${cfg.feeRoundtripPct}%`);
339
733
  console.log(` Spread floor: spread >= ${cfg.minSpreadFactor} x increment`);
734
+ console.log(` Reset (A): AMA drift >= ${cfg.repositionPct}% from recorded center${cfg.repositionPct === DEFAULT_REPOSITION_PCT ? ' (AMA_DELTA_THRESHOLD_PERCENT)' : ''}`);
735
+ console.log(` Asym. bounds: ${cfg.asymmetricBounds ? 'ON — slope reset (B) + grid price offset enabled (whitelist semantics)' : 'OFF — typical non-whitelisted bot (production default)'}`);
736
+ console.log(` Reset (B): |slope - slope@lastReset| >= ${(SLOPE_TRIGGER_FACTOR / 100) * SLOPE_MAX_PCT}% (${SLOPE_TRIGGER_FACTOR}% x ${SLOPE_MAX_PCT}, lookback ${SLOPE_LOOKBACK_BARS})${cfg.asymmetricBounds ? '' : ' [gated off]'}`);
737
+ console.log(` Tx fees: create=${fmt(cfg.btsCreateFee * cfg.makerCreateFactor, 5)} BTS, cancel=${fmt(cfg.btsCancelFee, 5)} BTS, 1 BTS=${fmt(cfg.txFeePrice, 2)} units, capital=${fmt(cfg.btsFeeCapital, 0)}`);
340
738
  console.log(` Risk W: duration=${cfg.riskWDuration}, peakOpen=${cfg.riskWPeakOpen}, imbalance=${cfg.riskWImbalance}, cancel=${cfg.riskWCancel}`);
341
739
  console.log(` Combos/AMA: ${totalCombos}\n`);
342
740
 
@@ -344,18 +742,31 @@ function run() {
344
742
 
345
743
  for (const s of strategies) {
346
744
  const amaValues = calculateAMA(closes, { erPeriod: s.er, fastPeriod: s.fast, slowPeriod: s.slow });
745
+ // Production-aligned warmup: ER window + convergence (getAmaWarmupBars).
746
+ const warmupBars = getAmaWarmupBars(s.er, s.slow, 0, s.fast);
347
747
  let best: any = null;
348
748
 
349
749
  for (const spreadPct of cfg.spreadValues) {
350
750
  for (const incrementPct of cfg.incrementValues) {
751
+ // Dedup guard only: computeGapSlots clamps the effective target
752
+ // spread up to increment × MIN_SPREAD_FACTOR, so combos below the
753
+ // floor would build an identical grid to the floored combo.
754
+ if (spreadPct + Number.EPSILON < (cfg.minSpreadFactor * incrementPct)) continue;
351
755
  for (const maxMinRatio of cfg.ratioValues) {
352
- if (spreadPct + Number.EPSILON < (cfg.minSpreadFactor * incrementPct)) continue;
353
756
  const sim = simulateForParams(candles, amaValues, {
354
757
  spreadPct,
355
758
  incrementPct: incrementPct / 100,
356
759
  maxMinRatio,
357
760
  activeOrders: cfg.activeOrders,
358
761
  feeRoundtripPct: cfg.feeRoundtripPct,
762
+ repositionThresholdPct: cfg.repositionPct,
763
+ asymmetricBounds: cfg.asymmetricBounds,
764
+ btsCreateFee: cfg.btsCreateFee,
765
+ btsCancelFee: cfg.btsCancelFee,
766
+ makerCreateFactor: cfg.makerCreateFactor,
767
+ txFeePrice: cfg.txFeePrice,
768
+ btsFeeCapital: cfg.btsFeeCapital,
769
+ warmupBars,
359
770
  risk: {
360
771
  duration: cfg.riskWDuration,
361
772
  peakOpen: cfg.riskWPeakOpen,
@@ -373,8 +784,8 @@ function run() {
373
784
 
374
785
  console.log('BEST PARAMS PER AMA');
375
786
  console.log('--------------------------------------------------------------------------------');
376
- console.log('AMA | spread | incr | ratio | pairs | fill% | net% | risk | score');
377
- console.log('---------------------------------|--------|------|-------|-------|-------|--------|-------|-------');
787
+ console.log('AMA | spread | incr | ratio | cyc | fill% | net% | risk | score | inv-units (info)');
788
+ console.log('---------------------------------|--------|------|-------|-------|-------|--------|-------|-------|-----------------');
378
789
  for (const row of byAma) {
379
790
  const b = row.best;
380
791
  if (!b) continue;
@@ -387,9 +798,11 @@ function run() {
387
798
  `${fmt(b.fillEfficiency, 1).padStart(5)} | ` +
388
799
  `${fmt(b.totalNetCapturePct, 1).padStart(6)} | ` +
389
800
  `${fmt(b.riskPenalty, 1).padStart(5)} | ` +
390
- `${fmt(b.score, 1).padStart(5)}`
801
+ `${fmt(b.score, 1).padStart(5)} | ` +
802
+ `${fmt(b.inventoryUnits, 0).padStart(8)} (${fmt(b.inventoryNetPts, 1)} pts)`
391
803
  );
392
804
  }
805
+ console.log('(* end-of-run inventory mark: informational only — excluded from scoring)');
393
806
  console.log();
394
807
 
395
808
  const outName = `bot_fitting_results_${path.basename(cfg.dataPath!, '.json')}.json`;
@@ -401,18 +814,34 @@ function run() {
401
814
  dataPath: path.relative(process.cwd(), cfg.dataPath!),
402
815
  resultsPath: path.relative(process.cwd(), cfg.resultsPath!),
403
816
  candles: candles.length,
404
- activeOrders: cfg.activeOrders,
817
+ activeOrders: Number.isFinite(cfg.activeOrders) ? cfg.activeOrders : 'all',
405
818
  feeRoundtripPct: cfg.feeRoundtripPct,
819
+ btsCreateFee: cfg.btsCreateFee,
820
+ btsCancelFee: cfg.btsCancelFee,
821
+ makerCreateFactor: cfg.makerCreateFactor,
822
+ txFeePrice: cfg.txFeePrice,
823
+ btsFeeCapital: cfg.btsFeeCapital,
406
824
  search: {
407
825
  spreadValues: cfg.spreadValues,
408
826
  incrementValues: cfg.incrementValues,
409
827
  ratioValues: cfg.ratioValues,
410
828
  minSpreadFactor: cfg.minSpreadFactor,
829
+ repositionPct: cfg.repositionPct,
830
+ asymmetricBounds: cfg.asymmetricBounds,
411
831
  combosPerAma: totalCombos,
412
832
  },
413
833
  scoring: {
414
- baseScore: 'totalNetCapturePct * (fillEfficiency / 100)',
415
- totalNetCapturePct: 'matchedPairs * (spread - increment - fee)',
834
+ baseScore: 'totalNetCaptureAfterFeesPct * (fillEfficiency / 100)',
835
+ gridModel: 'persistent fixed chain prices (createOrderGrid port): master rail at sqrt(1±inc) offsets bounded by [center/ratio, center*ratio], gapSlots spread zone centered on center; prices never follow AMA after placement',
836
+ spreadParam: 'targetSpreadPercent for calculateGapSlots (floored at increment * MIN_SPREAD_FACTOR)',
837
+ resetTriggers: `(A) AMA drift >= ${cfg.repositionPct}% from recorded center (ratchet)${cfg.asymmetricBounds ? ` OR (B) |slope - slopeAtLastReset| >= ${(SLOPE_TRIGGER_FACTOR / 100) * SLOPE_MAX_PCT}% over ${SLOPE_LOOKBACK_BARS}-bar average slope (baseline re-seeded on every reset)` : ' (trigger B gated off — asymmetricBounds whitelist)'}`,
838
+ gridPriceOffset: cfg.asymmetricBounds ? 'slope-ratio offset applied to placement center (direction * min(|slope|/maxSlopePct,1) * targetSpread/2) on every grid build' : 'disabled (asymmetricBounds whitelist)',
839
+ repositionAccounting: 'unfilled orders canceled + counted (incl armed refills/rebids); bought-and-held base carries across resets in a weighted-average-entry inventory pool (resync never market-sells); end-of-run inventory mark is informational and excluded from scoring',
840
+ cycleEconomics: 'slot rotation: filled buy re-offers one rail step up; that refill selling books ~increment% minus round-trip fee and the freed quote re-bids one step down; unlinked initial-grid sells only execute against held inventory at weighted-average entry (no shorting)',
841
+ totalGrossCapturePct: 'sum of realized per-rotation gross from actual fixed prices (linked rotations + inventory sales)',
842
+ totalNetCapturePct: 'realized gross - roundtrip fee per completed disposition; excludes the end-of-run inventory mark',
843
+ btsFees: `every order placement (initial grid + armed refills/rebids) pays maker create (${cfg.btsCreateFee}*${cfg.makerCreateFactor} BTS), every reset cancel pays ${cfg.btsCancelFee} BTS; btsFeePts = total BTS * txFeePrice / btsFeeCapital * 100 deducted from net before scoring`,
844
+ warmup: 'getAmaWarmupBars(er, slow, 0, fast)',
416
845
  riskPenalty: `avgOpenDurationBars*${cfg.riskWDuration} + peakOpenOrders*${cfg.riskWPeakOpen} + avgImbalance*${cfg.riskWImbalance} + canceledOnReposition*${cfg.riskWCancel}`,
417
846
  finalScore: 'baseScore - riskPenalty',
418
847
  },
@@ -423,4 +852,10 @@ function run() {
423
852
  console.log(`Saved: ${path.relative(process.cwd(), outPath)}`);
424
853
  }
425
854
 
426
- run();
855
+ // Main-thread entry guard: importing this module (e.g. from the logic tests)
856
+ // must not execute the CLI run.
857
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
858
+ run();
859
+ }
860
+
861
+ export { computeGapSlots, computeGridPriceOffsetPct, buildProductionGrid, simulateForParams };