@reefclaw/connect 0.1.12 → 0.1.13

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 (390) hide show
  1. package/assets/plugin/audit/mode-transition-audit.d.ts +11 -0
  2. package/assets/plugin/audit/mode-transition-audit.js +29 -0
  3. package/assets/plugin/balance-utils.d.ts +36 -0
  4. package/assets/plugin/balance-utils.js +98 -0
  5. package/assets/plugin/ccxt/binance-ban-gate.d.ts +47 -0
  6. package/assets/plugin/ccxt/binance-ban-gate.js +420 -0
  7. package/assets/plugin/ccxt/binance-private.d.ts +346 -0
  8. package/assets/plugin/ccxt/binance-private.js +1525 -0
  9. package/assets/plugin/ccxt/binance-public.d.ts +34 -0
  10. package/assets/plugin/ccxt/binance-public.js +180 -0
  11. package/assets/plugin/ccxt/intel-public.d.ts +25 -0
  12. package/assets/plugin/ccxt/intel-public.js +80 -0
  13. package/assets/plugin/ccxt/public-market-data-api.d.ts +26 -0
  14. package/assets/plugin/ccxt/public-market-data-api.js +23 -0
  15. package/assets/plugin/config/agent-config-client.d.ts +77 -0
  16. package/assets/plugin/config/agent-config-client.js +188 -0
  17. package/assets/plugin/config/agent-config-poller.d.ts +32 -0
  18. package/assets/plugin/config/agent-config-poller.js +101 -0
  19. package/assets/plugin/config/brackets-config.d.ts +22 -0
  20. package/assets/plugin/config/brackets-config.js +58 -0
  21. package/assets/plugin/config/entitlement-gate.d.ts +51 -0
  22. package/assets/plugin/config/entitlement-gate.js +137 -0
  23. package/assets/plugin/config/gate-store.d.ts +18 -0
  24. package/assets/plugin/config/gate-store.js +61 -0
  25. package/assets/plugin/config/plugin-config-io.d.ts +222 -0
  26. package/assets/plugin/config/plugin-config-io.js +126 -0
  27. package/assets/plugin/config/position-review-config.d.ts +35 -0
  28. package/assets/plugin/config/position-review-config.js +105 -0
  29. package/assets/plugin/config/tool-gate.d.ts +53 -0
  30. package/assets/plugin/config/tool-gate.js +128 -0
  31. package/assets/plugin/config/user-data-stream-config.d.ts +89 -0
  32. package/assets/plugin/config/user-data-stream-config.js +239 -0
  33. package/assets/plugin/connector-supervisor.d.ts +36 -0
  34. package/assets/plugin/connector-supervisor.js +149 -0
  35. package/assets/plugin/exchange-adapter.d.ts +65 -0
  36. package/assets/plugin/exchange-adapter.js +4 -0
  37. package/assets/plugin/index.d.ts +30 -0
  38. package/assets/plugin/index.js +2763 -0
  39. package/assets/plugin/ingest/pending-entry-metadata.d.ts +74 -0
  40. package/assets/plugin/ingest/pending-entry-metadata.js +236 -0
  41. package/assets/plugin/ingest/position-auto-capture.d.ts +171 -0
  42. package/assets/plugin/ingest/position-auto-capture.js +709 -0
  43. package/assets/plugin/ingest/position-decisions-client.d.ts +327 -0
  44. package/assets/plugin/ingest/position-decisions-client.js +306 -0
  45. package/assets/plugin/ingest/readiness-reporter.d.ts +42 -0
  46. package/assets/plugin/ingest/readiness-reporter.js +163 -0
  47. package/assets/plugin/ingest/reconcile-db-vs-exchange.d.ts +51 -0
  48. package/assets/plugin/ingest/reconcile-db-vs-exchange.js +170 -0
  49. package/assets/plugin/ingest/reconciler-cleanup.d.ts +37 -0
  50. package/assets/plugin/ingest/reconciler-cleanup.js +147 -0
  51. package/assets/plugin/ingest/rest-gap-filler.d.ts +191 -0
  52. package/assets/plugin/ingest/rest-gap-filler.js +565 -0
  53. package/assets/plugin/ingest/touched-symbols-store.d.ts +25 -0
  54. package/assets/plugin/ingest/touched-symbols-store.js +96 -0
  55. package/assets/plugin/ingest/trade-store-client.d.ts +40 -0
  56. package/assets/plugin/ingest/trade-store-client.js +125 -0
  57. package/assets/plugin/ingest/ws-ingest.d.ts +43 -0
  58. package/assets/plugin/ingest/ws-ingest.js +126 -0
  59. package/assets/plugin/learning/setup-family.d.ts +21 -0
  60. package/assets/plugin/learning/setup-family.js +103 -0
  61. package/assets/plugin/lifecycle/install-signal-handlers.d.ts +33 -0
  62. package/assets/plugin/lifecycle/install-signal-handlers.js +112 -0
  63. package/assets/plugin/lifecycle/shutdown-coordinator.d.ts +43 -0
  64. package/assets/plugin/lifecycle/shutdown-coordinator.js +131 -0
  65. package/assets/plugin/lifecycle/trading-operation-lock.d.ts +17 -0
  66. package/assets/plugin/lifecycle/trading-operation-lock.js +14 -0
  67. package/assets/plugin/live/bracket-id.d.ts +26 -0
  68. package/assets/plugin/live/bracket-id.js +112 -0
  69. package/assets/plugin/live/bracket-ledger.d.ts +54 -0
  70. package/assets/plugin/live/bracket-ledger.js +267 -0
  71. package/assets/plugin/live/bracket-manager.d.ts +82 -0
  72. package/assets/plugin/live/bracket-manager.js +478 -0
  73. package/assets/plugin/live/bracket-params.d.ts +22 -0
  74. package/assets/plugin/live/bracket-params.js +124 -0
  75. package/assets/plugin/live/bracket-reconciler.d.ts +95 -0
  76. package/assets/plugin/live/bracket-reconciler.js +573 -0
  77. package/assets/plugin/live/bracket-types.d.ts +102 -0
  78. package/assets/plugin/live/bracket-types.js +8 -0
  79. package/assets/plugin/live/deposit-tracker.d.ts +62 -0
  80. package/assets/plugin/live/deposit-tracker.js +97 -0
  81. package/assets/plugin/live/emergency-controls.d.ts +32 -0
  82. package/assets/plugin/live/emergency-controls.js +226 -0
  83. package/assets/plugin/live/exchange-errors.d.ts +12 -0
  84. package/assets/plugin/live/exchange-errors.js +130 -0
  85. package/assets/plugin/live/exchange-info-cache.d.ts +37 -0
  86. package/assets/plugin/live/exchange-info-cache.js +134 -0
  87. package/assets/plugin/live/fact-subscriber.d.ts +78 -0
  88. package/assets/plugin/live/fact-subscriber.js +182 -0
  89. package/assets/plugin/live/fill-price.d.ts +13 -0
  90. package/assets/plugin/live/fill-price.js +37 -0
  91. package/assets/plugin/live/intent-journal.d.ts +42 -0
  92. package/assets/plugin/live/intent-journal.js +122 -0
  93. package/assets/plugin/live/listen-key-manager.d.ts +70 -0
  94. package/assets/plugin/live/listen-key-manager.js +169 -0
  95. package/assets/plugin/live/live-adapter.d.ts +319 -0
  96. package/assets/plugin/live/live-adapter.js +1906 -0
  97. package/assets/plugin/live/live-balance-enricher.d.ts +32 -0
  98. package/assets/plugin/live/live-balance-enricher.js +104 -0
  99. package/assets/plugin/live/live-bracket-api.d.ts +13 -0
  100. package/assets/plugin/live/live-bracket-api.js +20 -0
  101. package/assets/plugin/live/live-state-store.d.ts +194 -0
  102. package/assets/plugin/live/live-state-store.js +450 -0
  103. package/assets/plugin/live/local-signal-service.d.ts +57 -0
  104. package/assets/plugin/live/local-signal-service.js +151 -0
  105. package/assets/plugin/live/local-strategy-evaluator.d.ts +62 -0
  106. package/assets/plugin/live/local-strategy-evaluator.js +131 -0
  107. package/assets/plugin/live/microstructure-assembler.d.ts +54 -0
  108. package/assets/plugin/live/microstructure-assembler.js +148 -0
  109. package/assets/plugin/live/order-poller.d.ts +29 -0
  110. package/assets/plugin/live/order-poller.js +125 -0
  111. package/assets/plugin/live/position-state-store.d.ts +87 -0
  112. package/assets/plugin/live/position-state-store.js +237 -0
  113. package/assets/plugin/live/proposal-decision-listener.d.ts +70 -0
  114. package/assets/plugin/live/proposal-decision-listener.js +292 -0
  115. package/assets/plugin/live/proposal-manager.d.ts +76 -0
  116. package/assets/plugin/live/proposal-manager.js +140 -0
  117. package/assets/plugin/live/rate-limiter.d.ts +47 -0
  118. package/assets/plugin/live/rate-limiter.js +159 -0
  119. package/assets/plugin/live/reconciler.d.ts +39 -0
  120. package/assets/plugin/live/reconciler.js +175 -0
  121. package/assets/plugin/live/setup-buckets.d.ts +7 -0
  122. package/assets/plugin/live/setup-buckets.js +33 -0
  123. package/assets/plugin/live/slippage-tracker.d.ts +45 -0
  124. package/assets/plugin/live/slippage-tracker.js +78 -0
  125. package/assets/plugin/live/stop-watcher.d.ts +67 -0
  126. package/assets/plugin/live/stop-watcher.js +218 -0
  127. package/assets/plugin/live/user-data-active-probe.d.ts +54 -0
  128. package/assets/plugin/live/user-data-active-probe.js +180 -0
  129. package/assets/plugin/live/user-data-stream-controller.d.ts +200 -0
  130. package/assets/plugin/live/user-data-stream-controller.js +579 -0
  131. package/assets/plugin/live/user-data-stream-ws.d.ts +22 -0
  132. package/assets/plugin/live/user-data-stream-ws.js +63 -0
  133. package/assets/plugin/live/user-data-stream.d.ts +243 -0
  134. package/assets/plugin/live/user-data-stream.js +704 -0
  135. package/assets/plugin/logger.d.ts +2 -0
  136. package/assets/plugin/logger.js +2 -0
  137. package/assets/plugin/mfe.d.ts +21 -0
  138. package/assets/plugin/mfe.js +68 -0
  139. package/assets/plugin/onboarding/mode-ladder.d.ts +1 -0
  140. package/assets/plugin/onboarding/mode-ladder.js +3 -0
  141. package/assets/plugin/onboarding/runtime.d.ts +102 -0
  142. package/assets/plugin/onboarding/runtime.js +215 -0
  143. package/assets/plugin/openclaw.plugin.json +92 -0
  144. package/assets/plugin/paper-adapter.d.ts +24 -0
  145. package/assets/plugin/paper-adapter.js +91 -0
  146. package/assets/plugin/persistence/state-manager.d.ts +66 -0
  147. package/assets/plugin/persistence/state-manager.js +222 -0
  148. package/assets/plugin/pinned-plan.d.ts +9 -0
  149. package/assets/plugin/pinned-plan.js +23 -0
  150. package/assets/plugin/portfolio/reentry-tracker.d.ts +36 -0
  151. package/assets/plugin/portfolio/reentry-tracker.js +127 -0
  152. package/assets/plugin/portfolio/wave9-admission.d.ts +67 -0
  153. package/assets/plugin/portfolio/wave9-admission.js +262 -0
  154. package/assets/plugin/portfolio/wave9-policy.d.ts +36 -0
  155. package/assets/plugin/portfolio/wave9-policy.js +183 -0
  156. package/assets/plugin/risk/pre-trade-check.d.ts +38 -0
  157. package/assets/plugin/risk/pre-trade-check.js +345 -0
  158. package/assets/plugin/risk/pre-trade-types.d.ts +60 -0
  159. package/assets/plugin/risk/pre-trade-types.js +3 -0
  160. package/assets/plugin/shadow/shadow-tracker.d.ts +36 -0
  161. package/assets/plugin/shadow/shadow-tracker.js +151 -0
  162. package/assets/plugin/shadow/types.d.ts +42 -0
  163. package/assets/plugin/shadow/types.js +20 -0
  164. package/assets/plugin/shared/indicators-extended.d.ts +52 -0
  165. package/assets/plugin/shared/indicators-extended.js +291 -0
  166. package/assets/plugin/shared/indicators.d.ts +15 -0
  167. package/assets/plugin/shared/indicators.js +114 -0
  168. package/assets/plugin/signals/conditions/registry.d.ts +16 -0
  169. package/assets/plugin/signals/conditions/registry.js +1333 -0
  170. package/assets/plugin/signals/conditions/types.d.ts +1 -0
  171. package/assets/plugin/signals/conditions/types.js +4 -0
  172. package/assets/plugin/signals/direction-rules.d.ts +3 -0
  173. package/assets/plugin/signals/direction-rules.js +24 -0
  174. package/assets/plugin/signals/entry-rules.d.ts +6 -0
  175. package/assets/plugin/signals/entry-rules.js +33 -0
  176. package/assets/plugin/signals/serialize-context.d.ts +4 -0
  177. package/assets/plugin/signals/serialize-context.js +39 -0
  178. package/assets/plugin/signals/stop-rules.d.ts +3 -0
  179. package/assets/plugin/signals/stop-rules.js +48 -0
  180. package/assets/plugin/signals/strategy-adapter.d.ts +47 -0
  181. package/assets/plugin/signals/strategy-adapter.js +209 -0
  182. package/assets/plugin/signals/types.d.ts +1 -0
  183. package/assets/plugin/signals/types.js +8 -0
  184. package/assets/plugin/simulator/exchange-simulator.d.ts +105 -0
  185. package/assets/plugin/simulator/exchange-simulator.js +759 -0
  186. package/assets/plugin/simulator/fill-engine.d.ts +53 -0
  187. package/assets/plugin/simulator/fill-engine.js +280 -0
  188. package/assets/plugin/simulator/paper-market-feed.d.ts +26 -0
  189. package/assets/plugin/simulator/paper-market-feed.js +104 -0
  190. package/assets/plugin/simulator/realistic-fills.d.ts +59 -0
  191. package/assets/plugin/simulator/realistic-fills.js +175 -0
  192. package/assets/plugin/simulator/types.d.ts +228 -0
  193. package/assets/plugin/simulator/types.js +43 -0
  194. package/assets/plugin/strategy/builtin-strategies.d.ts +2 -0
  195. package/assets/plugin/strategy/builtin-strategies.js +113 -0
  196. package/assets/plugin/strategy/condition-registry.d.ts +3 -0
  197. package/assets/plugin/strategy/condition-registry.js +153 -0
  198. package/assets/plugin/strategy/evaluator.d.ts +70 -0
  199. package/assets/plugin/strategy/evaluator.js +98 -0
  200. package/assets/plugin/tools/assessment-validation.d.ts +141 -0
  201. package/assets/plugin/tools/assessment-validation.js +473 -0
  202. package/assets/plugin/tools/attach-brackets.d.ts +39 -0
  203. package/assets/plugin/tools/attach-brackets.js +564 -0
  204. package/assets/plugin/tools/audit-bracket-protection.d.ts +49 -0
  205. package/assets/plugin/tools/audit-bracket-protection.js +683 -0
  206. package/assets/plugin/tools/bracket-control.d.ts +12 -0
  207. package/assets/plugin/tools/bracket-control.js +35 -0
  208. package/assets/plugin/tools/cancel-all-orders.d.ts +9 -0
  209. package/assets/plugin/tools/cancel-all-orders.js +8 -0
  210. package/assets/plugin/tools/cancel-order.d.ts +14 -0
  211. package/assets/plugin/tools/cancel-order.js +60 -0
  212. package/assets/plugin/tools/check-position-health.d.ts +46 -0
  213. package/assets/plugin/tools/check-position-health.js +194 -0
  214. package/assets/plugin/tools/clear-exchange-credentials.d.ts +24 -0
  215. package/assets/plugin/tools/clear-exchange-credentials.js +70 -0
  216. package/assets/plugin/tools/close-position.d.ts +45 -0
  217. package/assets/plugin/tools/close-position.js +722 -0
  218. package/assets/plugin/tools/create-order.d.ts +89 -0
  219. package/assets/plugin/tools/create-order.js +1555 -0
  220. package/assets/plugin/tools/exit-gate.d.ts +58 -0
  221. package/assets/plugin/tools/exit-gate.js +162 -0
  222. package/assets/plugin/tools/fetch-balance.d.ts +5 -0
  223. package/assets/plugin/tools/fetch-balance.js +4 -0
  224. package/assets/plugin/tools/fetch-ohlcv.d.ts +11 -0
  225. package/assets/plugin/tools/fetch-ohlcv.js +8 -0
  226. package/assets/plugin/tools/fetch-open-orders.d.ts +7 -0
  227. package/assets/plugin/tools/fetch-open-orders.js +4 -0
  228. package/assets/plugin/tools/fetch-positions.d.ts +7 -0
  229. package/assets/plugin/tools/fetch-positions.js +4 -0
  230. package/assets/plugin/tools/fetch-ticker.d.ts +11 -0
  231. package/assets/plugin/tools/fetch-ticker.js +5 -0
  232. package/assets/plugin/tools/get-agent-profile.d.ts +4 -0
  233. package/assets/plugin/tools/get-agent-profile.js +6 -0
  234. package/assets/plugin/tools/get-analytics.d.ts +6 -0
  235. package/assets/plugin/tools/get-analytics.js +7 -0
  236. package/assets/plugin/tools/get-backtest.d.ts +12 -0
  237. package/assets/plugin/tools/get-backtest.js +91 -0
  238. package/assets/plugin/tools/get-basis.d.ts +7 -0
  239. package/assets/plugin/tools/get-basis.js +7 -0
  240. package/assets/plugin/tools/get-bracket-config.d.ts +11 -0
  241. package/assets/plugin/tools/get-bracket-config.js +24 -0
  242. package/assets/plugin/tools/get-cascade-risk.d.ts +7 -0
  243. package/assets/plugin/tools/get-cascade-risk.js +8 -0
  244. package/assets/plugin/tools/get-crypto-metrics.d.ts +18 -0
  245. package/assets/plugin/tools/get-crypto-metrics.js +62 -0
  246. package/assets/plugin/tools/get-cvd.d.ts +6 -0
  247. package/assets/plugin/tools/get-cvd.js +6 -0
  248. package/assets/plugin/tools/get-divergences.d.ts +6 -0
  249. package/assets/plugin/tools/get-divergences.js +6 -0
  250. package/assets/plugin/tools/get-funding-context.d.ts +6 -0
  251. package/assets/plugin/tools/get-funding-context.js +16 -0
  252. package/assets/plugin/tools/get-liquidation-levels.d.ts +7 -0
  253. package/assets/plugin/tools/get-liquidation-levels.js +7 -0
  254. package/assets/plugin/tools/get-liquidation-pulse.d.ts +9 -0
  255. package/assets/plugin/tools/get-liquidation-pulse.js +22 -0
  256. package/assets/plugin/tools/get-market-breadth.d.ts +6 -0
  257. package/assets/plugin/tools/get-market-breadth.js +8 -0
  258. package/assets/plugin/tools/get-market-intel.d.ts +19 -0
  259. package/assets/plugin/tools/get-market-intel.js +116 -0
  260. package/assets/plugin/tools/get-market-structure.d.ts +47 -0
  261. package/assets/plugin/tools/get-market-structure.js +198 -0
  262. package/assets/plugin/tools/get-my-mined-patterns.d.ts +20 -0
  263. package/assets/plugin/tools/get-my-mined-patterns.js +61 -0
  264. package/assets/plugin/tools/get-my-proposed-learnings.d.ts +20 -0
  265. package/assets/plugin/tools/get-my-proposed-learnings.js +55 -0
  266. package/assets/plugin/tools/get-my-recent-reviews.d.ts +22 -0
  267. package/assets/plugin/tools/get-my-recent-reviews.js +66 -0
  268. package/assets/plugin/tools/get-orderbook.d.ts +21 -0
  269. package/assets/plugin/tools/get-orderbook.js +32 -0
  270. package/assets/plugin/tools/get-pattern-scan.d.ts +7 -0
  271. package/assets/plugin/tools/get-pattern-scan.js +8 -0
  272. package/assets/plugin/tools/get-regime.d.ts +6 -0
  273. package/assets/plugin/tools/get-regime.js +7 -0
  274. package/assets/plugin/tools/get-relevant-learnings.d.ts +21 -0
  275. package/assets/plugin/tools/get-relevant-learnings.js +65 -0
  276. package/assets/plugin/tools/get-resting-liquidity.d.ts +6 -0
  277. package/assets/plugin/tools/get-resting-liquidity.js +11 -0
  278. package/assets/plugin/tools/get-risk-scenario.d.ts +29 -0
  279. package/assets/plugin/tools/get-risk-scenario.js +47 -0
  280. package/assets/plugin/tools/get-risk-summary.d.ts +51 -0
  281. package/assets/plugin/tools/get-risk-summary.js +118 -0
  282. package/assets/plugin/tools/get-sentiment.d.ts +4 -0
  283. package/assets/plugin/tools/get-sentiment.js +6 -0
  284. package/assets/plugin/tools/get-session-review.d.ts +7 -0
  285. package/assets/plugin/tools/get-session-review.js +8 -0
  286. package/assets/plugin/tools/get-setup-detail.d.ts +7 -0
  287. package/assets/plugin/tools/get-setup-detail.js +322 -0
  288. package/assets/plugin/tools/get-signals.d.ts +15 -0
  289. package/assets/plugin/tools/get-signals.js +54 -0
  290. package/assets/plugin/tools/get-sizing.d.ts +6 -0
  291. package/assets/plugin/tools/get-sizing.js +6 -0
  292. package/assets/plugin/tools/get-trade-feedback.d.ts +7 -0
  293. package/assets/plugin/tools/get-trade-feedback.js +8 -0
  294. package/assets/plugin/tools/get-trade-flow.d.ts +7 -0
  295. package/assets/plugin/tools/get-trade-flow.js +7 -0
  296. package/assets/plugin/tools/get-volume-analysis.d.ts +21 -0
  297. package/assets/plugin/tools/get-volume-analysis.js +74 -0
  298. package/assets/plugin/tools/get-volume-profile.d.ts +7 -0
  299. package/assets/plugin/tools/get-volume-profile.js +7 -0
  300. package/assets/plugin/tools/get-wave9-status.d.ts +127 -0
  301. package/assets/plugin/tools/get-wave9-status.js +796 -0
  302. package/assets/plugin/tools/helpers.d.ts +26 -0
  303. package/assets/plugin/tools/helpers.js +39 -0
  304. package/assets/plugin/tools/intel-api.d.ts +34 -0
  305. package/assets/plugin/tools/intel-api.js +119 -0
  306. package/assets/plugin/tools/intel-cache.d.ts +25 -0
  307. package/assets/plugin/tools/intel-cache.js +148 -0
  308. package/assets/plugin/tools/list-strategies.d.ts +17 -0
  309. package/assets/plugin/tools/list-strategies.js +23 -0
  310. package/assets/plugin/tools/modify-stop.d.ts +21 -0
  311. package/assets/plugin/tools/modify-stop.js +120 -0
  312. package/assets/plugin/tools/modify-target.d.ts +21 -0
  313. package/assets/plugin/tools/modify-target.js +110 -0
  314. package/assets/plugin/tools/propose-learning.d.ts +22 -0
  315. package/assets/plugin/tools/propose-learning.js +65 -0
  316. package/assets/plugin/tools/query-review-outcomes.d.ts +30 -0
  317. package/assets/plugin/tools/query-review-outcomes.js +64 -0
  318. package/assets/plugin/tools/query-trades.d.ts +21 -0
  319. package/assets/plugin/tools/query-trades.js +37 -0
  320. package/assets/plugin/tools/record-position-reviews.d.ts +38 -0
  321. package/assets/plugin/tools/record-position-reviews.js +147 -0
  322. package/assets/plugin/tools/save-strategy.d.ts +16 -0
  323. package/assets/plugin/tools/save-strategy.js +46 -0
  324. package/assets/plugin/tools/scan-pairs.d.ts +22 -0
  325. package/assets/plugin/tools/scan-pairs.js +234 -0
  326. package/assets/plugin/tools/score-setup.d.ts +31 -0
  327. package/assets/plugin/tools/score-setup.js +268 -0
  328. package/assets/plugin/tools/set-bracket-requirement.d.ts +18 -0
  329. package/assets/plugin/tools/set-bracket-requirement.js +81 -0
  330. package/assets/plugin/tools/set-exchange-credentials.d.ts +25 -0
  331. package/assets/plugin/tools/set-exchange-credentials.js +80 -0
  332. package/assets/plugin/tools/set-trading-mode.d.ts +26 -0
  333. package/assets/plugin/tools/set-trading-mode.js +152 -0
  334. package/assets/plugin/tools/test-exchange-credentials.d.ts +16 -0
  335. package/assets/plugin/tools/test-exchange-credentials.js +100 -0
  336. package/assets/plugin/tools/toggle-strategy.d.ts +8 -0
  337. package/assets/plugin/tools/toggle-strategy.js +15 -0
  338. package/assets/plugin/trading-params-cache.d.ts +26 -0
  339. package/assets/plugin/trading-params-cache.js +52 -0
  340. package/assets/plugin/types.d.ts +134 -0
  341. package/assets/plugin/types.js +7 -0
  342. package/assets/plugin/util/plugin-paths.d.ts +3 -0
  343. package/assets/plugin/util/plugin-paths.js +15 -0
  344. package/assets/plugin/venues/hyperliquid/hl-balance.d.ts +116 -0
  345. package/assets/plugin/venues/hyperliquid/hl-balance.js +145 -0
  346. package/assets/plugin/venues/hyperliquid/hl-bracket-coordinator.d.ts +123 -0
  347. package/assets/plugin/venues/hyperliquid/hl-bracket-coordinator.js +533 -0
  348. package/assets/plugin/venues/hyperliquid/hl-brackets.d.ts +102 -0
  349. package/assets/plugin/venues/hyperliquid/hl-brackets.js +172 -0
  350. package/assets/plugin/venues/hyperliquid/hl-cloid.d.ts +22 -0
  351. package/assets/plugin/venues/hyperliquid/hl-cloid.js +82 -0
  352. package/assets/plugin/venues/hyperliquid/hl-info-cache.d.ts +46 -0
  353. package/assets/plugin/venues/hyperliquid/hl-info-cache.js +125 -0
  354. package/assets/plugin/venues/hyperliquid/hl-live-adapter.d.ts +146 -0
  355. package/assets/plugin/venues/hyperliquid/hl-live-adapter.js +728 -0
  356. package/assets/plugin/venues/hyperliquid/hl-precision.d.ts +61 -0
  357. package/assets/plugin/venues/hyperliquid/hl-precision.js +176 -0
  358. package/assets/plugin/venues/hyperliquid/hl-private.d.ts +88 -0
  359. package/assets/plugin/venues/hyperliquid/hl-private.js +357 -0
  360. package/assets/plugin/venues/hyperliquid/hl-public.d.ts +79 -0
  361. package/assets/plugin/venues/hyperliquid/hl-public.js +436 -0
  362. package/assets/plugin/venues/hyperliquid/hl-rate-gate.d.ts +57 -0
  363. package/assets/plugin/venues/hyperliquid/hl-rate-gate.js +220 -0
  364. package/assets/plugin/venues/hyperliquid/hl-user-stream.d.ts +90 -0
  365. package/assets/plugin/venues/hyperliquid/hl-user-stream.js +220 -0
  366. package/assets/plugin/venues/registry.d.ts +38 -0
  367. package/assets/plugin/venues/registry.js +46 -0
  368. package/assets/plugin/venues/symbols.d.ts +43 -0
  369. package/assets/plugin/venues/symbols.js +107 -0
  370. package/assets/plugin/wave9/live-account-capture.d.ts +67 -0
  371. package/assets/plugin/wave9/live-account-capture.js +435 -0
  372. package/assets/plugin/wave9/live-autonomous-protection.d.ts +39 -0
  373. package/assets/plugin/wave9/live-autonomous-protection.js +112 -0
  374. package/assets/plugin/wave9/live-durable-reconciliation-scheduler.d.ts +33 -0
  375. package/assets/plugin/wave9/live-durable-reconciliation-scheduler.js +115 -0
  376. package/assets/plugin/wave9/live-execution-ledger.d.ts +107 -0
  377. package/assets/plugin/wave9/live-execution-ledger.js +498 -0
  378. package/assets/plugin/wave9/live-position-confirmation.d.ts +18 -0
  379. package/assets/plugin/wave9/live-position-confirmation.js +111 -0
  380. package/assets/plugin/wave9/live-residual-protection.d.ts +18 -0
  381. package/assets/plugin/wave9/live-residual-protection.js +250 -0
  382. package/assets/plugin/wave9/live-startup-reconciliation.d.ts +38 -0
  383. package/assets/plugin/wave9/live-startup-reconciliation.js +454 -0
  384. package/assets/plugin/wave9/live-symbol-ownership.d.ts +20 -0
  385. package/assets/plugin/wave9/live-symbol-ownership.js +132 -0
  386. package/assets/plugin/wave9/paper-admission-guard.d.ts +199 -0
  387. package/assets/plugin/wave9/paper-admission-guard.js +650 -0
  388. package/assets/plugin/wave9/usdm-evidence-provider.d.ts +42 -0
  389. package/assets/plugin/wave9/usdm-evidence-provider.js +133 -0
  390. package/package.json +1 -1
@@ -0,0 +1,78 @@
1
+ // Slippage tracker — records expected vs actual fill price for every live order.
2
+ // Computes slippage in basis points and tracks statistics per symbol.
3
+ import { logger } from '../logger.js';
4
+ const TAG = 'slippage';
5
+ export class SlippageTracker {
6
+ records = [];
7
+ maxRecords;
8
+ constructor(maxRecords = 1000) {
9
+ this.maxRecords = maxRecords;
10
+ }
11
+ /** Record a fill with slippage measurement. */
12
+ recordFill(params) {
13
+ // Slippage: positive means worse execution for the order side
14
+ // For buy: actual > expected = positive slippage (paid more)
15
+ // For sell: actual < expected = positive slippage (received less)
16
+ let slippageBps;
17
+ if (params.side === 'buy') {
18
+ slippageBps = ((params.actualPrice - params.expectedPrice) / params.expectedPrice) * 10_000;
19
+ }
20
+ else {
21
+ slippageBps = ((params.expectedPrice - params.actualPrice) / params.expectedPrice) * 10_000;
22
+ }
23
+ const record = {
24
+ ...params,
25
+ slippageBps,
26
+ timestamp: Date.now(),
27
+ };
28
+ this.records.push(record);
29
+ // Circular buffer
30
+ if (this.records.length > this.maxRecords) {
31
+ this.records.shift();
32
+ }
33
+ logger.info(TAG, `${params.symbol} ${params.side}: expected=${params.expectedPrice}, actual=${params.actualPrice}, slippage=${slippageBps.toFixed(2)}bps`);
34
+ return record;
35
+ }
36
+ /** Get average slippage in bps for a symbol. */
37
+ getAverageSlippage(symbol) {
38
+ const relevant = symbol
39
+ ? this.records.filter(r => r.symbol === symbol)
40
+ : this.records;
41
+ if (relevant.length === 0)
42
+ return 0;
43
+ return relevant.reduce((s, r) => s + r.slippageBps, 0) / relevant.length;
44
+ }
45
+ /** Get slippage stats summary. */
46
+ getStats(symbol) {
47
+ const relevant = symbol
48
+ ? this.records.filter(r => r.symbol === symbol)
49
+ : this.records;
50
+ if (relevant.length === 0) {
51
+ return { count: 0, avgBps: 0, maxBps: 0, minBps: 0, p95Bps: 0, totalCost: 0 };
52
+ }
53
+ const bps = relevant.map(r => r.slippageBps).sort((a, b) => a - b);
54
+ const p95Idx = Math.floor(bps.length * 0.95);
55
+ return {
56
+ count: relevant.length,
57
+ avgBps: bps.reduce((s, v) => s + v, 0) / bps.length,
58
+ maxBps: bps[bps.length - 1],
59
+ minBps: bps[0],
60
+ p95Bps: bps[p95Idx],
61
+ totalCost: relevant.reduce((s, r) => {
62
+ // Cost of slippage = (actualPrice - expectedPrice) * amount
63
+ const diff = r.side === 'buy'
64
+ ? (r.actualPrice - r.expectedPrice)
65
+ : (r.expectedPrice - r.actualPrice);
66
+ return s + diff * r.amount;
67
+ }, 0),
68
+ };
69
+ }
70
+ /** Get recent records (last N). */
71
+ getRecent(count = 10) {
72
+ return this.records.slice(-count);
73
+ }
74
+ /** Get all records for a symbol. */
75
+ getRecordsForSymbol(symbol) {
76
+ return this.records.filter(r => r.symbol === symbol);
77
+ }
78
+ }
@@ -0,0 +1,67 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { IExchangeAdapter } from '../exchange-adapter.js';
3
+ import type { CcxtOrder, CcxtPosition } from '../types.js';
4
+ import type { TradingOperationLock } from '../lifecycle/trading-operation-lock.js';
5
+ export declare const DEFAULT_INTERVAL_MS = 10000;
6
+ export interface StopTriggeredEvent {
7
+ symbol: string;
8
+ side: 'long' | 'short';
9
+ stopPrice: number;
10
+ markPrice: number;
11
+ quantity: number;
12
+ }
13
+ /** Emitted with 'stop_closed' — carries the executed close order so listeners
14
+ * (the journal auto-capture wiring in index.ts) can record the close with the
15
+ * real fill. Without this, watcher closes bypassed the Position Decision
16
+ * Journal entirely and left phantom-open journal positions (issue #199). */
17
+ export interface StopClosedEvent extends StopTriggeredEvent {
18
+ order?: CcxtOrder;
19
+ }
20
+ export type Wave9StopCloseOutcome = {
21
+ status: 'flat';
22
+ detail: string;
23
+ candidateId: string;
24
+ symbol: string;
25
+ } | {
26
+ status: 'protected' | 'manual_intervention';
27
+ detail: string;
28
+ candidateId: string;
29
+ symbol: string;
30
+ stopPrice: number;
31
+ residualQuantity?: number;
32
+ stopClientOrderId?: string;
33
+ };
34
+ /** Optional durable Wave 9 lifecycle. Generic watcher behavior is unchanged
35
+ * when this is absent or the current position has no owned candidate. */
36
+ export interface Wave9StopCloseLifecycle {
37
+ resolveCandidateId(position: CcxtPosition): Promise<string | undefined> | string | undefined;
38
+ settleAfterClose(candidateId: string, symbol: string): Promise<Wave9StopCloseOutcome>;
39
+ }
40
+ /** Decide whether a position has crossed its stop.
41
+ * Exported for direct unit-testing without spinning up a watcher loop. */
42
+ export declare function isStopBreached(position: CcxtPosition): boolean;
43
+ export declare class PositionWatcher extends EventEmitter {
44
+ private interval;
45
+ private intervalMs;
46
+ private adapter;
47
+ private readonly operationLock?;
48
+ private wave9CloseLifecycle?;
49
+ /** Symbols currently awaiting a close RPC — second trigger in the same tick
50
+ * is suppressed so the watcher cannot double-close. Cleared after the close
51
+ * call resolves (success or failure). */
52
+ private closePending;
53
+ /** Symbols we've already logged a breach for this session, to avoid spamming
54
+ * the log every tick while the close is in flight. */
55
+ private notifiedBreach;
56
+ constructor(adapter: IExchangeAdapter, intervalMs?: number, operationLock?: TradingOperationLock);
57
+ /** Configure this after the durable execution ledger is ready. Runtime
58
+ * lifecycle hooks reapply it to every watcher after reconnect. */
59
+ setWave9CloseLifecycle(lifecycle?: Wave9StopCloseLifecycle): void;
60
+ start(): void;
61
+ stop(): void;
62
+ /** Run one check cycle. Exposed for tests (skip setInterval). */
63
+ tick(): Promise<void>;
64
+ private fireClose;
65
+ /** For tests. */
66
+ isRunning(): boolean;
67
+ }
@@ -0,0 +1,218 @@
1
+ // Stop watcher — polls open positions and auto-closes any that have crossed
2
+ // their metadata stopPrice, independent of the LLM agent.
3
+ //
4
+ // Policy vs enforcement split:
5
+ // - The LLM owns POLICY: it sets stopPrice at entry and may update it at
6
+ // any time (move to breakeven, trail, widen, or remove entirely by
7
+ // clearing it). The watcher just reads whatever stopPrice currently is.
8
+ // - The watcher owns ENFORCEMENT: if mark crosses stopPrice, it fires a
9
+ // market close immediately rather than waiting for the agent's next
10
+ // 15-min heartbeat. This closes the exit-latency gap the LLM-centric
11
+ // model would otherwise leave open.
12
+ //
13
+ // Scope:
14
+ // - Works in paper mode (PaperAdapter.getPositions returns CcxtPosition with
15
+ // stopPrice populated from position metadata).
16
+ // - In live mode LiveAdapter does NOT track per-position metadata, so
17
+ // getPositions returns positions without stopPrice and the watcher
18
+ // naturally skips them. Wiring live-mode metadata is a separate change
19
+ // for when live trading turns on.
20
+ import { EventEmitter } from 'node:events';
21
+ import { logger, formatError } from '../logger.js';
22
+ const TAG = 'stop-watcher';
23
+ // 2026-05-15 EFFICIENCY_QUICK_WINS Change 1: 3s → 10s. The watcher was
24
+ // designed when it was the ONLY stop-enforcement mechanism; with exchange-
25
+ // native brackets (live in enforce mode) it is now belt-and-suspenders, so
26
+ // 10s latency to catch a watcher-triggered close is fine — Binance fires the
27
+ // bracket stop in ms regardless. Saves ~70 weight/min (100→30). Operator can
28
+ // revert with zero deploy via plugin-config.json `stopWatcher.intervalMs`.
29
+ // See docs/EFFICIENCY_QUICK_WINS_PLAN.md + the Binance ban-gate context.
30
+ export const DEFAULT_INTERVAL_MS = 10_000;
31
+ /** Decide whether a position has crossed its stop.
32
+ * Exported for direct unit-testing without spinning up a watcher loop. */
33
+ export function isStopBreached(position) {
34
+ const stop = position.stopPrice;
35
+ if (stop === undefined || stop === null || !Number.isFinite(stop) || stop <= 0) {
36
+ return false;
37
+ }
38
+ const mark = position.markPrice;
39
+ if (!Number.isFinite(mark) || mark <= 0) {
40
+ return false;
41
+ }
42
+ return position.side === 'long' ? mark <= stop : mark >= stop;
43
+ }
44
+ export class PositionWatcher extends EventEmitter {
45
+ interval = null;
46
+ intervalMs;
47
+ adapter;
48
+ operationLock;
49
+ wave9CloseLifecycle;
50
+ /** Symbols currently awaiting a close RPC — second trigger in the same tick
51
+ * is suppressed so the watcher cannot double-close. Cleared after the close
52
+ * call resolves (success or failure). */
53
+ closePending = new Set();
54
+ /** Symbols we've already logged a breach for this session, to avoid spamming
55
+ * the log every tick while the close is in flight. */
56
+ notifiedBreach = new Set();
57
+ constructor(adapter, intervalMs = DEFAULT_INTERVAL_MS, operationLock) {
58
+ super();
59
+ this.adapter = adapter;
60
+ this.intervalMs = intervalMs;
61
+ this.operationLock = operationLock;
62
+ }
63
+ /** Configure this after the durable execution ledger is ready. Runtime
64
+ * lifecycle hooks reapply it to every watcher after reconnect. */
65
+ setWave9CloseLifecycle(lifecycle) {
66
+ this.wave9CloseLifecycle = lifecycle;
67
+ }
68
+ start() {
69
+ if (this.interval) {
70
+ logger.warn(TAG, 'Already running');
71
+ return;
72
+ }
73
+ logger.info(TAG, `Starting stop watcher (interval: ${this.intervalMs}ms)`);
74
+ this.interval = setInterval(() => {
75
+ void this.tick();
76
+ }, this.intervalMs);
77
+ }
78
+ stop() {
79
+ if (this.interval) {
80
+ clearInterval(this.interval);
81
+ this.interval = null;
82
+ }
83
+ this.closePending.clear();
84
+ this.notifiedBreach.clear();
85
+ logger.info(TAG, 'Stopped');
86
+ }
87
+ /** Run one check cycle. Exposed for tests (skip setInterval). */
88
+ async tick() {
89
+ let positions;
90
+ try {
91
+ positions = await this.adapter.getPositionsOrNull();
92
+ }
93
+ catch (err) {
94
+ logger.warn(TAG, `getPositions failed: ${formatError(err)}`);
95
+ return;
96
+ }
97
+ if (positions === null) {
98
+ // Fetch failed (429 / weight-paced / transient) — position state is
99
+ // UNKNOWN, not "flat". Skip this tick WITHOUT touching notifiedBreach /
100
+ // closePending: a null-collapse to [] would wipe the in-flight close
101
+ // guard (openSymbols below would be empty, clearing every closePending
102
+ // entry) and let the NEXT tick fire a SECOND close on a position whose
103
+ // close is already in flight. State repairs on the next successful tick.
104
+ logger.warn(TAG, 'getPositions unavailable (null) — skipping tick, preserving in-flight close guards');
105
+ return;
106
+ }
107
+ // Clear notification flags for any symbol no longer holding a position,
108
+ // so a re-entered position later gets a fresh log line on re-breach.
109
+ const openSymbols = new Set(positions.map(p => p.symbol));
110
+ for (const sym of this.notifiedBreach) {
111
+ if (!openSymbols.has(sym))
112
+ this.notifiedBreach.delete(sym);
113
+ }
114
+ for (const sym of this.closePending) {
115
+ if (!openSymbols.has(sym))
116
+ this.closePending.delete(sym);
117
+ }
118
+ for (const position of positions) {
119
+ if (!isStopBreached(position))
120
+ continue;
121
+ if (this.closePending.has(position.symbol))
122
+ continue;
123
+ if (!this.notifiedBreach.has(position.symbol)) {
124
+ logger.warn(TAG, `STOP BREACHED: ${position.symbol} ${position.side} mark=${position.markPrice} stop=${position.stopPrice} — auto-closing`);
125
+ this.notifiedBreach.add(position.symbol);
126
+ }
127
+ this.closePending.add(position.symbol);
128
+ // Fire-and-forget per symbol so one slow close doesn't stall the tick.
129
+ void this.fireClose(position);
130
+ }
131
+ }
132
+ async fireClose(position) {
133
+ const event = {
134
+ symbol: position.symbol,
135
+ side: position.side,
136
+ stopPrice: position.stopPrice ?? 0,
137
+ markPrice: position.markPrice,
138
+ quantity: position.contracts,
139
+ };
140
+ this.emit('stop_triggered', event);
141
+ // Captured close order for the 'stop_closed' event — set inside close()
142
+ // (which may run under the symbol lock) and read after it resolves.
143
+ let closeOrder;
144
+ const close = async () => {
145
+ let trustedPosition = position;
146
+ // Re-read after acquiring the shared trading boundary. A Wave 9
147
+ // reversal or another stop may have flattened the symbol while this
148
+ // watcher was queued.
149
+ if (this.operationLock) {
150
+ const current = await this.adapter.getPositionsOrNull(position.symbol);
151
+ if (current === null) {
152
+ throw new Error('position recheck unavailable while holding trading operation lock');
153
+ }
154
+ const latest = current.find((candidate) => candidate.symbol === position.symbol);
155
+ if (!latest || !isStopBreached(latest)) {
156
+ logger.info(TAG, `${position.symbol} no longer stop-triggered after locked recheck — skipping`);
157
+ return 'skipped';
158
+ }
159
+ trustedPosition = latest;
160
+ }
161
+ let candidateId;
162
+ let ownershipError;
163
+ if (this.wave9CloseLifecycle) {
164
+ try {
165
+ candidateId = await this.wave9CloseLifecycle.resolveCandidateId(trustedPosition);
166
+ }
167
+ catch (error) {
168
+ ownershipError = formatError(error);
169
+ }
170
+ }
171
+ if (ownershipError) {
172
+ throw new Error(`Wave 9 ownership resolution failed before stop close submission: ${ownershipError}; ` +
173
+ 'IMMEDIATE MANUAL INTERVENTION REQUIRED');
174
+ }
175
+ closeOrder = await this.adapter.closePosition(trustedPosition.symbol, 'stop_watcher');
176
+ if (!candidateId || !this.wave9CloseLifecycle)
177
+ return 'closed';
178
+ const outcome = await this.wave9CloseLifecycle.settleAfterClose(candidateId, trustedPosition.symbol);
179
+ if (outcome.status === 'flat')
180
+ return 'closed';
181
+ if (outcome.status === 'protected') {
182
+ logger.warn(TAG, `Stop close left a Wave 9 residual on ${trustedPosition.symbol}; ` +
183
+ `${outcome.detail}. The exact frozen stop is proven and the watcher will retry.`);
184
+ this.emit('stop_residual_protected', { ...event, ...outcome });
185
+ return 'residual_protected';
186
+ }
187
+ throw new Error(outcome.detail);
188
+ };
189
+ try {
190
+ const result = this.operationLock
191
+ ? await this.operationLock.withSymbolLock(position.symbol, `stop_watcher:${position.symbol}`, close)
192
+ : await close();
193
+ if (result !== 'closed')
194
+ return;
195
+ logger.info(TAG, `Auto-closed ${position.symbol} (reason=stop_watcher)`);
196
+ this.emit('stop_closed', { ...event, order: closeOrder });
197
+ }
198
+ catch (err) {
199
+ const msg = formatError(err);
200
+ // "No open position" means the LLM or another path already closed it —
201
+ // that's a successful no-op, not a failure.
202
+ if (msg.includes('No open position')) {
203
+ logger.info(TAG, `${position.symbol} already closed by another path — skipping`);
204
+ }
205
+ else {
206
+ logger.error(TAG, `Auto-close failed for ${position.symbol}: ${msg}`);
207
+ }
208
+ this.emit('stop_failed', { ...event, error: msg });
209
+ }
210
+ finally {
211
+ this.closePending.delete(position.symbol);
212
+ }
213
+ }
214
+ /** For tests. */
215
+ isRunning() {
216
+ return this.interval !== null;
217
+ }
218
+ }
@@ -0,0 +1,54 @@
1
+ import type { BinancePrivateApi } from '../ccxt/binance-private.js';
2
+ import type { UserDataStream } from './user-data-stream.js';
3
+ import type { LiveStateStore } from './live-state-store.js';
4
+ export interface UserDataActiveProbeOptions {
5
+ api: BinancePrivateApi;
6
+ stream: UserDataStream;
7
+ store: LiveStateStore;
8
+ /** How often to run the probe. Default 60_000 (60s). Pass 0 to disable. */
9
+ intervalMs?: number;
10
+ /** Subtracted from `lastEventAt` when computing `since` so we don't miss
11
+ * trades whose timestamp landed in the same millisecond as our last
12
+ * frame. Default 1_000 (1 s). */
13
+ safetyBufferMs?: number;
14
+ /** For unit tests — injected timers + clock. */
15
+ timers?: {
16
+ setInterval?: (fn: () => void, ms: number) => ReturnType<typeof setInterval>;
17
+ clearInterval?: (h: ReturnType<typeof setInterval>) => void;
18
+ };
19
+ /** For unit tests — injected clock used only for log timestamps. */
20
+ now?: () => number;
21
+ }
22
+ export interface UserDataActiveProbeHealth {
23
+ probesRun: number;
24
+ reconnectsTriggered: number;
25
+ errorCount: number;
26
+ lastReconnectReason: string | null;
27
+ lastReconnectAt: number | null;
28
+ }
29
+ export declare class UserDataActiveProbe {
30
+ private readonly api;
31
+ private readonly stream;
32
+ private readonly store;
33
+ private readonly intervalMs;
34
+ private readonly safetyBufferMs;
35
+ private readonly setIntervalFn;
36
+ private readonly clearIntervalFn;
37
+ private readonly now;
38
+ private timer;
39
+ private running;
40
+ private inFlight;
41
+ private probesRun;
42
+ private reconnectsTriggered;
43
+ private errorCount;
44
+ private lastReconnectReason;
45
+ private lastReconnectAt;
46
+ constructor(opts: UserDataActiveProbeOptions);
47
+ start(): void;
48
+ stop(): void;
49
+ getHealth(): UserDataActiveProbeHealth;
50
+ /** Testing hook — runs one probe pass synchronously (well, async, but
51
+ * awaitable). Production uses the interval-driven path; tests can drive
52
+ * this directly without touching fake timers. */
53
+ runProbe(): Promise<void>;
54
+ }
@@ -0,0 +1,180 @@
1
+ // UserDataActiveProbe — proactive REST probe that catches "WS silently
2
+ // stopped delivering" before the staleness watchdog does.
3
+ //
4
+ // WHY THIS EXISTS
5
+ // ---------------
6
+ // On 2026-04-25 we observed the prod user-data stream go silent for 15
7
+ // minutes at a stretch while a real bracket-stop fill happened on Binance.
8
+ // The socket stayed "open" at the OS level (no FIN, no error), the listenKey
9
+ // keep-alive succeeded, but no frames arrived. The 15-min staleness watchdog
10
+ // caught it eventually, but in that window the local store still showed an
11
+ // already-closed position as live. Either alone — adaptive watchdog OR active
12
+ // probe — narrows the window; together they close it.
13
+ //
14
+ // WHAT IT DOES
15
+ // ------------
16
+ // Every `intervalMs` (default 60s):
17
+ // 1. If the stream isn't connected, skip — reconnect logic owns recovery.
18
+ // 2. Read symbols from `store.getPositions()` — only positions where a
19
+ // missed fill matters (a closed position is a non-event).
20
+ // 3. For each symbol, call `api.fetchMyTrades(symbol, since=lastEventAt - 1s)`.
21
+ // 4. If any returned trade has `timestamp > lastEventAt`, the WS missed
22
+ // a fill: log the trade id + symbol and call `stream.requestReconnect`.
23
+ // 5. Stop after the first detection — reconnect will trigger snapshot +
24
+ // gap-fill, and another probe pass on the next tick covers anything new.
25
+ //
26
+ // WHAT IT DOES NOT DO
27
+ // -------------------
28
+ // - Does NOT ingest the trade itself. Reconnect → resyncRequired → gap-filler
29
+ // handles ingest with `source='rest_reconcile'`. Single-responsibility.
30
+ // - Does NOT touch LiveStateStore. Reconnect snapshot reseeds it.
31
+ // - Does NOT run on flat accounts. With no open positions there's nothing to
32
+ // protect — the watchdog's looser idle threshold is appropriate.
33
+ //
34
+ // COST
35
+ // ----
36
+ // `fetchMyTrades` is weight 5 per symbol per call on Binance Futures. With
37
+ // ~5 open positions on a typical account, that's 25 weight/min — well below
38
+ // the 2400/min budget.
39
+ import { logger, formatError } from '../logger.js';
40
+ const TAG = 'user-data-active-probe';
41
+ /** CCXT unified symbol → API form. The reconciler stores `BTC/USDT` (no suffix);
42
+ * CCXT's fetchMyTrades takes either form, but we re-attach the settle suffix
43
+ * to be explicit. */
44
+ function toApiSymbol(s) {
45
+ if (!s)
46
+ return s;
47
+ if (s.includes(':'))
48
+ return s;
49
+ const parts = s.split('/');
50
+ return parts.length === 2 ? `${s}:${parts[1]}` : s;
51
+ }
52
+ export class UserDataActiveProbe {
53
+ api;
54
+ stream;
55
+ store;
56
+ intervalMs;
57
+ safetyBufferMs;
58
+ setIntervalFn;
59
+ clearIntervalFn;
60
+ now;
61
+ timer = null;
62
+ running = false;
63
+ inFlight = false;
64
+ probesRun = 0;
65
+ reconnectsTriggered = 0;
66
+ errorCount = 0;
67
+ lastReconnectReason = null;
68
+ lastReconnectAt = null;
69
+ constructor(opts) {
70
+ this.api = opts.api;
71
+ this.stream = opts.stream;
72
+ this.store = opts.store;
73
+ this.intervalMs = opts.intervalMs ?? 60_000;
74
+ this.safetyBufferMs = opts.safetyBufferMs ?? 1_000;
75
+ this.setIntervalFn = opts.timers?.setInterval ?? ((fn, ms) => setInterval(fn, ms));
76
+ this.clearIntervalFn = opts.timers?.clearInterval ?? ((h) => clearInterval(h));
77
+ this.now = opts.now ?? (() => Date.now());
78
+ }
79
+ start() {
80
+ if (this.running)
81
+ return;
82
+ if (this.intervalMs <= 0) {
83
+ logger.info(TAG, 'disabled (intervalMs=0)');
84
+ return;
85
+ }
86
+ this.running = true;
87
+ this.timer = this.setIntervalFn(() => {
88
+ void this.runProbe().catch((err) => {
89
+ this.errorCount++;
90
+ logger.warn(TAG, `probe pass threw: ${formatError(err)} — continuing`);
91
+ });
92
+ }, this.intervalMs);
93
+ logger.info(TAG, `started (intervalMs=${this.intervalMs}, safetyBufferMs=${this.safetyBufferMs})`);
94
+ }
95
+ stop() {
96
+ if (!this.running)
97
+ return;
98
+ if (this.timer) {
99
+ this.clearIntervalFn(this.timer);
100
+ this.timer = null;
101
+ }
102
+ this.running = false;
103
+ logger.info(TAG, 'stopped');
104
+ }
105
+ getHealth() {
106
+ return {
107
+ probesRun: this.probesRun,
108
+ reconnectsTriggered: this.reconnectsTriggered,
109
+ errorCount: this.errorCount,
110
+ lastReconnectReason: this.lastReconnectReason,
111
+ lastReconnectAt: this.lastReconnectAt,
112
+ };
113
+ }
114
+ /** Testing hook — runs one probe pass synchronously (well, async, but
115
+ * awaitable). Production uses the interval-driven path; tests can drive
116
+ * this directly without touching fake timers. */
117
+ async runProbe() {
118
+ if (!this.running && this.timer === null) {
119
+ // Allow direct invocation in tests even when start() wasn't called.
120
+ }
121
+ if (this.inFlight)
122
+ return;
123
+ this.inFlight = true;
124
+ this.probesRun++;
125
+ try {
126
+ if (!this.stream.isConnected())
127
+ return;
128
+ const health = this.stream.getHealth();
129
+ const lastEventAt = health.lastEventAt;
130
+ if (lastEventAt == null)
131
+ return; // Never received an event; nothing to compare against.
132
+ const positions = this.store.getPositions().filter((p) => p && p.contracts > 0);
133
+ if (positions.length === 0)
134
+ return;
135
+ const symbols = Array.from(new Set(positions.map((p) => p.symbol).filter(Boolean)));
136
+ const since = lastEventAt - this.safetyBufferMs;
137
+ for (const symbol of symbols) {
138
+ let trades;
139
+ try {
140
+ trades = await this.api.fetchMyTrades(toApiSymbol(symbol), since, 50);
141
+ }
142
+ catch (err) {
143
+ this.errorCount++;
144
+ logger.warn(TAG, `fetchMyTrades(${symbol}) threw: ${formatError(err)}`);
145
+ continue;
146
+ }
147
+ if (!Array.isArray(trades) || trades.length === 0)
148
+ continue;
149
+ let newest = null;
150
+ for (const t of trades) {
151
+ if (!t || typeof t !== 'object')
152
+ continue;
153
+ const rec = t;
154
+ const ts = typeof rec.timestamp === 'number' ? rec.timestamp : 0;
155
+ if (ts <= lastEventAt)
156
+ continue;
157
+ const id = typeof rec.id === 'string' ? rec.id : String(rec.id ?? '?');
158
+ if (!newest || ts > newest.timestamp) {
159
+ newest = { id, timestamp: ts };
160
+ }
161
+ }
162
+ if (newest) {
163
+ const lagMs = this.now() - newest.timestamp;
164
+ const reason = `active_probe_silent_stream`;
165
+ logger.error(TAG, `silent-stream detected: ${symbol} trade id=${newest.id} t=${new Date(newest.timestamp).toISOString()} ` +
166
+ `> lastEventAt=${new Date(lastEventAt).toISOString()} (lagMs=${lagMs}) — forcing reconnect`);
167
+ this.reconnectsTriggered++;
168
+ this.lastReconnectReason = `${reason}:${symbol}`;
169
+ this.lastReconnectAt = this.now();
170
+ this.stream.requestReconnect(reason);
171
+ // One reconnect per pass — don't fire repeatedly across symbols.
172
+ return;
173
+ }
174
+ }
175
+ }
176
+ finally {
177
+ this.inFlight = false;
178
+ }
179
+ }
180
+ }