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,35 +1,44 @@
1
+ 'use strict';
1
2
 
2
3
  import path from 'node:path';
3
4
  import os from 'node:os';
4
- import { pathToFileURL } from 'node:url';
5
+ import { pathToFileURL, fileURLToPath } from 'node:url';
5
6
  import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
6
- import { calculateAMA } from '../../market_adapter/core/strategies/ama.js';
7
+ import { calculateAMA, getAmaWarmupBars } from '../../market_adapter/core/strategies/ama.js';
8
+ import { computeAverageAmaSlopePct } from '../../market_adapter/core/strategies/ama_slope_model.js';
7
9
  import { range } from '../math_utils.js';
8
10
  import { parseListOrRange, loadLpData, fmt } from './shared_utils.js';
9
11
  import { getStorage } from '../../modules/storage/index.js';
10
12
  import { PATHS } from '../../modules/paths.js';
13
+ import { GRID_LIMITS, MARKET_ADAPTER } from '../../modules/constants.js';
14
+ // Production grid geometry + slope-ratio offset — shared with bot_fitting so
15
+ // both tools build byte-identical grids for identical params (#12).
16
+ import { buildProductionGrid, computeGridPriceOffsetPct } from './backtest_bot_fitting.js';
11
17
  const { readJSON, writeJSON } = getStorage();
12
- 'use strict';
13
18
 
14
19
  /**
15
20
  * AMA SWEEP BACKTEST — persistent grid simulation
16
21
  *
17
22
  * Models the real bot mechanics:
18
23
  * - Orders sit at FIXED chain prices until canceled or filled
19
- * - When AMA drifts past reposition threshold, grid re-centers
20
- * - Grid compression: AMA shift pushes one side's orders closer to market
24
+ * - Slot rotation: a filled buy re-offers its base ONE RAIL STEP UP; when
25
+ * that refill sells, one increment (minus fees) is booked and the freed
26
+ * quote re-bids one rail step down — anchor-&-refill cycling
27
+ * - Unlinked (initial-grid) sells execute only against held inventory;
28
+ * bought-and-held base carries across resets as a weighted-average-entry
29
+ * position whose final mark is reported informationally, not scored
21
30
  * - Order sizing depends on capital, ratio (range width), and weight profile
22
31
  * - Three weight profiles: valley, neutral, mountain (symmetric buy/sell)
23
32
  *
24
33
  * Usage:
25
- * tsx analysis/bot_fitting/backtest_ama_sweep.ts --data <path-to-lp-candles.json>
26
- * tsx analysis/bot_fitting/backtest_ama_sweep.ts --data <path-to-lp-candles.json> --spread 4:16:1 --increment 0.5:4:0.25
34
+ * node dist/analysis/bot_fitting/backtest_ama_sweep.js --data <path-to-lp-candles.json>
35
+ * node dist/analysis/bot_fitting/backtest_ama_sweep.js --data <path-to-lp-candles.json> --spread 4:16:1 --increment 0.5:4:0.25
27
36
  */
28
37
 
29
-
30
- const DEFAULT_MAX_ORDERS = 20; // matches bot default activeOrders per side
38
+ const DEFAULT_MAX_ORDERS = 20; // weight-profile sizing cap per side (search abstraction)
31
39
  const DEFAULT_FEE_ROUNDTRIP_PCT = 0.20;
32
- const DEFAULT_MIN_SPREAD_FACTOR = 2.1;
40
+ // Canonical spread floor from production grid limits (modules/constants.ts).
41
+ const DEFAULT_MIN_SPREAD_FACTOR = GRID_LIMITS.MIN_SPREAD_FACTOR;
33
42
  const DEFAULT_CAPITAL = 10000; // notional units per side
34
43
  const DEFAULT_BTS_CREATE_FEE = 0.48260;
35
44
  const DEFAULT_BTS_CANCEL_FEE = 0.00482;
@@ -47,13 +56,21 @@ const WEIGHT_PROFILES = {
47
56
  };
48
57
 
49
58
  // Search grid defaults — centered around bot defaults (spread=2%, increment=0.5%)
50
- // Spread = distance from center to first order on each side (half the bid-ask gap)
51
- // Increment = distance between successive orders on the same side
59
+ // Spread = targetSpreadPercent for the gapSlots spread zone (production
60
+ // semantics, matching bot_fitting — the legacy half-spread dead zone is gone).
61
+ // Increment = geometric rail step between successive orders on the same side.
52
62
  const DEFAULT_SPREAD_VALUES = [...range(0.5, 4, 0.25), ...range(5, 12, 1)];
53
63
  const DEFAULT_INCREMENT_VALUES = [...range(0.2, 2, 0.1), ...range(2.5, 8, 0.5)];
54
64
  const DEFAULT_RATIO_VALUES = [1.05, 1.1, 1.15, 1.2, 1.3, 1.5, 2, 3, 5, 10];
55
- // Reposition threshold: AMA must move this fraction from last grid center to trigger re-center
56
- const DEFAULT_REPOSITION_PCT = 2.5;
65
+ // Reposition threshold: production default (MARKET_ADAPTER
66
+ // AMA_DELTA_THRESHOLD_PERCENT = 1%); --reposition overrides.
67
+ const DEFAULT_REPOSITION_PCT = MARKET_ADAPTER.AMA_DELTA_THRESHOLD_PERCENT;
68
+
69
+ // Trigger B / grid-price offset (asymmetricBounds whitelist) — same constants
70
+ // as bot_fitting, shared semantics with the live adapter.
71
+ const SLOPE_TRIGGER_FACTOR = MARKET_ADAPTER.AMA_SLOPE_DELTA_THRESHOLD_PERCENT;
72
+ const SLOPE_MAX_PCT = MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT;
73
+ const SLOPE_LOOKBACK_BARS = MARKET_ADAPTER.DYNAMIC_WEIGHT_AMA_LOOKBACK_BARS;
57
74
 
58
75
 
59
76
  function parseArgs() {
@@ -69,6 +86,7 @@ function parseArgs() {
69
86
  minSpreadFactor: number;
70
87
  capital: number;
71
88
  repositionPct: number;
89
+ asymmetricBounds: boolean;
72
90
  btsCreateFee: number;
73
91
  btsCancelFee: number;
74
92
  makerCreateFactor: number;
@@ -85,6 +103,7 @@ function parseArgs() {
85
103
  minSpreadFactor: DEFAULT_MIN_SPREAD_FACTOR,
86
104
  capital: DEFAULT_CAPITAL,
87
105
  repositionPct: DEFAULT_REPOSITION_PCT,
106
+ asymmetricBounds: false,
88
107
  btsCreateFee: DEFAULT_BTS_CREATE_FEE,
89
108
  btsCancelFee: DEFAULT_BTS_CANCEL_FEE,
90
109
  makerCreateFactor: DEFAULT_BTS_MAKER_CREATE_FACTOR,
@@ -94,6 +113,7 @@ function parseArgs() {
94
113
 
95
114
  for (let i = 0; i < args.length; i++) {
96
115
  const arg = args[i];
116
+ if (arg === '--asymmetric-bounds') { out.asymmetricBounds = true; continue; }
97
117
  const val = args[i + 1];
98
118
  if (arg === '--help' || arg === '-h') { printHelp(); process.exit(0); }
99
119
  if (!val) continue;
@@ -133,14 +153,15 @@ function printHelp() {
133
153
  console.log('Options:');
134
154
  console.log(' --data <path> LP candle JSON');
135
155
  console.log(' --results <path> AMA optimizer results JSON');
136
- console.log(' --spread <spec> Spread values (% ): 1:10:0.5 or 2,4,8');
156
+ console.log(' --spread <spec> Target spread % (gapSlots zone): 1:10:0.5 or 2,4,8');
137
157
  console.log(' --increment <spec> Increment values (%): 0.5:5:0.5 or 1,2,3');
138
158
  console.log(' --ratio <spec> Max/min ratio: 1.5,2,3,5');
139
- console.log(' --max-orders <n> Max orders per side (default: 20, actual limited by ratio+increment)');
159
+ console.log(' --max-orders <n> Size cap per side (default: 20)');
140
160
  console.log(' --fee <pct> Round-trip fee % (default: 0.20)');
141
161
  console.log(' --min-spread-factor <n> Spread >= factor * increment (default: 2.1)');
142
162
  console.log(' --capital <n> Notional capital per side (default: 10000)');
143
- console.log(' --reposition <pct> AMA drift % to trigger re-center (default: 2.5)');
163
+ console.log(' --reposition <pct> AMA drift % to trigger re-center (default: 1.00, production)');
164
+ console.log(' --asymmetric-bounds Enable slope-delta reset (B) + grid price offset (whitelist semantics)');
144
165
  console.log(' --bts-create-fee <n> BTS create fee (default: 0.48260)');
145
166
  console.log(' --bts-cancel-fee <n> BTS cancel fee (default: 0.00482)');
146
167
  console.log(' --maker-create-factor Maker share of create fee (default: 0.10)');
@@ -203,119 +224,135 @@ function allocateFundsByWeights(totalFunds: number, n: number, weight: number, i
203
224
  // ── Persistent grid simulation ───────────────────────────────────────────────
204
225
 
205
226
  /**
206
- * Build a fresh grid centered at `center`.
207
- *
208
- * Grid placement (matches real bot):
209
- * Level k (1-based):
210
- * offset_k = spreadPct/2/100 + (k-1) * incrementPct
211
- * buyPrice = center * (1 - offset_k)
212
- * sellPrice = center * (1 + offset_k)
213
- *
214
- * So spread controls the dead zone around center (no orders within spread/2),
215
- * and increment is the gap between successive orders on the same side.
227
+ * Build a fresh grid centered at `center` using the SHARED production
228
+ * geometry (buildProductionGrid from backtest_bot_fitting — createOrderGrid
229
+ * port): master rail at √(1±inc) offsets bounded by [center/ratio,
230
+ * center*ratio] with a calculateGapSlots spread zone. This guarantees the
231
+ * sweep and bot_fitting build byte-identical grids for identical params
232
+ * (#12). Weight-profile sizing is applied over the capped nearest-to-gap
233
+ * levels, index 0 = closest to the gap.
216
234
  *
217
235
  * Returns arrays of buy and sell order objects with fixed chain prices and sizes.
218
236
  */
219
237
  function buildGrid(center: number, params: any, capitalPerSide: number, weightFactor: number) {
220
238
  const { incrementPct, maxMinRatio, maxOrders, spreadPct } = params;
221
- const minBound = center / maxMinRatio;
222
- const maxBound = center * maxMinRatio;
223
- const halfSpread = spreadPct / 200; // as fraction
224
-
225
- const buys: any[] = [];
226
- const sells: any[] = [];
239
+ const built = buildProductionGrid(center, spreadPct, incrementPct, maxMinRatio, maxOrders);
227
240
 
228
- // Fill as many levels as fit within ratio bounds (up to maxOrders cap)
229
- for (let k = 1; k <= maxOrders; k++) {
230
- const offset = halfSpread + (k - 1) * incrementPct;
231
- if (offset >= 0.95) continue;
232
- const buyPrice = center * (1 - offset);
233
- const sellPrice = center * (1 + offset);
234
-
235
- // Bounds check — skip levels outside ratio range
236
- if (buyPrice < minBound || sellPrice > maxBound) continue;
237
-
238
- buys.push({ level: k, price: buyPrice, filledBar: -1, size: 0 });
239
- sells.push({ level: k, price: sellPrice, filledBar: -1, size: 0 });
240
- }
241
-
242
- // Size allocation — index 0 = closest to center
243
- const buySizes = allocateFundsByWeights(capitalPerSide, buys.length, weightFactor, incrementPct);
244
- const sellSizes = allocateFundsByWeights(capitalPerSide, sells.length, weightFactor, incrementPct);
245
- buys.forEach((o, i) => { o.size = buySizes[i] || 0; });
246
- sells.forEach((o, i) => { o.size = sellSizes[i] || 0; });
247
-
248
- return { buys, sells };
249
- }
241
+ const buySizes = allocateFundsByWeights(capitalPerSide, built.buys.length, weightFactor, incrementPct);
242
+ const sellSizes = allocateFundsByWeights(capitalPerSide, built.sells.length, weightFactor, incrementPct);
250
243
 
251
- function closeFilledInventoryAtPrice(openBuys: Map<any, any>, openSells: Map<any, any>, exitPrice: number, feeRoundtripPct: number) {
252
- if (!Number.isFinite(exitPrice) || exitPrice <= 0) {
253
- return { grossUnits: 0, profitUnits: 0, closedOrders: 0 };
254
- }
255
-
256
- let grossUnits = 0;
257
- let profitUnits = 0;
258
- let closedOrders = 0;
259
-
260
- for (const order of openBuys.values()) {
261
- if (!order?.filled || !Number.isFinite(order.price) || order.price <= 0) continue;
262
- if (!Number.isFinite(order.size) || order.size <= 0) continue;
263
- const grossPct = (exitPrice / order.price - 1) * 100;
264
- const netPct = grossPct - feeRoundtripPct;
265
- grossUnits += order.size * (grossPct / 100);
266
- profitUnits += order.size * (netPct / 100);
267
- closedOrders++;
268
- }
269
-
270
- for (const order of openSells.values()) {
271
- if (!order?.filled || !Number.isFinite(order.price) || order.price <= 0) continue;
272
- if (!Number.isFinite(order.size) || order.size <= 0) continue;
273
- const grossPct = (order.price / exitPrice - 1) * 100;
274
- const netPct = grossPct - feeRoundtripPct;
275
- grossUnits += order.size * (grossPct / 100);
276
- profitUnits += order.size * (netPct / 100);
277
- closedOrders++;
278
- }
279
-
280
- return { grossUnits, profitUnits, closedOrders };
244
+ // Level k = k-th slot from the gap on each side. Each placed slot also
245
+ // carries its MASTER-RAIL index so rotation hops land on adjacent rail
246
+ // nodes (live anchor-&-refill hop) instead of a flat ×(1+inc).
247
+ const buys: any[] = built.buys.map((price: number, i: number) => {
248
+ const k = built.buys.length - i;
249
+ return { level: k, price, railIdx: built.buySliceStart + i, cooldownUntil: -1, size: buySizes[k - 1] || 0 };
250
+ });
251
+ const sells: any[] = built.sells.map((price: number, i: number) => ({
252
+ level: i + 1, price, railIdx: built.sellStartIdx + i, cooldownUntil: -1, size: sellSizes[i] || 0,
253
+ }));
254
+ return { buys, sells, rail: built.rail };
281
255
  }
282
256
 
283
- function countCancelableOrders(openBuys: Map<any, any>, openSells: Map<any, any>): number {
284
- let count = 0;
285
- for (const order of openBuys.values()) {
286
- if (!order?.filled) count++;
287
- }
288
- for (const order of openSells.values()) {
289
- if (!order?.filled) count++;
257
+ /**
258
+ * Mark the held inventory position to `markPrice` in capital units.
259
+ *
260
+ * Carried bags are a REAL risk the drawdown tracker should see bar-by-bar,
261
+ * but their mark is informational for scoring: realized rotation profit plus
262
+ * op fees drive netProfit, and the end-of-run position is reported separately
263
+ * instead of being dumped into totals.
264
+ */
265
+ function markInventoryAtPrice(inventory: { units: number; cost: number }, exitPrice: number, feeRoundtripPct: number) {
266
+ if (!Number.isFinite(exitPrice) || exitPrice <= 0 || inventory.units <= 0) {
267
+ return { grossUnits: 0, profitUnits: 0 };
290
268
  }
291
- return count;
269
+ const avgEntry = inventory.cost / inventory.units;
270
+ const grossUnits = (exitPrice - avgEntry) * inventory.units;
271
+ const feeUnits = avgEntry * inventory.units * ((feeRoundtripPct / 2) / 100);
272
+ return { grossUnits, profitUnits: grossUnits - feeUnits };
292
273
  }
293
274
 
294
275
  function simulatePersistentGrid(candles: any[], amaValues: number[], params: any, weightName: string, weightFactor: number) {
295
- const { spreadPct, incrementPct, maxMinRatio, maxOrders, feeRoundtripPct,
296
- capital, repositionThreshold, btsCreateFee, btsCancelFee,
276
+ const { spreadPct, incrementPct, maxMinRatio, feeRoundtripPct,
277
+ capital, repositionThreshold, asymmetricBounds, btsCreateFee, btsCancelFee,
297
278
  makerCreateFactor, txFeePrice } = params;
298
- const skip = Math.max(20, Math.floor(candles.length * 0.1));
279
+ // Warmup follows production AMA seeding/convergence (getAmaWarmupBars,
280
+ // passed via params.warmupBars from sweepOneAma) instead of an arbitrary
281
+ // fraction of the dataset. Direct callers that omit warmupBars fall back
282
+ // to the legacy 10% cut.
283
+ const warmupParam = Number.isFinite(params.warmupBars)
284
+ ? params.warmupBars
285
+ : Math.max(20, Math.floor(candles.length * 0.1));
286
+ const skip = Math.min(warmupParam, Math.max(0, candles.length - 2));
299
287
  const capitalPerSide = capital;
288
+ const makerCreateFeeBts = btsCreateFee * makerCreateFactor;
289
+ const slopeDeltaThresholdPct = (SLOPE_TRIGGER_FACTOR / 100) * SLOPE_MAX_PCT;
290
+
291
+ // First tradable bar: need a finite positive AMA to anchor the grid.
292
+ let startIdx = Math.min(skip, candles.length - 1);
293
+ let gridCenter = Number.NaN;
294
+ for (let j = startIdx; j < candles.length; j++) {
295
+ const v = amaValues[j];
296
+ if (Number.isFinite(v) && v > 0) { gridCenter = v; startIdx = j; break; }
297
+ }
298
+
299
+ // Production AMA slope series (%/bar averaged over the lookback window) —
300
+ // feeds trigger B and the grid price offset when asymmetricBounds is on.
301
+ const slopeAt: (number | null)[] = new Array(candles.length).fill(null);
302
+ for (let j = SLOPE_LOOKBACK_BARS; j < candles.length; j++) {
303
+ const s = computeAverageAmaSlopePct(amaValues[j], amaValues[j - SLOPE_LOOKBACK_BARS], SLOPE_LOOKBACK_BARS);
304
+ if (s != null && Number.isFinite(s)) slopeAt[j] = s;
305
+ }
300
306
 
301
- // State
302
- let gridCenter = amaValues[skip];
303
- let grid = buildGrid(gridCenter, params, capitalPerSide, weightFactor);
304
- let lastRepositionBar = skip;
307
+ // State — slot-rotation engine (mirrors simulateForParams in
308
+ // backtest_bot_fitting, with weight-profile sized orders):
309
+ // orders: id -> { side, price, size, linkedBuyPrice, linkedEntryBar,
310
+ // cooldownUntil }. linkedBuyPrice != null marks an armed
311
+ // refill sell created by a specific filled buy (one-increment rotation).
312
+ const orders = new Map<number, any>();
313
+ let nextOrderId = 0;
314
+ // Bought-and-held base across the whole run (weighted-average entry pool).
315
+ // Never negative — unfundable sells stay pending instead of shorting.
316
+ const inventory = { units: 0, cost: 0 };
317
+ const stepUpFrac = 1 + incrementPct; // one-rail-step rotation distance
318
+
319
+ let btsFeesBts = 0;
320
+ let offsetAppliedCount = 0;
321
+ // Master rail of the CURRENT epoch — rotation hops read adjacent nodes.
322
+ let activeRail: number[] = [];
323
+
324
+ const placeInitialGrid = (center: number, slopePct: number | null) => {
325
+ const offsetPct = (asymmetricBounds && slopePct != null)
326
+ ? computeGridPriceOffsetPct(slopePct, spreadPct)
327
+ : 0;
328
+ if (offsetPct !== 0) offsetAppliedCount++;
329
+ const effCenter = center * (1 + offsetPct / 100);
330
+ const grid = buildGrid(effCenter, params, capitalPerSide, weightFactor);
331
+ activeRail = grid.rail;
332
+ orders.clear();
333
+ for (const o of grid.buys) {
334
+ orders.set(nextOrderId++, { side: 'buy', price: o.price, size: o.size, railIdx: o.railIdx, linkedBuyPrice: null, linkedEntryBar: -1, cooldownUntil: -1 });
335
+ }
336
+ for (const o of grid.sells) {
337
+ orders.set(nextOrderId++, { side: 'sell', price: o.price, size: o.size, railIdx: o.railIdx, linkedBuyPrice: null, linkedEntryBar: -1, cooldownUntil: -1 });
338
+ }
339
+ btsFeesBts += (grid.buys.length + grid.sells.length) * makerCreateFeeBts;
340
+ };
305
341
 
306
- // Open orders: maps level -> { price, size, bar, side }
307
- const openBuys = new Map();
308
- const openSells = new Map();
309
- // Place initial orders
310
- for (const o of grid.buys) openBuys.set(o.level, { price: o.price, size: o.size, bar: skip });
311
- for (const o of grid.sells) openSells.set(o.level, { price: o.price, size: o.size, bar: skip });
342
+ if (Number.isFinite(gridCenter)) placeInitialGrid(gridCenter, slopeAt[startIdx]);
343
+ let lastRepositionBar = startIdx;
312
344
 
313
345
  let matchedPairs = 0;
314
- let totalProfitUnits = 0; // profit in capital units (size * netPct)
346
+ let cyclesTotal = 0;
347
+ let rotationCount = 0; // linked ping-pong rotations (buy → refill sell)
348
+ let inventorySaleCount = 0; // unlinked sells executed against held bags
349
+ let totalProfitUnits = 0; // REALIZED profit in capital units (size * netPct)
315
350
  let totalGrossUnits = 0;
316
351
  let touchedOrders = 0;
317
352
  let canceledOnReposition = 0;
318
353
  let repositionCount = 0;
354
+ let driftTriggerCount = 0;
355
+ let slopeTriggerCount = 0;
319
356
  let peakOpenOrders = 0;
320
357
  let imbalanceSum = 0;
321
358
  let imbalanceSamples = 0;
@@ -329,22 +366,30 @@ function simulatePersistentGrid(candles: any[], amaValues: number[], params: any
329
366
  let runningProfit = 0;
330
367
  let peakEquityProfit = 0;
331
368
  let maxDrawdown = 0;
332
- // Track inventory risk: net exposure from filled-but-unmatched orders
333
- let inventoryExposure = 0; // positive = long (bought), negative = short (sold)
369
+ // Track inventory risk: bought-and-held base, carried across resets —
370
+ // resets never realize inventory (#4). Long-only (no unfunded shorts).
371
+ let inventoryExposure = 0; // held base units (long-only, ≥ 0)
334
372
  let maxInventoryExposure = 0;
335
373
 
336
- const liveBars = candles.length - skip - 1;
337
- const ordersPerSide = grid.buys.length; // actual orders placed per side (clipped by ratio)
338
- const makerCreateFeeBts = btsCreateFee * makerCreateFactor;
339
- const newOrdersPerReposition = ordersPerSide * 2;
374
+ const invAvgEntry = () => (inventory.units > 0 ? inventory.cost / inventory.units : 0);
375
+ const liveBars = candles.length - startIdx - 1;
376
+ const ordersPerSide = [...orders.values()].filter((o) => o.side === 'buy').length;
377
+
378
+ // Slope-delta baseline: mirrors botState.gridRangeScalingAmaSlope —
379
+ // seeded at bootstrap and re-seeded on every reset.
380
+ let slopeBaseline: number | null = null;
381
+ for (let j = startIdx + 1; j < candles.length; j++) {
382
+ if (slopeAt[j] != null) { slopeBaseline = slopeAt[j]; break; }
383
+ }
340
384
 
341
- for (let i = skip + 1; i < candles.length; i++) {
385
+ for (let i = startIdx + 1; i < candles.length; i++) {
342
386
  const ama = amaValues[i];
343
387
  const hi = candles[i].high;
344
388
  const lo = candles[i].low;
345
389
  const gridAgeBars = i - lastRepositionBar;
346
390
 
347
- // ── Reposition check: AMA drifted too far from grid center ──────
391
+ // ── Reposition check: trigger A (AMA drift, ratchet) or trigger B
392
+ // (slope delta, only under the asymmetricBounds whitelist gate).
348
393
  const drift = Math.abs(ama - gridCenter) / gridCenter;
349
394
  const driftPct = drift * 100;
350
395
  centerDriftSumPct += driftPct;
@@ -352,106 +397,151 @@ function simulatePersistentGrid(candles: any[], amaValues: number[], params: any
352
397
  gridAgeSumBars += gridAgeBars;
353
398
  if (gridAgeBars > maxGridAgeBars) maxGridAgeBars = gridAgeBars;
354
399
  if (drift >= repositionThreshold * 0.5) nearThresholdBars++;
355
- if (drift >= repositionThreshold) {
356
- triggerDriftSumPct += driftPct;
357
- canceledOnReposition += countCancelableOrders(openBuys, openSells);
358
- repositionCount++;
359
- const exitPrice = Number.isFinite(ama) && ama > 0 ? ama : candles[i].close;
360
- const forcedClose = closeFilledInventoryAtPrice(openBuys, openSells, exitPrice, feeRoundtripPct);
361
- totalGrossUnits += forcedClose.grossUnits;
362
- totalProfitUnits += forcedClose.profitUnits;
363
- runningProfit += forcedClose.profitUnits;
364
- // Unmatched inventory is realized on reposition (position closed at market)
365
- inventoryExposure = 0;
366
- openBuys.clear();
367
- openSells.clear();
368
400
 
401
+ let shouldReposition = drift >= repositionThreshold;
402
+ if (shouldReposition) { triggerDriftSumPct += driftPct; driftTriggerCount++; }
403
+ if (!shouldReposition && asymmetricBounds && slopeBaseline != null && slopeAt[i] != null) {
404
+ const slopeDeltaPct = Math.abs(slopeAt[i]! - slopeBaseline);
405
+ if (slopeDeltaPct >= slopeDeltaThresholdPct) { shouldReposition = true; slopeTriggerCount++; }
406
+ }
407
+
408
+ if (shouldReposition && Number.isFinite(ama) && ama > 0) {
409
+ canceledOnReposition += orders.size;
410
+ btsFeesBts += orders.size * btsCancelFee;
411
+ orders.clear(); // inventory survives — resync never market-sells
412
+ repositionCount++;
369
413
  // Re-center grid
370
414
  gridCenter = ama;
371
- grid = buildGrid(gridCenter, params, capitalPerSide, weightFactor);
415
+ if (slopeAt[i] != null) slopeBaseline = slopeAt[i];
416
+ placeInitialGrid(gridCenter, slopeAt[i]);
372
417
  lastRepositionBar = i;
373
-
374
- for (const o of grid.buys) openBuys.set(o.level, { price: o.price, size: o.size, bar: i });
375
- for (const o of grid.sells) openSells.set(o.level, { price: o.price, size: o.size, bar: i });
376
418
  }
377
419
 
378
420
  // ── Track imbalance & peak ──────────────────────────────────────
379
- const currentOpen = openBuys.size + openSells.size;
421
+ const currentOpen = orders.size;
380
422
  if (currentOpen > peakOpenOrders) peakOpenOrders = currentOpen;
381
- imbalanceSum += Math.abs(openBuys.size - openSells.size);
423
+ let buyCountNow = 0;
424
+ for (const [, o] of orders) { if (o.side === 'buy') buyCountNow++; }
425
+ imbalanceSum += Math.abs(buyCountNow - (orders.size - buyCountNow));
382
426
  imbalanceSamples++;
383
427
 
384
- // ── Check fills on PERSISTENT orders (fixed chain prices) ───────
385
- // Only check UNFILLED orders (skip already-filled pending match)
386
- const filledBuysThisBar: any[] = [];
387
- const filledSellsThisBar: any[] = [];
388
-
389
- for (const [lvl, order] of openBuys) {
390
- if (!order.filled && lo <= order.price) {
391
- filledBuysThisBar.push({ lvl, order });
392
- }
393
- }
394
- for (const [lvl, order] of openSells) {
395
- if (!order.filled && hi >= order.price) {
396
- filledSellsThisBar.push({ lvl, order });
397
- }
428
+ // ── Fill detection against FIXED chain prices ───────────────────
429
+ const filledBuysThisBar: { id: number; order: any }[] = [];
430
+ const filledSellsThisBar: { id: number; order: any }[] = [];
431
+ for (const [id, o] of orders) {
432
+ if (i < o.cooldownUntil || !(o.size > 0)) continue;
433
+ if (o.side === 'buy' && lo <= o.price) filledBuysThisBar.push({ id, order: o });
434
+ else if (o.side === 'sell' && hi >= o.price) filledSellsThisBar.push({ id, order: o });
398
435
  }
436
+ touchedOrders += filledBuysThisBar.length + filledSellsThisBar.length;
437
+ // Base held BEFORE this bar's intakes — unlinked sells may only
438
+ // dispose against pre-existing funds (same-bar funding not assumed).
439
+ const disposablesAtBarStart = inventory.units;
399
440
 
400
- // Process buy fills
441
+ // ── Buy intakes first: base enters the inventory pool, refill armed
442
+ // at the ADJACENT MASTER-RAIL NODE above (cooldown blocks same-bar).
401
443
  for (const fb of filledBuysThisBar) {
402
- touchedOrders++;
403
- inventoryExposure += fb.order.size; // bought → long exposure
404
- // Mark as filled, waiting for paired sell at same level
405
- openBuys.set(fb.lvl, { ...fb.order, filled: true, filledBar: i, side: 'buy' });
444
+ orders.delete(fb.id);
445
+ inventory.units += fb.order.size;
446
+ inventory.cost += fb.order.price * fb.order.size;
447
+ const upIdx = (fb.order.railIdx ?? -1) + 1;
448
+ const refillPrice = activeRail[upIdx] ?? fb.order.price * stepUpFrac;
449
+ orders.set(nextOrderId++, {
450
+ side: 'sell',
451
+ price: refillPrice,
452
+ size: fb.order.size,
453
+ railIdx: upIdx,
454
+ linkedBuyPrice: fb.order.price,
455
+ linkedEntryBar: i,
456
+ cooldownUntil: i + 1,
457
+ });
458
+ btsFeesBts += makerCreateFeeBts;
406
459
  }
407
460
 
408
- // Process sell fills
461
+ // ── Sell disposals: linked refills book the one-rail-hop rotation;
462
+ // unlinked sells need held inventory. Linked resolve FIRST.
463
+ filledSellsThisBar.sort((a, b) => ((a.order.linkedBuyPrice != null ? 0 : 1) - (b.order.linkedBuyPrice != null ? 0 : 1)));
464
+ let disposables = disposablesAtBarStart;
409
465
  for (const fs of filledSellsThisBar) {
410
- touchedOrders++;
411
- inventoryExposure -= fs.order.size; // sold → short exposure
412
- // Mark as filled, waiting for paired buy at same level
413
- openSells.set(fs.lvl, { ...fs.order, filled: true, filledBar: i, side: 'sell' });
414
- }
415
-
416
- // Match completed pairs: both buy AND sell at same level are filled
417
- for (let k = 1; k <= maxOrders; k++) {
418
- const buyEntry = openBuys.get(k);
419
- const sellEntry = openSells.get(k);
420
- if (!buyEntry?.filled || !sellEntry?.filled) continue;
421
-
422
- // Matched pair!
423
- openBuys.delete(k);
424
- openSells.delete(k);
425
- matchedPairs++;
426
- const duration = Math.abs(sellEntry.filledBar - buyEntry.filledBar);
427
- matchedOpenDurationBars += duration;
428
- const avgSize = (buyEntry.size + sellEntry.size) / 2;
429
- const pairGrossPct = (sellEntry.price / buyEntry.price - 1) * 100;
430
- const pairNetPct = pairGrossPct - feeRoundtripPct;
431
- const profitUnits = avgSize * (pairNetPct / 100);
432
- totalGrossUnits += avgSize * (pairGrossPct / 100);
433
- totalProfitUnits += profitUnits;
434
- runningProfit += profitUnits;
466
+ const order = fs.order;
467
+ const size = order.size;
468
+ if (order.linkedBuyPrice != null) {
469
+ const grossPct = (order.price / order.linkedBuyPrice - 1) * 100;
470
+ const netPct = grossPct - feeRoundtripPct;
471
+ totalGrossUnits += size * (grossPct / 100);
472
+ totalProfitUnits += size * (netPct / 100);
473
+ runningProfit += size * (netPct / 100);
474
+ cyclesTotal++;
475
+ rotationCount++;
476
+ matchedPairs++;
477
+ matchedOpenDurationBars += Math.abs(i - order.linkedEntryBar);
478
+ // Dispose the base this rotation bought (at pool-average cost).
479
+ const applied = Math.min(size, inventory.units);
480
+ inventory.cost -= applied * invAvgEntry();
481
+ inventory.units -= applied;
482
+ // Its disposal also drains the pre-bar funding budget —
483
+ // otherwise later unlinked sales could overspend stock.
484
+ disposables -= applied;
485
+ // Freed quote re-bids the ADJACENT RAIL NODE below.
486
+ const downIdx = (order.railIdx ?? 0) - 1;
487
+ const rebidPrice = activeRail[downIdx] ?? order.price / stepUpFrac;
488
+ orders.delete(fs.id);
489
+ orders.set(nextOrderId++, {
490
+ side: 'buy',
491
+ price: rebidPrice,
492
+ size,
493
+ railIdx: downIdx,
494
+ linkedBuyPrice: null,
495
+ linkedEntryBar: -1,
496
+ cooldownUntil: i + 1,
497
+ });
498
+ btsFeesBts += makerCreateFeeBts;
499
+ } else if (disposables >= size - 1e-12) {
500
+ const avgEntry = invAvgEntry();
501
+ const grossPct = (order.price / avgEntry - 1) * 100;
502
+ const netPct = grossPct - feeRoundtripPct;
503
+ totalGrossUnits += size * (grossPct / 100);
504
+ totalProfitUnits += size * (netPct / 100);
505
+ runningProfit += size * (netPct / 100);
506
+ cyclesTotal++;
507
+ inventorySaleCount++;
508
+ matchedPairs++;
509
+ inventory.cost -= avgEntry * size;
510
+ inventory.units -= size;
511
+ disposables -= size;
512
+ orders.delete(fs.id); // sold bag is gone; slot not re-armed
513
+ } else {
514
+ // Unfundable (not enough base): stays open, retries next bar.
515
+ order.cooldownUntil = i + 1;
516
+ }
435
517
  }
436
518
 
437
- // Track max inventory exposure
438
- const absExposure = Math.abs(inventoryExposure);
439
- if (absExposure > maxInventoryExposure) maxInventoryExposure = absExposure;
519
+ // Track max inventory exposure (long-only)
520
+ if (inventory.units > maxInventoryExposure) maxInventoryExposure = inventory.units;
440
521
 
441
522
  // ── Drawdown tracking ───────────────────────────────────────────
442
- const markPrice = Number.isFinite(candles[i].close) && candles[i].close > 0
443
- ? candles[i].close
444
- : ((Number.isFinite(ama) && ama > 0) ? ama : gridCenter);
445
- const unrealized = closeFilledInventoryAtPrice(openBuys, openSells, markPrice, feeRoundtripPct).profitUnits;
446
- const equityProfit = runningProfit + unrealized;
523
+ // Realized equity only: carried bags are reported informationally and
524
+ // excluded from scoring, so their (unbounded, balance-free) marks must
525
+ // not distort the risk term either. Bag risk stays visible via
526
+ // finalInventoryUnits / finalInventoryMarkUnits.
527
+ const equityProfit = runningProfit;
447
528
  if (equityProfit > peakEquityProfit) peakEquityProfit = equityProfit;
448
529
  const dd = peakEquityProfit - equityProfit;
449
530
  if (dd > maxDrawdown) maxDrawdown = dd;
450
531
  }
451
532
 
452
- const fillEfficiency = touchedOrders > 0 ? (matchedPairs / touchedOrders) * 100 : 0;
533
+ // ── End-of-run inventory mark (informational, NOT in profit) ────────
534
+ // Bought-and-held base is real carried risk but unrealized bag marks are
535
+ // excluded from netProfit/scoring so trend combos can't dump phantom
536
+ // paper profit into the objective. Drawdown likewise tracks REALIZED
537
+ // equity only; bag risk stays visible through these info fields.
538
+ inventoryExposure = inventory.units;
539
+ const lastClose = candles.length > 0 ? candles[candles.length - 1].close : NaN;
540
+ const finalInventoryMark = markInventoryAtPrice(inventory, lastClose, feeRoundtripPct);
541
+
542
+ const fillEfficiency = touchedOrders > 0 ? (cyclesTotal / touchedOrders) * 100 : 0;
453
543
  const pairsPerDay = liveBars > 0 ? matchedPairs / (liveBars / 24) : 0;
454
- const avgOpenDurationBars = matchedPairs > 0 ? matchedOpenDurationBars / matchedPairs : 0;
544
+ const avgOpenDurationBars = rotationCount > 0 ? matchedOpenDurationBars / rotationCount : 0;
455
545
  const avgImbalance = imbalanceSamples > 0 ? imbalanceSum / imbalanceSamples : 0;
456
546
  const avgProfitPerPair = matchedPairs > 0 ? totalProfitUnits / matchedPairs : 0;
457
547
  const profitPerCapital = capital > 0 ? totalProfitUnits / (capital * 2) : 0; // total capital = 2 sides
@@ -461,9 +551,11 @@ function simulatePersistentGrid(candles: any[], amaValues: number[], params: any
461
551
  const nearThresholdBarsPct = liveBars > 0 ? (nearThresholdBars / liveBars) * 100 : 0;
462
552
  const avgTriggerDriftPct = repositionCount > 0 ? triggerDriftSumPct / repositionCount : 0;
463
553
  const avgCancelOrdersPerReposition = repositionCount > 0 ? canceledOnReposition / repositionCount : 0;
464
- const estimatedFeePerRepositionBts = newOrdersPerReposition * makerCreateFeeBts + avgCancelOrdersPerReposition * btsCancelFee;
465
- const totalRepositionFeesBts = repositionCount * estimatedFeePerRepositionBts;
554
+ // Exact BTS fee totals (creates + cancels + per-cycle refills), not an
555
+ // estimate — the live bot pays the same per-op fees.
556
+ const totalRepositionFeesBts = btsFeesBts;
466
557
  const feePerDayBts = liveBars > 0 ? totalRepositionFeesBts / (liveBars / 24) : 0;
558
+ const estimatedFeePerRepositionBts = repositionCount > 0 ? totalRepositionFeesBts / repositionCount : 0;
467
559
  const totalRepositionFeeUnits = totalRepositionFeesBts * txFeePrice;
468
560
  const netProfitUnits = totalProfitUnits - totalRepositionFeeUnits;
469
561
  const netProfitPerCapital = capital > 0 ? netProfitUnits / (capital * 2) : 0;
@@ -480,10 +572,16 @@ function simulatePersistentGrid(candles: any[], amaValues: number[], params: any
480
572
  incrementPct: incrementPct * 100, // store as %
481
573
  maxMinRatio,
482
574
  matchedPairs,
575
+ cyclesTotal,
576
+ rotationCount,
577
+ inventorySaleCount,
483
578
  touchedOrders,
484
579
  fillEfficiency,
485
580
  totalProfitUnits,
486
581
  totalGrossUnits,
582
+ finalInventoryUnits: inventoryExposure,
583
+ finalInventoryAvgEntry: inventory.units > 0 ? inventory.cost / inventory.units : 0,
584
+ finalInventoryMarkUnits: finalInventoryMark.profitUnits, // informational
487
585
  profitPerCapital,
488
586
  pairsPerDay,
489
587
  avgOpenDurationBars,
@@ -492,6 +590,8 @@ function simulatePersistentGrid(candles: any[], amaValues: number[], params: any
492
590
  avgImbalance,
493
591
  canceledOnReposition,
494
592
  repositionCount,
593
+ driftTriggerCount,
594
+ slopeTriggerCount,
495
595
  maxDrawdown,
496
596
  maxDrawdownPct,
497
597
  maxInventoryExposure,
@@ -501,6 +601,7 @@ function simulatePersistentGrid(candles: any[], amaValues: number[], params: any
501
601
  maxCenterDriftPct,
502
602
  nearThresholdBarsPct,
503
603
  avgTriggerDriftPct,
604
+ offsetAppliedCount,
504
605
  makerCreateFeeBts,
505
606
  btsCancelFee,
506
607
  avgCancelOrdersPerReposition,
@@ -520,6 +621,8 @@ function simulatePersistentGrid(candles: any[], amaValues: number[], params: any
520
621
 
521
622
  function sweepOneAma(strategy: any, candles: any[], closes: number[], weightEntries: [string, number][], cfg: any) {
522
623
  const amaValues = calculateAMA(closes, { erPeriod: strategy.er, fastPeriod: strategy.fast, slowPeriod: strategy.slow });
624
+ // Production-aligned warmup: ER window + convergence (getAmaWarmupBars).
625
+ const warmupBars = getAmaWarmupBars(strategy.er, strategy.slow, 0, strategy.fast);
523
626
  let best: any = null;
524
627
  const top5: any[] = [];
525
628
  const allSims: any[] = [];
@@ -541,10 +644,12 @@ function sweepOneAma(strategy: any, candles: any[], closes: number[], weightEntr
541
644
  feeRoundtripPct: cfg.feeRoundtripPct,
542
645
  capital: cfg.capital,
543
646
  repositionThreshold: cfg.repositionPct / 100,
647
+ asymmetricBounds: cfg.asymmetricBounds,
544
648
  btsCreateFee: cfg.btsCreateFee,
545
649
  btsCancelFee: cfg.btsCancelFee,
546
650
  makerCreateFactor: cfg.makerCreateFactor,
547
651
  txFeePrice: cfg.txFeePrice,
652
+ warmupBars,
548
653
  }, weightName, weightFactor);
549
654
 
550
655
  if (!best || sim.score > best.score) best = sim;
@@ -573,8 +678,10 @@ function sweepOneAma(strategy: any, candles: any[], closes: number[], weightEntr
573
678
  if (!isMainThread) {
574
679
  const { strategy, candles, closes, weightEntries, cfg } = workerData;
575
680
  const result = sweepOneAma(strategy, candles, closes, weightEntries, cfg);
576
- parentPort!.postMessage(result);
577
- process.exit(0);
681
+ parentPort!.postMessage(result);
682
+ // Exit naturally: an explicit process.exit(0) here can race the
683
+ // postMessage flush and silently drop the result (same pattern as
684
+ // optimizer_high_resolution.ts workers, which exit on their own).
578
685
  }
579
686
 
580
687
  // ── Parallel dispatch (main thread) ─────────────────────────────────────────
@@ -585,7 +692,9 @@ function runParallel(strategies: any[], candles: any[], closes: number[], weight
585
692
 
586
693
  return Promise.all(strategies.map((strategy) => {
587
694
  return new Promise((resolve, reject) => {
588
- const worker = new Worker(__filename, {
695
+ // ESM: resolve this module's path from import.meta.url
696
+ // (__filename is undefined in ES modules).
697
+ const worker = new Worker(fileURLToPath(import.meta.url), {
589
698
  workerData: { strategy, candles, closes, weightEntries, cfg },
590
699
  });
591
700
  worker.on('message', resolve);
@@ -620,9 +729,11 @@ async function run() {
620
729
  console.log(` Spread: ${cfg.spreadValues[0]}..${cfg.spreadValues[cfg.spreadValues.length - 1]}% (${cfg.spreadValues.length})`);
621
730
  console.log(` Increment: ${cfg.incrementValues[0]}..${cfg.incrementValues[cfg.incrementValues.length - 1]}% (${cfg.incrementValues.length})`);
622
731
  console.log(` Ratio: ${cfg.ratioValues[0]}..${cfg.ratioValues[cfg.ratioValues.length - 1]} (${cfg.ratioValues.length})`);
623
- console.log(` Max orders: ${cfg.maxOrders}/side (actual count from ratio+increment) | Capital: ${cfg.capital}/side | Fee: ${cfg.feeRoundtripPct}%`);
732
+ console.log(` Max orders: ${cfg.maxOrders}/side (size cap) | Capital: ${cfg.capital}/side | Fee: ${cfg.feeRoundtripPct}%`);
624
733
  console.log(` Spread floor: > fee (${cfg.feeRoundtripPct}%)`);
625
- console.log(` Reposition: ${cfg.repositionPct}% AMA drift from grid center`);
734
+ console.log(` Reset (A): ${cfg.repositionPct}% AMA drift from grid center (ratchet)`);
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| >= ${fmt((SLOPE_TRIGGER_FACTOR / 100) * SLOPE_MAX_PCT, 4)}% (lookback ${SLOPE_LOOKBACK_BARS})${cfg.asymmetricBounds ? '' : ' [gated off]'}`);
626
737
  console.log(` Tx model: create=${fmt(cfg.btsCreateFee, 5)} BTS, cancel=${fmt(cfg.btsCancelFee, 5)} BTS, maker=${fmt(cfg.makerCreateFactor * 100, 1)}%, 1 BTS=${fmt(cfg.txFeePrice, 2)} units`);
627
738
  console.log(` Combos/AMA: ${totalCombos} | Total: ${totalCombos * strategies.length}\n`);
628
739
 
@@ -755,7 +866,9 @@ async function run() {
755
866
  console.log(` Repositions: ${winner.sim.repositionCount}`);
756
867
  console.log(` Grid age: avg ${fmt(winner.sim.avgGridAgeBars, 1)} bars | max ${fmt(winner.sim.maxGridAgeBars, 0)} bars`);
757
868
  console.log(` Drift: avg ${fmt(winner.sim.avgCenterDriftPct, 2)}% | max ${fmt(winner.sim.maxCenterDriftPct, 2)}% | near-threshold ${fmt(winner.sim.nearThresholdBarsPct, 1)}%`);
758
- console.log(` Tx burn: ${fmt(winner.sim.estimatedFeePerRepositionBts, 4)} BTS/reposition | ${fmt(winner.sim.feePerDayBts, 2)} BTS/day`);
869
+ console.log(` Tx burn: ${fmt(winner.sim.estimatedFeePerRepositionBts, 4)} BTS/reposition | ${fmt(winner.sim.feePerDayBts, 2)} BTS/day (exact per-op totals)`);
870
+ console.log(` Cycles: ${winner.sim.cyclesTotal} rotations (${winner.sim.rotationCount} rail-step + ${winner.sim.inventorySaleCount} inventory sales)`);
871
+ console.log(` Carried: ${fmt(winner.sim.finalInventoryUnits, 4)} base @ ${fmt(winner.sim.finalInventoryAvgEntry, 6)} avg (mark ${fmt(winner.sim.finalInventoryMarkUnits, 2)} units — info only, not scored)`);
759
872
  console.log(` Score: ${fmt(winner.sim.score, 2)}`);
760
873
 
761
874
  // ── Save JSON ───────────────────────────────────────────────────────────
@@ -772,6 +885,7 @@ async function run() {
772
885
  capitalPerSide: cfg.capital,
773
886
  feeRoundtripPct: cfg.feeRoundtripPct,
774
887
  repositionPct: cfg.repositionPct,
888
+ asymmetricBounds: cfg.asymmetricBounds,
775
889
  btsCreateFee: cfg.btsCreateFee,
776
890
  btsCancelFee: cfg.btsCancelFee,
777
891
  makerCreateFactor: cfg.makerCreateFactor,
@@ -786,6 +900,7 @@ async function run() {
786
900
  totalCombos: totalCombos * strategies.length,
787
901
  },
788
902
  scoring: 'netProfitPerCapital * 100 * log10(max(1, matchedPairs)) - maxDrawdownPct * 0.5',
903
+ gridModel: 'production createOrderGrid port (shared with bot_fitting): master rail sqrt(1±inc) + gapSlots spread zone; slot rotation (filled buy re-offers one rail step up, freed quote re-bids one step down, ~increment% per completed rotation); unlinked sells execute only against held inventory at weighted-average entry; bought base carries across resets and the end-of-run inventory mark is informational (excluded from scoring); exact per-op BTS fees',
789
904
  },
790
905
  strategies,
791
906
  perAma: byAma.map((row) => ({
@@ -809,9 +924,11 @@ async function run() {
809
924
  console.log(`\nSaved: ${path.relative(process.cwd(), outPath)}`);
810
925
  }
811
926
 
812
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
927
+ // Main-thread only: workers inherit process.argv[1], so the entry guard alone
928
+ // would also match inside workers and re-run main() there.
929
+ if (isMainThread && process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
813
930
  run().catch((err) => { console.error(err); process.exit(1); });
814
931
  }
815
932
 
816
- export { WEIGHT_PROFILES, allocateFundsByWeights, buildGrid, closeFilledInventoryAtPrice, countCancelableOrders, simulatePersistentGrid, sweepOneAma }
933
+ export { WEIGHT_PROFILES, allocateFundsByWeights, buildGrid, markInventoryAtPrice, simulatePersistentGrid, sweepOneAma }
817
934