@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,11 @@
1
+ import type { TradingMode } from '../types.js';
2
+ export interface ModeTransitionAuditEntry {
3
+ ts: string;
4
+ previousMode: TradingMode;
5
+ targetMode: TradingMode;
6
+ acknowledged: boolean;
7
+ ok: boolean;
8
+ reason?: string;
9
+ testnet?: boolean;
10
+ }
11
+ export declare function recordModeTransition(entry: Omit<ModeTransitionAuditEntry, 'ts'>, pluginId?: string): void;
@@ -0,0 +1,29 @@
1
+ // Mode-transition audit — append-only JSONL record of every set_trading_mode call.
2
+ //
3
+ // Written BEFORE the adapter swap (so even a crashed transition leaves a trail)
4
+ // and updated with the outcome. Intended for post-incident review and regulatory
5
+ // defence ("the operator acknowledged the LIVE transition at 2026-04-20T14:32:11Z").
6
+ //
7
+ // Path: ~/.openclaw/plugins/<pluginId>/mode-transitions.jsonl
8
+ // Append-only; never truncated. One JSON object per line.
9
+ import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import { logger, formatError } from '../logger.js';
12
+ import { resolvePluginsBaseDir } from '../util/plugin-paths.js';
13
+ const TAG = 'mode-audit';
14
+ function resolvePath(pluginId) {
15
+ const dir = join(resolvePluginsBaseDir(), pluginId ?? 'reefclaw-paper-trading');
16
+ if (!existsSync(dir))
17
+ mkdirSync(dir, { recursive: true });
18
+ return join(dir, 'mode-transitions.jsonl');
19
+ }
20
+ export function recordModeTransition(entry, pluginId) {
21
+ const full = { ts: new Date().toISOString(), ...entry };
22
+ try {
23
+ appendFileSync(resolvePath(pluginId), JSON.stringify(full) + '\n', 'utf8');
24
+ logger.info(TAG, `${full.previousMode} → ${full.targetMode} ok=${full.ok} ack=${full.acknowledged}`);
25
+ }
26
+ catch (err) {
27
+ logger.error(TAG, `Failed to write audit entry: ${formatError(err)}`);
28
+ }
29
+ }
@@ -0,0 +1,36 @@
1
+ import type { CcxtBalance } from './types.js';
2
+ /**
3
+ * Read the total quote-currency balance from a CCXT balance map.
4
+ *
5
+ * Uses the PRIMARY funded currency only — whichever of USDT/USDC has
6
+ * the larger balance. Summing both was incorrect because Binance
7
+ * cross-margin accounting reports position margin/value as a phantom
8
+ * balance in the settlement currency (e.g., opening an ATOM/USDT
9
+ * position with a USDC-funded account creates a USDT "balance" entry
10
+ * equal to the position's notional, inflating getQuoteBalance by the
11
+ * position value and causing the KPI to double-count).
12
+ *
13
+ * For wallet/equity/NAV reads on Binance Futures, prefer
14
+ * `getQuoteWalletBalance(balance)` — it handles BNFCR.
15
+ */
16
+ export declare function getQuoteBalance(balanceMap: Record<string, number> | undefined): number;
17
+ /**
18
+ * Read the effective wallet-equivalent quote balance from a Binance Futures
19
+ * fetchBalance response, including the BNFCR realized-PnL accumulator.
20
+ *
21
+ * Why this exists: Binance multi-asset accounts settle realized PnL into
22
+ * BNFCR (negative when net-loss, positive when net-gain). CCXT's `total`
23
+ * map carries `marginBalance` (= walletBalance + uPnl), so the BNFCR line
24
+ * mixes uPnl into the total, which double-counts when the caller adds
25
+ * position uPnl on top. This helper reads `info.assets[i].walletBalance`
26
+ * directly, which excludes uPnl by construction.
27
+ *
28
+ * Returns max(USDT_wallet, USDC_wallet) + BNFCR_wallet. BNFCR is added
29
+ * (not max'd) because it is independent of the funded stablecoin — a
30
+ * USDC-funded account with cumulative losses shows USDC unchanged plus
31
+ * BNFCR negative; the user's true wallet is the sum.
32
+ *
33
+ * Falls back to `getQuoteBalance(balance.total)` when `info.assets` is
34
+ * absent (mock balances in unit tests, non-Binance exchanges).
35
+ */
36
+ export declare function getQuoteWalletBalance(balance: CcxtBalance | null | undefined): number;
@@ -0,0 +1,98 @@
1
+ // Utility for reading quote-currency balance from CCXT balance objects.
2
+ // Binance USDⓈ-M Futures settles in USDT or USDC — accounts may hold either or both.
3
+ // On multi-asset accounts a third asset, BNFCR, accumulates realized PnL as a
4
+ // signed wallet line (negative = cumulative loss, positive = cumulative gain).
5
+ /**
6
+ * Read the total quote-currency balance from a CCXT balance map.
7
+ *
8
+ * Uses the PRIMARY funded currency only — whichever of USDT/USDC has
9
+ * the larger balance. Summing both was incorrect because Binance
10
+ * cross-margin accounting reports position margin/value as a phantom
11
+ * balance in the settlement currency (e.g., opening an ATOM/USDT
12
+ * position with a USDC-funded account creates a USDT "balance" entry
13
+ * equal to the position's notional, inflating getQuoteBalance by the
14
+ * position value and causing the KPI to double-count).
15
+ *
16
+ * For wallet/equity/NAV reads on Binance Futures, prefer
17
+ * `getQuoteWalletBalance(balance)` — it handles BNFCR.
18
+ */
19
+ export function getQuoteBalance(balanceMap) {
20
+ if (!balanceMap)
21
+ return 0;
22
+ const usdt = balanceMap.USDT ?? 0;
23
+ const usdc = balanceMap.USDC ?? 0;
24
+ // Use whichever currency has the larger wallet balance (the funded one).
25
+ // If both are zero, return 0. If both are funded, use the larger.
26
+ return Math.max(usdt, usdc);
27
+ }
28
+ /**
29
+ * Read the effective wallet-equivalent quote balance from a Binance Futures
30
+ * fetchBalance response, including the BNFCR realized-PnL accumulator.
31
+ *
32
+ * Why this exists: Binance multi-asset accounts settle realized PnL into
33
+ * BNFCR (negative when net-loss, positive when net-gain). CCXT's `total`
34
+ * map carries `marginBalance` (= walletBalance + uPnl), so the BNFCR line
35
+ * mixes uPnl into the total, which double-counts when the caller adds
36
+ * position uPnl on top. This helper reads `info.assets[i].walletBalance`
37
+ * directly, which excludes uPnl by construction.
38
+ *
39
+ * Returns max(USDT_wallet, USDC_wallet) + BNFCR_wallet. BNFCR is added
40
+ * (not max'd) because it is independent of the funded stablecoin — a
41
+ * USDC-funded account with cumulative losses shows USDC unchanged plus
42
+ * BNFCR negative; the user's true wallet is the sum.
43
+ *
44
+ * Falls back to `getQuoteBalance(balance.total)` when `info.assets` is
45
+ * absent (mock balances in unit tests, non-Binance exchanges).
46
+ */
47
+ export function getQuoteWalletBalance(balance) {
48
+ if (!balance)
49
+ return 0;
50
+ // Prefer Binance's own account-level wallet aggregate when present.
51
+ // `/fapi/v3/account` (CCXT passes it through verbatim at `info`) returns
52
+ // `totalWalletBalance` — in Multi-Assets Mode this is the USD-denominated
53
+ // sum across all margin assets, with each non-USD collateral valued at
54
+ // Binance's live index price (verified against developers.binance.com →
55
+ // Account Information V3, 2026-05-16). It also excludes uPnL by
56
+ // construction (marginBalance = walletBalance + uPnL), matching this
57
+ // function's "wallet, uPnL added by caller" contract.
58
+ //
59
+ // Why this beats the per-asset reconstruction below: summing
60
+ // `max(USDT,USDC) + BNFCR` values USDC at FACE 1.0000, but Binance values
61
+ // USDC collateral at the live USDC/USD index (~0.9998). That made the
62
+ // dashboard wallet permanently ~$0.2 high vs the Binance app on this
63
+ // account (always-on, same-sign, slowly varying with the USDC peg —
64
+ // 2026-05-16 "figures slightly off always"). totalWalletBalance is the
65
+ // exact number the app's Futures "Wallet Balance" shows. The read path is
66
+ // already REST (`LiveAdapter.getBalance` → `fetchBalance()` every poll),
67
+ // so this is zero freshness regression — same cadence, correct value.
68
+ const info = balance?.info;
69
+ if (info && info.totalWalletBalance != null) {
70
+ const twb = parseFloat(String(info.totalWalletBalance));
71
+ if (Number.isFinite(twb))
72
+ return twb;
73
+ }
74
+ // Fallback (no Binance aggregate — non-Binance exchange, mock balances in
75
+ // unit tests, or a WS-store-derived balance object): the legacy
76
+ // face-value per-asset reconstruction. Carries the documented ~USDC-index
77
+ // drift but is the best available when the aggregate is absent.
78
+ const assets = balance?.info?.assets;
79
+ if (!Array.isArray(assets))
80
+ return getQuoteBalance(balance.total);
81
+ let usdt = 0;
82
+ let usdc = 0;
83
+ let bnfcr = 0;
84
+ for (const a of assets) {
85
+ if (!a || typeof a.asset !== 'string')
86
+ continue;
87
+ const wb = parseFloat(String(a.walletBalance ?? 0));
88
+ if (!Number.isFinite(wb))
89
+ continue;
90
+ if (a.asset === 'USDT')
91
+ usdt = wb;
92
+ else if (a.asset === 'USDC')
93
+ usdc = wb;
94
+ else if (a.asset === 'BNFCR')
95
+ bnfcr = wb;
96
+ }
97
+ return Math.max(usdt, usdc) + bnfcr;
98
+ }
@@ -0,0 +1,47 @@
1
+ /** Thrown by `assertNotBanned` while the gate is closed. Carries the
2
+ * remaining cooldown so callers/telemetry can reason about it. Methods
3
+ * catch this in their existing catch block — it is NOT meant to escape
4
+ * the API layer with special handling; it degrades to the method's normal
5
+ * failure contract. */
6
+ export declare class BinanceBannedError extends Error {
7
+ readonly retryAfterMs: number;
8
+ constructor(retryAfterMs: number, context: string);
9
+ }
10
+ /** Thrown by the proactive weight pacer. Subclass of BinanceBannedError so
11
+ * every `instanceof BinanceBannedError` check and every method's existing
12
+ * catch keep working unchanged — it degrades to the same null/[]/throw
13
+ * contract (treated exactly like a transient fetch failure). The distinct
14
+ * name lets journal/telemetry tell prevention apart from a real IP ban. */
15
+ export declare class BinanceWeightPacedError extends BinanceBannedError {
16
+ constructor(retryAfterMs: number, context: string);
17
+ }
18
+ /** Fast pre-flight check. Call as the first line inside a Binance call's
19
+ * try-block. Throws `BinanceBannedError` (no network) while the gate is
20
+ * open. */
21
+ export declare function assertNotBanned(context: string): void;
22
+ /** Inspect a failed call's error and arm/extend the gate on a Binance
23
+ * rate-limit signal. Non-rate-limit errors are ignored (normal transient
24
+ * handling stays in the caller). Safe to call with any error shape. */
25
+ export declare function noteBinanceError(err: unknown): void;
26
+ /** Relax the soft-429 ladder after a clean call. (A hard 418 window is NOT
27
+ * shortened by this — only Binance's stated expiry clears it.) */
28
+ export declare function noteSuccess(): void;
29
+ /** Reconcile the local weight estimate against Binance truth from a response
30
+ * header (`x-mbx-used-weight-1m`). Best-effort: callers pass whatever header
31
+ * bag they have; absent/garbage values are ignored. The local estimate
32
+ * leads (counts in-flight calls Binance hasn't tallied yet), so we take the
33
+ * MAX within the window — never let a lagging header lower our guard. A
34
+ * header far BELOW the estimate means Binance's minute rolled; re-anchor the
35
+ * window to Binance's clock so we don't shed for a stale local total. */
36
+ export declare function noteResponseWeight(headers: Record<string, string> | undefined | null): void;
37
+ /** Test/diagnostic helper — current gate state. */
38
+ export declare function getBanGateState(): {
39
+ banUntilMs: number;
40
+ remainingMs: number;
41
+ rateLimitStep: number;
42
+ weightUsed: number;
43
+ weightCeiling: number;
44
+ cosmeticCeiling: number;
45
+ };
46
+ /** Test-only reset. Not used in production code paths. */
47
+ export declare function __resetBanGateForTests(): void;
@@ -0,0 +1,420 @@
1
+ // Process-wide Binance rate-limit / IP-ban gate.
2
+ //
3
+ // Why this exists (2026-05-15 incident): the plugin runs ~6 independent
4
+ // Binance pollers (LiveBalanceEnricher, position reconciler, bracket
5
+ // reconciler, user-data active probe, deposit tracker, public ticker/OHLCV)
6
+ // plus agent-driven tool calls. NONE of them shared any ban awareness. When
7
+ // Binance returned a 418 (IP ban), every poller kept firing on its own
8
+ // cadence, and since Binance EXTENDS an IP ban on every request received
9
+ // DURING the ban, the system never recovered on its own — a
10
+ // self-perpetuating lockout that zeroed the dashboard and blinded the agent
11
+ // for the whole window (REST dead; only the user-data WS kept working,
12
+ // which is exactly what Binance's error message tells you to use).
13
+ //
14
+ // This module is a SINGLETON (module-level state) deliberately: the ban is
15
+ // per-IP, not per-adapter-instance, so the gate must be shared across every
16
+ // BinancePrivateApi / BinancePublicApi instance in the process. The
17
+ // per-instance BinanceRateLimiter (rate-limiter.ts) tracks weight windows
18
+ // but has no ban model and is not consulted before every call — this gate
19
+ // is the hard stop that complements it.
20
+ //
21
+ // Binance taxonomy (USD-M Futures, General Info docs):
22
+ // - 429 / -1003 "Too many requests": soft rate-limit violation, no ban yet.
23
+ // - 418: auto-ban for CONTINUING to send after a 429. Message carries an
24
+ // explicit `banned until <epoch-ms>` we must respect — guessing a
25
+ // shorter window just re-bans and extends it.
26
+ //
27
+ // Integration contract (see binance-private.ts / binance-public.ts):
28
+ // - `assertNotBanned(ctx)` is called as the FIRST line inside each
29
+ // fetch/order method's existing try-block. While banned it throws a
30
+ // `BinanceBannedError`, which the method's EXISTING catch absorbs into
31
+ // its existing contract (return null / [] / rethrow) — so callers see
32
+ // the same shape they'd see for any failed fetch, but ZERO network
33
+ // traffic is generated during the ban. That's what lets the ban expire.
34
+ // - `noteBinanceError(err)` is called in each catch to arm/extend the gate.
35
+ // - `noteSuccess()` is called after a clean call to relax the soft-429
36
+ // backoff ladder.
37
+ import { logger } from '../logger.js';
38
+ const TAG = 'binance-ban-gate';
39
+ /** Soft-429 exponential backoff schedule (ms). Advance one step per
40
+ * consecutive 429; a clean call resets to step -1 (→ step 0 on next hit).
41
+ * Capped so we still probe ~every 2 min — enough to notice the limit
42
+ * clearing without climbing back toward a 418. Mirrors the skill-side
43
+ * poller circuit-breaker for behavioural consistency. */
44
+ const RATE_LIMIT_BACKOFF_MS = [5_000, 15_000, 45_000, 90_000, 120_000];
45
+ /** Slack added on top of Binance's stated `banned until` before we resume —
46
+ * clock skew + margin so the first post-ban call doesn't land on the
47
+ * boundary and immediately re-ban. */
48
+ const BAN_RESUME_SLACK_MS = 8_000;
49
+ // ---------------------------------------------------------------------------
50
+ // Proactive weight-budget pacer (prevention half — 2026-05-15).
51
+ //
52
+ // The reactive 418/429 backoff above only engages AFTER Binance has already
53
+ // rate-limited us. The recurring incident driver is the agent heartbeat
54
+ // SCAN burst: scan_pairs across the configured symbol set + per-candidate
55
+ // drill-downs fire dozens of fetchTicker/fetchOrderBook/fetchOHLCV calls within seconds,
56
+ // spiking the 1-min IP weight from baseline to 2000-2400 (Binance cap 2400)
57
+ // and tripping 418 at the window edge — every heartbeat.
58
+ //
59
+ // This is a per-IP, process-wide budget (every BinancePrivateApi /
60
+ // BinancePublicApi call already funnels through assertNotBanned), so the
61
+ // pacer lives here. It self-limits at a SOFT ceiling well below Binance's
62
+ // hard 2400 cap: once the rolling 1-min estimate would cross the ceiling,
63
+ // non-order reads fast-fail (same proven absorbed-by-existing-catch contract
64
+ // as the ban error) until the 1-min window rolls. Result: the burst is
65
+ // shed BEFORE it can reach 418 territory — the ban becomes structurally
66
+ // unreachable in normal operation instead of merely recovered-from.
67
+ //
68
+ // Local estimation (incremented on every allowed call) leads the headers,
69
+ // which lag by in-flight concurrency; noteResponseWeight() reconciles the
70
+ // estimate up to Binance truth when a response header is available.
71
+ // ---------------------------------------------------------------------------
72
+ const WEIGHT_WINDOW_MS = 60_000;
73
+ /** Binance USD-M Futures hard IP cap is 2400 weight / 1-min. We self-limit
74
+ * under it with ~400 weight of headroom (absorbs in-flight concurrency +
75
+ * header-vs-local lag; reactive 418/429 gate is the hard backstop).
76
+ *
77
+ * 2026-05-15 calibration (evidence-based, NOT a guess): after the measured
78
+ * efficiency pass — audit no longer sweeps unscoped when flat, algoRefresh
79
+ * 15s→60s, skill ticker/ohlcv/market-structure cadence cuts, all verified
80
+ * via the per-context + skill poll-window instrumentation — the residual
81
+ * flat-state IP weight is ~1650-1750/min of IRREDUCIBLE legitimate live-
82
+ * trading polling (positions, balance, open-orders, OHLCV, income,
83
+ * transfers, the 60s safety reconcilers). 1600 was below that floor, so
84
+ * the pacer shed legitimate heartbeat reads indefinitely and blinded the
85
+ * agent. Measured Binance x-mbx-used-weight-1m peaks ~1750-1900 << 2400,
86
+ * so 2000 stops the false shedding while keeping ~400 real headroom.
87
+ * Env-overridable for emergency tuning without a code change. */
88
+ const WEIGHT_SOFT_CEILING = (() => {
89
+ const raw = Number(process.env.RC_BINANCE_WEIGHT_CEILING);
90
+ return Number.isFinite(raw) && raw >= 200 && raw <= 2400 ? raw : 2000;
91
+ })();
92
+ // ---------------------------------------------------------------------------
93
+ // Safety-reserve priority tiering (2026-05-16).
94
+ //
95
+ // The pacer above sheds ALL non-order reads uniformly at the soft ceiling.
96
+ // That let the recurring real-money failure persist: a heartbeat SCAN burst
97
+ // (fetchTicker/fetchOHLCV/fetchOrderBook across the symbol set) pins the 1-min IP
98
+ // weight at the ceiling, so the bracket audit's getOpenOrders /
99
+ // queryAlgoOrder / fetchPositions were shed every heartbeat and the agent
100
+ // went blind on live-position protection for HOURS (2026-05-14 ATOM →
101
+ // 2026-05-16 INJ 04:29→05:54 straight). Absolute weight cuts (Option A
102
+ // income poll; 983cef0 reconciler/deposit) lower the floor; this makes the
103
+ // starvation STRUCTURALLY impossible regardless of future polling creep.
104
+ //
105
+ // Mechanism: low-priority cosmetic/market-data contexts (SHED_FIRST) are
106
+ // paced against COSMETIC_SOFT_CEILING; the reserve band
107
+ // [COSMETIC_SOFT_CEILING, WEIGHT_SOFT_CEILING] is reachable ONLY by
108
+ // safety-critical reads. This is strictly additive — nothing is made more
109
+ // lenient than today; only SHED_FIRST contexts shed earlier, so a safety
110
+ // read can never be newly starved by this change.
111
+ // ---------------------------------------------------------------------------
112
+ /** Weight reserved exclusively for safety-critical reads. Env-overridable;
113
+ * clamp 0–1000 (0 disables tiering → exact pre-2026-05-16 behaviour). */
114
+ const SAFETY_RESERVE = (() => {
115
+ const raw = Number(process.env.RC_BINANCE_SAFETY_RESERVE);
116
+ return Number.isFinite(raw) && raw >= 0 && raw <= 1000 ? raw : 400;
117
+ })();
118
+ /** Effective ceiling for SHED_FIRST contexts. Floored at 200 so a large
119
+ * reserve can't drive it to zero and shed cosmetic reads forever. */
120
+ const COSMETIC_SOFT_CEILING = Math.max(200, WEIGHT_SOFT_CEILING - SAFETY_RESERVE);
121
+ /** Approx Binance request weight per call context (the string passed to
122
+ * assertNotBanned). Conservative — over-estimating paces slightly early,
123
+ * which is the safe direction. Unknown contexts default to 5. */
124
+ const CONTEXT_WEIGHT = {
125
+ fetchTicker: 2,
126
+ fetchTickerRaw: 2,
127
+ fetchOrderBook: 5,
128
+ fetchOHLCV: 2,
129
+ fetchFundingRate: 1,
130
+ fetchOpenInterest: 1,
131
+ // Readiness gate's host→Binance reachability probe (fapiPublicGetTime, wt 1).
132
+ reachabilityProbe: 1,
133
+ // ExchangeInfoCache boot load — ccxt loadMarkets() → GET /fapi/v1/exchangeInfo,
134
+ // IP weight 1 (doc-verified developers.binance.com 2026-07-10). Was the
135
+ // known "ungated_ip" ccxt-internal call named in the window-summary comment.
136
+ loadMarkets: 1,
137
+ fetchBalance: 5,
138
+ fetchPositions: 5,
139
+ // BinancePrivateApi.fetchOpenOrders merges TWO REST calls (regular
140
+ // /fapi/v1/openOrders + algo /fapi/v1/openAlgoOrders), each 1 scoped /
141
+ // 40 unscoped (doc-verified developers.binance.com 2026-05-15). The call
142
+ // site passes the scoped/unscoped context so this is exact, not a guess.
143
+ fetchOpenOrders: 2, // 1 regular + 1 algo (symbol given)
144
+ fetchOpenOrdersAll: 80, // 40 regular + 40 algo (symbol omitted)
145
+ fetchMyTrades: 5,
146
+ fetchOrder: 1, // GET /fapi/v1/order (also used for by-clientId)
147
+ // GET /fapi/v1/algoOrder — single conditional-order status by clientAlgoId.
148
+ // Weight 1, doc-verified developers.binance.com/.../Query-Algo-Order
149
+ // (2026-05-15). The per-order bracket-spiral disambiguator: a successful
150
+ // empty /fapi/v1/openAlgoOrders is NOT proof a leg is gone; this asks
151
+ // about ONE exact order id. Same weight as the scoped snapshot, but it
152
+ // answers the actual question instead of inferring from absence.
153
+ queryAlgoOrder: 1,
154
+ fetchPositionMode: 30, // GET /fapi/v1/positionSide/dual (doc-verified)
155
+ fetchTransfers: 30,
156
+ fetchTodayIncomeBreakdown: 30,
157
+ // Same /fapi/v1/income call (weight 30) but the never-paced variant the
158
+ // boot + UTC-day-rollover KPI anchor uses. Still counted so the estimate
159
+ // stays honest; just not shed (see NEVER_PACE doc).
160
+ fetchTodayIncomeBreakdownForced: 30,
161
+ fetchRecentTradedSymbols: 30,
162
+ createListenKey: 1,
163
+ keepAliveListenKey: 1,
164
+ closeListenKey: 1,
165
+ };
166
+ /** Contexts that consume weight but must NEVER be blocked by the pacer:
167
+ * order/cancel paths (must always reach the exchange — same invariant as
168
+ * the ban gate), listenKey lifecycle (a paced-out keep-alive could let the
169
+ * user-data WS key expire; weight is trivial = 1), and the FORCED KPI
170
+ * income anchor. The latter (`fetchTodayIncomeBreakdownForced`) is the
171
+ * /fapi/v1/income call made only at boot and at UTC-day rollover — it
172
+ * establishes `sessionStartNav = wallet_at_midnight`, the single value the
173
+ * ENTIRE Day-P&L / Realized KPI chain is derived from. It runs at most
174
+ * ~twice a day, so pacing it buys ~0 weight relief but, if shed, the
175
+ * failed fetch used to corrupt the anchor to wallet_now and zero Day P&L
176
+ * (2026-05-16 KPI incident). Routine same-day income refreshes stay
177
+ * SHED_FIRST/cosmetic (the anchor is invariant within a UTC day, so a
178
+ * paced miss there is a harmless no-op). All NEVER_PACE contexts still
179
+ * COUNT toward the window so the estimate stays honest. */
180
+ const NEVER_PACE = new Set([
181
+ 'createOrder', 'cancelOrder', 'cancelAllOrders', 'cancelOrderByClientId',
182
+ 'createBracketOrder', 'createListenKey', 'keepAliveListenKey', 'closeListenKey',
183
+ 'fetchTodayIncomeBreakdownForced',
184
+ // ExchangeInfoCache boot load: runs once per adapter init and a failure
185
+ // BLOCKS the adapter (no retry until restart), so a paced-out load would
186
+ // trade ~1 weight of relief for a dead adapter. Ban-gated + counted like
187
+ // the listenKey contexts, never shed.
188
+ 'loadMarkets',
189
+ ]);
190
+ /** Low-priority contexts that shed FIRST under weight pressure (paced
191
+ * against COSMETIC_SOFT_CEILING, not the full ceiling). Two safe-to-shed
192
+ * groups: dashboard/KPI anchors that explicitly tolerate minute-scale
193
+ * staleness (income / transfers / recent-symbols) and public market-data
194
+ * scan reads that have a WS alternative and are re-fetched next heartbeat.
195
+ *
196
+ * DELIBERATELY EXCLUDES the bracket/position safety reads —
197
+ * fetchOpenOrders, fetchOpenOrdersAll, fetchPositions, queryAlgoOrder,
198
+ * fetchOrder, fetchMyTrades, fetchBalance, fetchPositionMode — which keep
199
+ * the full WEIGHT_SOFT_CEILING (today's behaviour, never made stricter
200
+ * here). fetchBalance/fetchMyTrades stay protected because they feed
201
+ * risk/sizing + fill reconciliation, not just display. */
202
+ const SHED_FIRST = new Set([
203
+ 'fetchTodayIncomeBreakdown', 'fetchTransfers', 'fetchRecentTradedSymbols',
204
+ 'fetchTicker', 'fetchTickerRaw', 'fetchOHLCV', 'fetchOrderBook',
205
+ 'fetchFundingRate', 'fetchOpenInterest', 'reachabilityProbe',
206
+ ]);
207
+ let weightWindowStart = 0;
208
+ let weightUsed = 0;
209
+ /** Throttle the "weight pacer engaged" log to once per window. */
210
+ let pacerLogged = false;
211
+ /** Per-context attribution for the current window (observability only —
212
+ * NOT consulted by any decision). Lets prod prove WHICH callers drive the
213
+ * weight so durable cadence cuts target the real consumer instead of a
214
+ * guess. `allowed` = weight let through; `shed` = paced-out call count. */
215
+ const weightByContext = new Map();
216
+ const shedByContext = new Map();
217
+ function rollWeightWindowIfDue(now) {
218
+ if (now - weightWindowStart >= WEIGHT_WINDOW_MS) {
219
+ if (weightUsed > 0 || shedByContext.size > 0) {
220
+ const fmt = (m, n) => [...m.entries()].sort((a, b) => b[1] - a[1]).slice(0, n)
221
+ .map(([k, v]) => `${k}=${v}`).join(' ');
222
+ // `weightUsed` (=total) is reconciled UP to Binance's IP-wide
223
+ // x-mbx-used-weight-1m header (noteResponseWeight); `weightByContext`
224
+ // only sees calls that passed through THIS gate (plugin reads). The
225
+ // difference is weight Binance charged this IP that the gate never
226
+ // saw — the skill process's own REST polls, ccxt-internal calls
227
+ // (loadMarkets/fetchTime), or anything else sharing the IP/key.
228
+ // Naming it explicitly is the whole point of the 2026-05-18
229
+ // "instrument, don't guess" pass: the per-context map summed to
230
+ // ~275 while total was ~1900 and the ~1600 gap was invisible,
231
+ // folded silently into `total`. Print ALL contexts (no top-N
232
+ // truncation) so no real consumer can hide either.
233
+ const gatedSum = [...weightByContext.values()].reduce((s, v) => s + v, 0);
234
+ const ungatedIp = Math.max(0, weightUsed - gatedSum);
235
+ logger.info(TAG, `weight window summary: total=${weightUsed}/${WEIGHT_SOFT_CEILING} ` +
236
+ `gated=${gatedSum} ungated_ip=${ungatedIp} ` +
237
+ `allowed[${fmt(weightByContext, 100)}] shed[${fmt(shedByContext, 100)}]`);
238
+ }
239
+ weightWindowStart = now;
240
+ weightUsed = 0;
241
+ pacerLogged = false;
242
+ weightByContext.clear();
243
+ shedByContext.clear();
244
+ }
245
+ }
246
+ function contextWeight(ctx) {
247
+ return CONTEXT_WEIGHT[ctx] ?? 5;
248
+ }
249
+ /** Thrown by `assertNotBanned` while the gate is closed. Carries the
250
+ * remaining cooldown so callers/telemetry can reason about it. Methods
251
+ * catch this in their existing catch block — it is NOT meant to escape
252
+ * the API layer with special handling; it degrades to the method's normal
253
+ * failure contract. */
254
+ export class BinanceBannedError extends Error {
255
+ retryAfterMs;
256
+ constructor(retryAfterMs, context) {
257
+ super(`Binance rate-limit/IP-ban gate is OPEN (retry in ~${Math.ceil(retryAfterMs / 1000)}s) — skipping ${context} to let the ban expire instead of extending it`);
258
+ this.retryAfterMs = retryAfterMs;
259
+ this.name = 'BinanceBannedError';
260
+ }
261
+ }
262
+ /** Thrown by the proactive weight pacer. Subclass of BinanceBannedError so
263
+ * every `instanceof BinanceBannedError` check and every method's existing
264
+ * catch keep working unchanged — it degrades to the same null/[]/throw
265
+ * contract (treated exactly like a transient fetch failure). The distinct
266
+ * name lets journal/telemetry tell prevention apart from a real IP ban. */
267
+ export class BinanceWeightPacedError extends BinanceBannedError {
268
+ constructor(retryAfterMs, context) {
269
+ super(retryAfterMs, context);
270
+ this.name = 'BinanceWeightPacedError';
271
+ this.message =
272
+ `Binance weight pacer engaged (1-min window reached the ~${WEIGHT_SOFT_CEILING}/2400 ` +
273
+ `soft ceiling, retry in ~${Math.ceil(retryAfterMs / 1000)}s) — shedding ${context} ` +
274
+ `to stay below the 418 threshold instead of bursting into an IP ban`;
275
+ }
276
+ }
277
+ let banUntilMs = 0;
278
+ let rateLimitStep = -1;
279
+ /** Throttles the "gate open, skipping" log to once per cooldown window so a
280
+ * multi-minute ban doesn't emit hundreds of identical lines. */
281
+ let cooldownLogged = false;
282
+ /** Fast pre-flight check. Call as the first line inside a Binance call's
283
+ * try-block. Throws `BinanceBannedError` (no network) while the gate is
284
+ * open. */
285
+ export function assertNotBanned(context) {
286
+ const now = Date.now();
287
+ if (now < banUntilMs) {
288
+ const remaining = banUntilMs - now;
289
+ if (!cooldownLogged) {
290
+ logger.warn(TAG, `gate OPEN — pausing ALL Binance calls for ~${Math.ceil(remaining / 1000)}s (418/429 backoff). First skipped: ${context}`);
291
+ cooldownLogged = true;
292
+ }
293
+ throw new BinanceBannedError(remaining, context);
294
+ }
295
+ if (cooldownLogged) {
296
+ logger.info(TAG, 'gate CLOSED — Binance calls resuming');
297
+ cooldownLogged = false;
298
+ }
299
+ // --- Proactive weight pacing (prevention; runs only when not banned) ---
300
+ rollWeightWindowIfDue(now);
301
+ const w = contextWeight(context);
302
+ if (!NEVER_PACE.has(context)) {
303
+ // Priority tiering: cosmetic/market-data reads (SHED_FIRST) pace against
304
+ // the lower COSMETIC_SOFT_CEILING so the reserve band up to the full
305
+ // ceiling stays exclusively available to safety-critical reads. Anything
306
+ // not in SHED_FIRST keeps the full ceiling — identical to pre-2026-05-16
307
+ // behaviour, never more lenient.
308
+ const isCosmetic = SHED_FIRST.has(context);
309
+ const effectiveCeiling = isCosmetic ? COSMETIC_SOFT_CEILING : WEIGHT_SOFT_CEILING;
310
+ if (weightUsed + w > effectiveCeiling) {
311
+ const retryAfterMs = Math.max(0, weightWindowStart + WEIGHT_WINDOW_MS - now);
312
+ if (!pacerLogged) {
313
+ logger.warn(TAG, `weight pacer ENGAGED — 1-min IP weight ~${weightUsed}/${WEIGHT_SOFT_CEILING} ` +
314
+ `(cosmetic ceiling ${COSMETIC_SOFT_CEILING}, hard cap 2400); shedding ` +
315
+ `${isCosmetic ? 'cosmetic' : 'non-order'} reads ~${Math.ceil(retryAfterMs / 1000)}s until the window rolls. First shed: ${context}`);
316
+ pacerLogged = true;
317
+ }
318
+ shedByContext.set(context, (shedByContext.get(context) ?? 0) + 1);
319
+ throw new BinanceWeightPacedError(retryAfterMs, context);
320
+ }
321
+ }
322
+ // Allowed (or NEVER_PACE) → the request will go out; count its weight
323
+ // immediately so the estimate leads (not lags) the in-flight burst.
324
+ weightUsed += w;
325
+ weightByContext.set(context, (weightByContext.get(context) ?? 0) + w);
326
+ }
327
+ /** Inspect a failed call's error and arm/extend the gate on a Binance
328
+ * rate-limit signal. Non-rate-limit errors are ignored (normal transient
329
+ * handling stays in the caller). Safe to call with any error shape. */
330
+ export function noteBinanceError(err) {
331
+ // A BinanceBannedError means the gate was already open and we short-
332
+ // circuited — nothing to arm.
333
+ if (err instanceof BinanceBannedError)
334
+ return;
335
+ const msg = err instanceof Error ? err.message : typeof err === 'string' ? err : String(err);
336
+ // 418 — hard IP ban with an explicit expiry Binance gives us.
337
+ if (msg.includes('418') || /banned until/i.test(msg)) {
338
+ const m = /banned until (\d{10,})/i.exec(msg);
339
+ const stated = m ? Number(m[1]) : 0;
340
+ const base = Number.isFinite(stated) && stated > Date.now()
341
+ ? stated
342
+ : Date.now() + RATE_LIMIT_BACKOFF_MS[RATE_LIMIT_BACKOFF_MS.length - 1];
343
+ const target = base + BAN_RESUME_SLACK_MS;
344
+ if (target > banUntilMs) {
345
+ banUntilMs = target;
346
+ cooldownLogged = false; // force a fresh "gate OPEN ~Ns" line
347
+ logger.error(TAG, `Binance 418 IP BAN — gate engaged, ALL Binance calls paused ~${Math.ceil((target - Date.now()) / 1000)}s (Binance-stated expiry +slack)`);
348
+ }
349
+ return;
350
+ }
351
+ // 429 / -1003 — soft rate-limit, no ban yet. Exponential backoff so we
352
+ // stop climbing toward a 418.
353
+ if (msg.includes('429') ||
354
+ msg.includes('-1003') ||
355
+ /too many requests/i.test(msg)) {
356
+ rateLimitStep = Math.min(rateLimitStep + 1, RATE_LIMIT_BACKOFF_MS.length - 1);
357
+ const window = RATE_LIMIT_BACKOFF_MS[rateLimitStep];
358
+ const target = Date.now() + window;
359
+ if (target > banUntilMs) {
360
+ banUntilMs = target;
361
+ cooldownLogged = false;
362
+ logger.warn(TAG, `Binance 429 — gate backoff step ${rateLimitStep}, ALL Binance calls paused ${window / 1000}s`);
363
+ }
364
+ }
365
+ }
366
+ /** Relax the soft-429 ladder after a clean call. (A hard 418 window is NOT
367
+ * shortened by this — only Binance's stated expiry clears it.) */
368
+ export function noteSuccess() {
369
+ if (rateLimitStep !== -1)
370
+ rateLimitStep = -1;
371
+ }
372
+ /** Reconcile the local weight estimate against Binance truth from a response
373
+ * header (`x-mbx-used-weight-1m`). Best-effort: callers pass whatever header
374
+ * bag they have; absent/garbage values are ignored. The local estimate
375
+ * leads (counts in-flight calls Binance hasn't tallied yet), so we take the
376
+ * MAX within the window — never let a lagging header lower our guard. A
377
+ * header far BELOW the estimate means Binance's minute rolled; re-anchor the
378
+ * window to Binance's clock so we don't shed for a stale local total. */
379
+ export function noteResponseWeight(headers) {
380
+ if (!headers)
381
+ return;
382
+ const raw = headers['x-mbx-used-weight-1m'] ?? headers['X-MBX-USED-WEIGHT-1M'];
383
+ if (raw == null)
384
+ return;
385
+ const actual = parseInt(String(raw), 10);
386
+ if (!Number.isFinite(actual) || actual < 0)
387
+ return;
388
+ const now = Date.now();
389
+ rollWeightWindowIfDue(now);
390
+ if (actual + 50 < weightUsed) {
391
+ // Binance reports much less than our local tally → its 1-min counter
392
+ // reset on the exchange side. Re-anchor to Binance truth.
393
+ weightWindowStart = now;
394
+ weightUsed = actual;
395
+ pacerLogged = false;
396
+ return;
397
+ }
398
+ if (actual > weightUsed)
399
+ weightUsed = actual;
400
+ }
401
+ /** Test/diagnostic helper — current gate state. */
402
+ export function getBanGateState() {
403
+ return {
404
+ banUntilMs,
405
+ remainingMs: Math.max(0, banUntilMs - Date.now()),
406
+ rateLimitStep,
407
+ weightUsed,
408
+ weightCeiling: WEIGHT_SOFT_CEILING,
409
+ cosmeticCeiling: COSMETIC_SOFT_CEILING,
410
+ };
411
+ }
412
+ /** Test-only reset. Not used in production code paths. */
413
+ export function __resetBanGateForTests() {
414
+ banUntilMs = 0;
415
+ rateLimitStep = -1;
416
+ cooldownLogged = false;
417
+ weightWindowStart = 0;
418
+ weightUsed = 0;
419
+ pacerLogged = false;
420
+ }