@reefclaw/connect 0.1.11 → 0.1.12

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 (397) hide show
  1. package/assets/bridge/gateway/heartbeat-cron.d.ts +27 -0
  2. package/assets/bridge/gateway/heartbeat-cron.js +85 -0
  3. package/assets/bridge/providers/gateway.d.ts +2 -0
  4. package/assets/bridge/providers/gateway.js +57 -16
  5. package/assets/bridge/setup.js +6 -51
  6. package/dist/deps.js +2 -2
  7. package/dist/plugin.js +35 -7
  8. package/package.json +1 -1
  9. package/assets/plugin/audit/mode-transition-audit.d.ts +0 -11
  10. package/assets/plugin/audit/mode-transition-audit.js +0 -29
  11. package/assets/plugin/balance-utils.d.ts +0 -36
  12. package/assets/plugin/balance-utils.js +0 -98
  13. package/assets/plugin/ccxt/binance-ban-gate.d.ts +0 -47
  14. package/assets/plugin/ccxt/binance-ban-gate.js +0 -420
  15. package/assets/plugin/ccxt/binance-private.d.ts +0 -346
  16. package/assets/plugin/ccxt/binance-private.js +0 -1525
  17. package/assets/plugin/ccxt/binance-public.d.ts +0 -34
  18. package/assets/plugin/ccxt/binance-public.js +0 -180
  19. package/assets/plugin/ccxt/intel-public.d.ts +0 -25
  20. package/assets/plugin/ccxt/intel-public.js +0 -80
  21. package/assets/plugin/ccxt/public-market-data-api.d.ts +0 -26
  22. package/assets/plugin/ccxt/public-market-data-api.js +0 -23
  23. package/assets/plugin/config/agent-config-client.d.ts +0 -77
  24. package/assets/plugin/config/agent-config-client.js +0 -188
  25. package/assets/plugin/config/agent-config-poller.d.ts +0 -32
  26. package/assets/plugin/config/agent-config-poller.js +0 -101
  27. package/assets/plugin/config/brackets-config.d.ts +0 -22
  28. package/assets/plugin/config/brackets-config.js +0 -58
  29. package/assets/plugin/config/entitlement-gate.d.ts +0 -51
  30. package/assets/plugin/config/entitlement-gate.js +0 -137
  31. package/assets/plugin/config/gate-store.d.ts +0 -18
  32. package/assets/plugin/config/gate-store.js +0 -61
  33. package/assets/plugin/config/plugin-config-io.d.ts +0 -222
  34. package/assets/plugin/config/plugin-config-io.js +0 -126
  35. package/assets/plugin/config/position-review-config.d.ts +0 -35
  36. package/assets/plugin/config/position-review-config.js +0 -105
  37. package/assets/plugin/config/tool-gate.d.ts +0 -53
  38. package/assets/plugin/config/tool-gate.js +0 -128
  39. package/assets/plugin/config/user-data-stream-config.d.ts +0 -89
  40. package/assets/plugin/config/user-data-stream-config.js +0 -239
  41. package/assets/plugin/connector-supervisor.d.ts +0 -36
  42. package/assets/plugin/connector-supervisor.js +0 -149
  43. package/assets/plugin/exchange-adapter.d.ts +0 -65
  44. package/assets/plugin/exchange-adapter.js +0 -4
  45. package/assets/plugin/index.d.ts +0 -30
  46. package/assets/plugin/index.js +0 -2756
  47. package/assets/plugin/ingest/pending-entry-metadata.d.ts +0 -74
  48. package/assets/plugin/ingest/pending-entry-metadata.js +0 -236
  49. package/assets/plugin/ingest/position-auto-capture.d.ts +0 -171
  50. package/assets/plugin/ingest/position-auto-capture.js +0 -709
  51. package/assets/plugin/ingest/position-decisions-client.d.ts +0 -327
  52. package/assets/plugin/ingest/position-decisions-client.js +0 -306
  53. package/assets/plugin/ingest/readiness-reporter.d.ts +0 -42
  54. package/assets/plugin/ingest/readiness-reporter.js +0 -163
  55. package/assets/plugin/ingest/reconcile-db-vs-exchange.d.ts +0 -51
  56. package/assets/plugin/ingest/reconcile-db-vs-exchange.js +0 -170
  57. package/assets/plugin/ingest/reconciler-cleanup.d.ts +0 -37
  58. package/assets/plugin/ingest/reconciler-cleanup.js +0 -147
  59. package/assets/plugin/ingest/rest-gap-filler.d.ts +0 -191
  60. package/assets/plugin/ingest/rest-gap-filler.js +0 -565
  61. package/assets/plugin/ingest/touched-symbols-store.d.ts +0 -25
  62. package/assets/plugin/ingest/touched-symbols-store.js +0 -96
  63. package/assets/plugin/ingest/trade-store-client.d.ts +0 -40
  64. package/assets/plugin/ingest/trade-store-client.js +0 -125
  65. package/assets/plugin/ingest/ws-ingest.d.ts +0 -43
  66. package/assets/plugin/ingest/ws-ingest.js +0 -126
  67. package/assets/plugin/learning/setup-family.d.ts +0 -21
  68. package/assets/plugin/learning/setup-family.js +0 -103
  69. package/assets/plugin/lifecycle/install-signal-handlers.d.ts +0 -33
  70. package/assets/plugin/lifecycle/install-signal-handlers.js +0 -112
  71. package/assets/plugin/lifecycle/shutdown-coordinator.d.ts +0 -43
  72. package/assets/plugin/lifecycle/shutdown-coordinator.js +0 -131
  73. package/assets/plugin/lifecycle/trading-operation-lock.d.ts +0 -17
  74. package/assets/plugin/lifecycle/trading-operation-lock.js +0 -14
  75. package/assets/plugin/live/bracket-id.d.ts +0 -26
  76. package/assets/plugin/live/bracket-id.js +0 -112
  77. package/assets/plugin/live/bracket-ledger.d.ts +0 -54
  78. package/assets/plugin/live/bracket-ledger.js +0 -267
  79. package/assets/plugin/live/bracket-manager.d.ts +0 -82
  80. package/assets/plugin/live/bracket-manager.js +0 -478
  81. package/assets/plugin/live/bracket-params.d.ts +0 -22
  82. package/assets/plugin/live/bracket-params.js +0 -124
  83. package/assets/plugin/live/bracket-reconciler.d.ts +0 -95
  84. package/assets/plugin/live/bracket-reconciler.js +0 -573
  85. package/assets/plugin/live/bracket-types.d.ts +0 -102
  86. package/assets/plugin/live/bracket-types.js +0 -8
  87. package/assets/plugin/live/deposit-tracker.d.ts +0 -62
  88. package/assets/plugin/live/deposit-tracker.js +0 -97
  89. package/assets/plugin/live/emergency-controls.d.ts +0 -32
  90. package/assets/plugin/live/emergency-controls.js +0 -226
  91. package/assets/plugin/live/exchange-errors.d.ts +0 -12
  92. package/assets/plugin/live/exchange-errors.js +0 -130
  93. package/assets/plugin/live/exchange-info-cache.d.ts +0 -37
  94. package/assets/plugin/live/exchange-info-cache.js +0 -134
  95. package/assets/plugin/live/fact-subscriber.d.ts +0 -78
  96. package/assets/plugin/live/fact-subscriber.js +0 -182
  97. package/assets/plugin/live/fill-price.d.ts +0 -13
  98. package/assets/plugin/live/fill-price.js +0 -37
  99. package/assets/plugin/live/intent-journal.d.ts +0 -42
  100. package/assets/plugin/live/intent-journal.js +0 -122
  101. package/assets/plugin/live/listen-key-manager.d.ts +0 -70
  102. package/assets/plugin/live/listen-key-manager.js +0 -169
  103. package/assets/plugin/live/live-adapter.d.ts +0 -319
  104. package/assets/plugin/live/live-adapter.js +0 -1906
  105. package/assets/plugin/live/live-balance-enricher.d.ts +0 -32
  106. package/assets/plugin/live/live-balance-enricher.js +0 -104
  107. package/assets/plugin/live/live-bracket-api.d.ts +0 -13
  108. package/assets/plugin/live/live-bracket-api.js +0 -20
  109. package/assets/plugin/live/live-state-store.d.ts +0 -194
  110. package/assets/plugin/live/live-state-store.js +0 -450
  111. package/assets/plugin/live/local-signal-service.d.ts +0 -57
  112. package/assets/plugin/live/local-signal-service.js +0 -151
  113. package/assets/plugin/live/local-strategy-evaluator.d.ts +0 -62
  114. package/assets/plugin/live/local-strategy-evaluator.js +0 -131
  115. package/assets/plugin/live/microstructure-assembler.d.ts +0 -54
  116. package/assets/plugin/live/microstructure-assembler.js +0 -148
  117. package/assets/plugin/live/order-poller.d.ts +0 -29
  118. package/assets/plugin/live/order-poller.js +0 -125
  119. package/assets/plugin/live/position-state-store.d.ts +0 -87
  120. package/assets/plugin/live/position-state-store.js +0 -237
  121. package/assets/plugin/live/proposal-decision-listener.d.ts +0 -70
  122. package/assets/plugin/live/proposal-decision-listener.js +0 -292
  123. package/assets/plugin/live/proposal-manager.d.ts +0 -76
  124. package/assets/plugin/live/proposal-manager.js +0 -140
  125. package/assets/plugin/live/rate-limiter.d.ts +0 -47
  126. package/assets/plugin/live/rate-limiter.js +0 -159
  127. package/assets/plugin/live/reconciler.d.ts +0 -39
  128. package/assets/plugin/live/reconciler.js +0 -175
  129. package/assets/plugin/live/setup-buckets.d.ts +0 -7
  130. package/assets/plugin/live/setup-buckets.js +0 -33
  131. package/assets/plugin/live/slippage-tracker.d.ts +0 -45
  132. package/assets/plugin/live/slippage-tracker.js +0 -78
  133. package/assets/plugin/live/stop-watcher.d.ts +0 -67
  134. package/assets/plugin/live/stop-watcher.js +0 -218
  135. package/assets/plugin/live/user-data-active-probe.d.ts +0 -54
  136. package/assets/plugin/live/user-data-active-probe.js +0 -180
  137. package/assets/plugin/live/user-data-stream-controller.d.ts +0 -200
  138. package/assets/plugin/live/user-data-stream-controller.js +0 -579
  139. package/assets/plugin/live/user-data-stream-ws.d.ts +0 -22
  140. package/assets/plugin/live/user-data-stream-ws.js +0 -63
  141. package/assets/plugin/live/user-data-stream.d.ts +0 -243
  142. package/assets/plugin/live/user-data-stream.js +0 -704
  143. package/assets/plugin/logger.d.ts +0 -2
  144. package/assets/plugin/logger.js +0 -2
  145. package/assets/plugin/mfe.d.ts +0 -21
  146. package/assets/plugin/mfe.js +0 -68
  147. package/assets/plugin/onboarding/mode-ladder.d.ts +0 -1
  148. package/assets/plugin/onboarding/mode-ladder.js +0 -3
  149. package/assets/plugin/onboarding/runtime.d.ts +0 -90
  150. package/assets/plugin/onboarding/runtime.js +0 -184
  151. package/assets/plugin/openclaw.plugin.json +0 -92
  152. package/assets/plugin/paper-adapter.d.ts +0 -24
  153. package/assets/plugin/paper-adapter.js +0 -91
  154. package/assets/plugin/persistence/state-manager.d.ts +0 -66
  155. package/assets/plugin/persistence/state-manager.js +0 -222
  156. package/assets/plugin/pinned-plan.d.ts +0 -9
  157. package/assets/plugin/pinned-plan.js +0 -23
  158. package/assets/plugin/portfolio/reentry-tracker.d.ts +0 -36
  159. package/assets/plugin/portfolio/reentry-tracker.js +0 -127
  160. package/assets/plugin/portfolio/wave9-admission.d.ts +0 -67
  161. package/assets/plugin/portfolio/wave9-admission.js +0 -262
  162. package/assets/plugin/portfolio/wave9-policy.d.ts +0 -36
  163. package/assets/plugin/portfolio/wave9-policy.js +0 -183
  164. package/assets/plugin/risk/pre-trade-check.d.ts +0 -38
  165. package/assets/plugin/risk/pre-trade-check.js +0 -345
  166. package/assets/plugin/risk/pre-trade-types.d.ts +0 -60
  167. package/assets/plugin/risk/pre-trade-types.js +0 -3
  168. package/assets/plugin/shadow/shadow-tracker.d.ts +0 -36
  169. package/assets/plugin/shadow/shadow-tracker.js +0 -151
  170. package/assets/plugin/shadow/types.d.ts +0 -42
  171. package/assets/plugin/shadow/types.js +0 -20
  172. package/assets/plugin/shared/indicators-extended.d.ts +0 -52
  173. package/assets/plugin/shared/indicators-extended.js +0 -291
  174. package/assets/plugin/shared/indicators.d.ts +0 -15
  175. package/assets/plugin/shared/indicators.js +0 -114
  176. package/assets/plugin/signals/conditions/registry.d.ts +0 -16
  177. package/assets/plugin/signals/conditions/registry.js +0 -1333
  178. package/assets/plugin/signals/conditions/types.d.ts +0 -1
  179. package/assets/plugin/signals/conditions/types.js +0 -4
  180. package/assets/plugin/signals/direction-rules.d.ts +0 -3
  181. package/assets/plugin/signals/direction-rules.js +0 -24
  182. package/assets/plugin/signals/entry-rules.d.ts +0 -6
  183. package/assets/plugin/signals/entry-rules.js +0 -33
  184. package/assets/plugin/signals/serialize-context.d.ts +0 -4
  185. package/assets/plugin/signals/serialize-context.js +0 -39
  186. package/assets/plugin/signals/stop-rules.d.ts +0 -3
  187. package/assets/plugin/signals/stop-rules.js +0 -48
  188. package/assets/plugin/signals/strategy-adapter.d.ts +0 -47
  189. package/assets/plugin/signals/strategy-adapter.js +0 -209
  190. package/assets/plugin/signals/types.d.ts +0 -1
  191. package/assets/plugin/signals/types.js +0 -8
  192. package/assets/plugin/simulator/exchange-simulator.d.ts +0 -105
  193. package/assets/plugin/simulator/exchange-simulator.js +0 -759
  194. package/assets/plugin/simulator/fill-engine.d.ts +0 -53
  195. package/assets/plugin/simulator/fill-engine.js +0 -280
  196. package/assets/plugin/simulator/paper-market-feed.d.ts +0 -26
  197. package/assets/plugin/simulator/paper-market-feed.js +0 -104
  198. package/assets/plugin/simulator/realistic-fills.d.ts +0 -59
  199. package/assets/plugin/simulator/realistic-fills.js +0 -175
  200. package/assets/plugin/simulator/types.d.ts +0 -228
  201. package/assets/plugin/simulator/types.js +0 -43
  202. package/assets/plugin/strategy/builtin-strategies.d.ts +0 -2
  203. package/assets/plugin/strategy/builtin-strategies.js +0 -113
  204. package/assets/plugin/strategy/condition-registry.d.ts +0 -3
  205. package/assets/plugin/strategy/condition-registry.js +0 -153
  206. package/assets/plugin/strategy/evaluator.d.ts +0 -70
  207. package/assets/plugin/strategy/evaluator.js +0 -98
  208. package/assets/plugin/tools/assessment-validation.d.ts +0 -141
  209. package/assets/plugin/tools/assessment-validation.js +0 -473
  210. package/assets/plugin/tools/attach-brackets.d.ts +0 -39
  211. package/assets/plugin/tools/attach-brackets.js +0 -564
  212. package/assets/plugin/tools/audit-bracket-protection.d.ts +0 -49
  213. package/assets/plugin/tools/audit-bracket-protection.js +0 -683
  214. package/assets/plugin/tools/bracket-control.d.ts +0 -12
  215. package/assets/plugin/tools/bracket-control.js +0 -35
  216. package/assets/plugin/tools/cancel-all-orders.d.ts +0 -9
  217. package/assets/plugin/tools/cancel-all-orders.js +0 -8
  218. package/assets/plugin/tools/cancel-order.d.ts +0 -14
  219. package/assets/plugin/tools/cancel-order.js +0 -60
  220. package/assets/plugin/tools/check-position-health.d.ts +0 -46
  221. package/assets/plugin/tools/check-position-health.js +0 -194
  222. package/assets/plugin/tools/clear-exchange-credentials.d.ts +0 -24
  223. package/assets/plugin/tools/clear-exchange-credentials.js +0 -70
  224. package/assets/plugin/tools/close-position.d.ts +0 -45
  225. package/assets/plugin/tools/close-position.js +0 -722
  226. package/assets/plugin/tools/create-order.d.ts +0 -82
  227. package/assets/plugin/tools/create-order.js +0 -1544
  228. package/assets/plugin/tools/exit-gate.d.ts +0 -58
  229. package/assets/plugin/tools/exit-gate.js +0 -162
  230. package/assets/plugin/tools/fetch-balance.d.ts +0 -5
  231. package/assets/plugin/tools/fetch-balance.js +0 -4
  232. package/assets/plugin/tools/fetch-ohlcv.d.ts +0 -11
  233. package/assets/plugin/tools/fetch-ohlcv.js +0 -8
  234. package/assets/plugin/tools/fetch-open-orders.d.ts +0 -7
  235. package/assets/plugin/tools/fetch-open-orders.js +0 -4
  236. package/assets/plugin/tools/fetch-positions.d.ts +0 -7
  237. package/assets/plugin/tools/fetch-positions.js +0 -4
  238. package/assets/plugin/tools/fetch-ticker.d.ts +0 -11
  239. package/assets/plugin/tools/fetch-ticker.js +0 -5
  240. package/assets/plugin/tools/get-agent-profile.d.ts +0 -4
  241. package/assets/plugin/tools/get-agent-profile.js +0 -6
  242. package/assets/plugin/tools/get-analytics.d.ts +0 -6
  243. package/assets/plugin/tools/get-analytics.js +0 -7
  244. package/assets/plugin/tools/get-backtest.d.ts +0 -12
  245. package/assets/plugin/tools/get-backtest.js +0 -91
  246. package/assets/plugin/tools/get-basis.d.ts +0 -7
  247. package/assets/plugin/tools/get-basis.js +0 -7
  248. package/assets/plugin/tools/get-bracket-config.d.ts +0 -11
  249. package/assets/plugin/tools/get-bracket-config.js +0 -24
  250. package/assets/plugin/tools/get-cascade-risk.d.ts +0 -7
  251. package/assets/plugin/tools/get-cascade-risk.js +0 -8
  252. package/assets/plugin/tools/get-crypto-metrics.d.ts +0 -18
  253. package/assets/plugin/tools/get-crypto-metrics.js +0 -62
  254. package/assets/plugin/tools/get-cvd.d.ts +0 -6
  255. package/assets/plugin/tools/get-cvd.js +0 -6
  256. package/assets/plugin/tools/get-divergences.d.ts +0 -6
  257. package/assets/plugin/tools/get-divergences.js +0 -6
  258. package/assets/plugin/tools/get-funding-context.d.ts +0 -6
  259. package/assets/plugin/tools/get-funding-context.js +0 -16
  260. package/assets/plugin/tools/get-liquidation-levels.d.ts +0 -7
  261. package/assets/plugin/tools/get-liquidation-levels.js +0 -7
  262. package/assets/plugin/tools/get-liquidation-pulse.d.ts +0 -9
  263. package/assets/plugin/tools/get-liquidation-pulse.js +0 -22
  264. package/assets/plugin/tools/get-market-breadth.d.ts +0 -6
  265. package/assets/plugin/tools/get-market-breadth.js +0 -8
  266. package/assets/plugin/tools/get-market-intel.d.ts +0 -19
  267. package/assets/plugin/tools/get-market-intel.js +0 -116
  268. package/assets/plugin/tools/get-market-structure.d.ts +0 -47
  269. package/assets/plugin/tools/get-market-structure.js +0 -198
  270. package/assets/plugin/tools/get-my-mined-patterns.d.ts +0 -20
  271. package/assets/plugin/tools/get-my-mined-patterns.js +0 -61
  272. package/assets/plugin/tools/get-my-proposed-learnings.d.ts +0 -20
  273. package/assets/plugin/tools/get-my-proposed-learnings.js +0 -55
  274. package/assets/plugin/tools/get-my-recent-reviews.d.ts +0 -22
  275. package/assets/plugin/tools/get-my-recent-reviews.js +0 -66
  276. package/assets/plugin/tools/get-orderbook.d.ts +0 -21
  277. package/assets/plugin/tools/get-orderbook.js +0 -32
  278. package/assets/plugin/tools/get-pattern-scan.d.ts +0 -7
  279. package/assets/plugin/tools/get-pattern-scan.js +0 -8
  280. package/assets/plugin/tools/get-regime.d.ts +0 -6
  281. package/assets/plugin/tools/get-regime.js +0 -7
  282. package/assets/plugin/tools/get-relevant-learnings.d.ts +0 -21
  283. package/assets/plugin/tools/get-relevant-learnings.js +0 -65
  284. package/assets/plugin/tools/get-resting-liquidity.d.ts +0 -6
  285. package/assets/plugin/tools/get-resting-liquidity.js +0 -11
  286. package/assets/plugin/tools/get-risk-scenario.d.ts +0 -29
  287. package/assets/plugin/tools/get-risk-scenario.js +0 -47
  288. package/assets/plugin/tools/get-risk-summary.d.ts +0 -51
  289. package/assets/plugin/tools/get-risk-summary.js +0 -118
  290. package/assets/plugin/tools/get-sentiment.d.ts +0 -4
  291. package/assets/plugin/tools/get-sentiment.js +0 -6
  292. package/assets/plugin/tools/get-session-review.d.ts +0 -7
  293. package/assets/plugin/tools/get-session-review.js +0 -8
  294. package/assets/plugin/tools/get-setup-detail.d.ts +0 -7
  295. package/assets/plugin/tools/get-setup-detail.js +0 -322
  296. package/assets/plugin/tools/get-signals.d.ts +0 -15
  297. package/assets/plugin/tools/get-signals.js +0 -54
  298. package/assets/plugin/tools/get-sizing.d.ts +0 -6
  299. package/assets/plugin/tools/get-sizing.js +0 -6
  300. package/assets/plugin/tools/get-trade-feedback.d.ts +0 -7
  301. package/assets/plugin/tools/get-trade-feedback.js +0 -8
  302. package/assets/plugin/tools/get-trade-flow.d.ts +0 -7
  303. package/assets/plugin/tools/get-trade-flow.js +0 -7
  304. package/assets/plugin/tools/get-volume-analysis.d.ts +0 -21
  305. package/assets/plugin/tools/get-volume-analysis.js +0 -74
  306. package/assets/plugin/tools/get-volume-profile.d.ts +0 -7
  307. package/assets/plugin/tools/get-volume-profile.js +0 -7
  308. package/assets/plugin/tools/get-wave9-status.d.ts +0 -127
  309. package/assets/plugin/tools/get-wave9-status.js +0 -796
  310. package/assets/plugin/tools/helpers.d.ts +0 -26
  311. package/assets/plugin/tools/helpers.js +0 -39
  312. package/assets/plugin/tools/intel-api.d.ts +0 -34
  313. package/assets/plugin/tools/intel-api.js +0 -119
  314. package/assets/plugin/tools/intel-cache.d.ts +0 -25
  315. package/assets/plugin/tools/intel-cache.js +0 -148
  316. package/assets/plugin/tools/list-strategies.d.ts +0 -17
  317. package/assets/plugin/tools/list-strategies.js +0 -23
  318. package/assets/plugin/tools/modify-stop.d.ts +0 -21
  319. package/assets/plugin/tools/modify-stop.js +0 -120
  320. package/assets/plugin/tools/modify-target.d.ts +0 -21
  321. package/assets/plugin/tools/modify-target.js +0 -110
  322. package/assets/plugin/tools/propose-learning.d.ts +0 -22
  323. package/assets/plugin/tools/propose-learning.js +0 -65
  324. package/assets/plugin/tools/query-review-outcomes.d.ts +0 -30
  325. package/assets/plugin/tools/query-review-outcomes.js +0 -64
  326. package/assets/plugin/tools/query-trades.d.ts +0 -21
  327. package/assets/plugin/tools/query-trades.js +0 -37
  328. package/assets/plugin/tools/record-position-reviews.d.ts +0 -38
  329. package/assets/plugin/tools/record-position-reviews.js +0 -147
  330. package/assets/plugin/tools/save-strategy.d.ts +0 -16
  331. package/assets/plugin/tools/save-strategy.js +0 -46
  332. package/assets/plugin/tools/scan-pairs.d.ts +0 -22
  333. package/assets/plugin/tools/scan-pairs.js +0 -234
  334. package/assets/plugin/tools/score-setup.d.ts +0 -31
  335. package/assets/plugin/tools/score-setup.js +0 -268
  336. package/assets/plugin/tools/set-bracket-requirement.d.ts +0 -18
  337. package/assets/plugin/tools/set-bracket-requirement.js +0 -81
  338. package/assets/plugin/tools/set-exchange-credentials.d.ts +0 -25
  339. package/assets/plugin/tools/set-exchange-credentials.js +0 -80
  340. package/assets/plugin/tools/set-trading-mode.d.ts +0 -26
  341. package/assets/plugin/tools/set-trading-mode.js +0 -135
  342. package/assets/plugin/tools/test-exchange-credentials.d.ts +0 -16
  343. package/assets/plugin/tools/test-exchange-credentials.js +0 -100
  344. package/assets/plugin/tools/toggle-strategy.d.ts +0 -8
  345. package/assets/plugin/tools/toggle-strategy.js +0 -15
  346. package/assets/plugin/trading-params-cache.d.ts +0 -26
  347. package/assets/plugin/trading-params-cache.js +0 -52
  348. package/assets/plugin/types.d.ts +0 -134
  349. package/assets/plugin/types.js +0 -7
  350. package/assets/plugin/util/plugin-paths.d.ts +0 -3
  351. package/assets/plugin/util/plugin-paths.js +0 -15
  352. package/assets/plugin/venues/hyperliquid/hl-balance.d.ts +0 -116
  353. package/assets/plugin/venues/hyperliquid/hl-balance.js +0 -145
  354. package/assets/plugin/venues/hyperliquid/hl-bracket-coordinator.d.ts +0 -123
  355. package/assets/plugin/venues/hyperliquid/hl-bracket-coordinator.js +0 -533
  356. package/assets/plugin/venues/hyperliquid/hl-brackets.d.ts +0 -102
  357. package/assets/plugin/venues/hyperliquid/hl-brackets.js +0 -172
  358. package/assets/plugin/venues/hyperliquid/hl-cloid.d.ts +0 -22
  359. package/assets/plugin/venues/hyperliquid/hl-cloid.js +0 -82
  360. package/assets/plugin/venues/hyperliquid/hl-info-cache.d.ts +0 -46
  361. package/assets/plugin/venues/hyperliquid/hl-info-cache.js +0 -125
  362. package/assets/plugin/venues/hyperliquid/hl-live-adapter.d.ts +0 -146
  363. package/assets/plugin/venues/hyperliquid/hl-live-adapter.js +0 -728
  364. package/assets/plugin/venues/hyperliquid/hl-precision.d.ts +0 -61
  365. package/assets/plugin/venues/hyperliquid/hl-precision.js +0 -176
  366. package/assets/plugin/venues/hyperliquid/hl-private.d.ts +0 -88
  367. package/assets/plugin/venues/hyperliquid/hl-private.js +0 -357
  368. package/assets/plugin/venues/hyperliquid/hl-public.d.ts +0 -79
  369. package/assets/plugin/venues/hyperliquid/hl-public.js +0 -436
  370. package/assets/plugin/venues/hyperliquid/hl-rate-gate.d.ts +0 -57
  371. package/assets/plugin/venues/hyperliquid/hl-rate-gate.js +0 -220
  372. package/assets/plugin/venues/hyperliquid/hl-user-stream.d.ts +0 -90
  373. package/assets/plugin/venues/hyperliquid/hl-user-stream.js +0 -220
  374. package/assets/plugin/venues/registry.d.ts +0 -38
  375. package/assets/plugin/venues/registry.js +0 -46
  376. package/assets/plugin/venues/symbols.d.ts +0 -43
  377. package/assets/plugin/venues/symbols.js +0 -107
  378. package/assets/plugin/wave9/live-account-capture.d.ts +0 -67
  379. package/assets/plugin/wave9/live-account-capture.js +0 -435
  380. package/assets/plugin/wave9/live-autonomous-protection.d.ts +0 -39
  381. package/assets/plugin/wave9/live-autonomous-protection.js +0 -112
  382. package/assets/plugin/wave9/live-durable-reconciliation-scheduler.d.ts +0 -33
  383. package/assets/plugin/wave9/live-durable-reconciliation-scheduler.js +0 -115
  384. package/assets/plugin/wave9/live-execution-ledger.d.ts +0 -107
  385. package/assets/plugin/wave9/live-execution-ledger.js +0 -498
  386. package/assets/plugin/wave9/live-position-confirmation.d.ts +0 -18
  387. package/assets/plugin/wave9/live-position-confirmation.js +0 -111
  388. package/assets/plugin/wave9/live-residual-protection.d.ts +0 -18
  389. package/assets/plugin/wave9/live-residual-protection.js +0 -250
  390. package/assets/plugin/wave9/live-startup-reconciliation.d.ts +0 -38
  391. package/assets/plugin/wave9/live-startup-reconciliation.js +0 -454
  392. package/assets/plugin/wave9/live-symbol-ownership.d.ts +0 -20
  393. package/assets/plugin/wave9/live-symbol-ownership.js +0 -132
  394. package/assets/plugin/wave9/paper-admission-guard.d.ts +0 -199
  395. package/assets/plugin/wave9/paper-admission-guard.js +0 -650
  396. package/assets/plugin/wave9/usdm-evidence-provider.d.ts +0 -42
  397. package/assets/plugin/wave9/usdm-evidence-provider.js +0 -133
@@ -1,1544 +0,0 @@
1
- // Tool: create_order — order execution with real price data + pre-trade risk gate
2
- // Readiness gate: BLOCKED unless adapter.readiness === 'READY'.
3
- import { randomUUID } from 'node:crypto';
4
- import { formatError } from '../logger.js';
5
- import { getQuoteBalance, getQuoteWalletBalance } from '../balance-utils.js';
6
- import { fetchCurrentPrice, fetchOrderBook, isError } from './helpers.js';
7
- import { validateCreateOrder, sanitizeRealizationRule, validateProtectiveGeometry } from './assessment-validation.js';
8
- import { preTradeRiskCheck, getDefaultPreTradeLimits, computeConsecutiveLosses } from '../risk/pre-trade-check.js';
9
- import { bracketsEnabled, loadBracketMode, loadBracketRequirements } from '../config/brackets-config.js';
10
- import { onCreateOrderFilled } from '../ingest/position-auto-capture.js';
11
- import { wave9ClientOrderId, } from '../wave9/live-execution-ledger.js';
12
- import { inspectWave9LiveSymbolOwnership, } from '../wave9/live-symbol-ownership.js';
13
- import { confirmWave9LivePositionFlat } from '../wave9/live-position-confirmation.js';
14
- import { reprotectWave9LiveResidual, } from '../wave9/live-residual-protection.js';
15
- import { captureWave9PaperAccountSnapshot, isWave9ManagedPosition, WAVE9_BUNDLE_SETUP_TYPE, } from '../wave9/paper-admission-guard.js';
16
- function currentWave9ExecutionMode(deps) {
17
- const runtimeMode = deps.wave9TradingModeCheck?.() ?? deps.adapter.mode;
18
- if (!deps.adapter.isLive && deps.adapter.mode === 'PAPER' && runtimeMode === 'PAPER')
19
- return 'PAPER';
20
- if (deps.adapter.isLive && deps.adapter.mode === 'LIVE' && runtimeMode === 'LIVE')
21
- return 'LIVE';
22
- return null;
23
- }
24
- function residualProtectionMessage(outcome) {
25
- const protection = outcome.residualProtection;
26
- if (!protection)
27
- return ' Residual protection state is unavailable.';
28
- return protection.status === 'protected'
29
- ? ` The residual is protected by the proven frozen native stop ${protection.stopPrice}` +
30
- `${protection.stopClientOrderId ? ` (${protection.stopClientOrderId})` : ''}.`
31
- : ` ${protection.detail}`;
32
- }
33
- function confirmedWave9EntryPrice(order, side, stopPrice) {
34
- const average = typeof order.average === 'number'
35
- && Number.isFinite(order.average)
36
- && order.average > 0
37
- ? order.average
38
- : undefined;
39
- const limitPrice = typeof order.price === 'number'
40
- && Number.isFinite(order.price)
41
- && order.price > 0
42
- ? order.price
43
- : undefined;
44
- const responsePrice = average ?? limitPrice;
45
- if (responsePrice === undefined)
46
- return undefined;
47
- return side === 'buy'
48
- ? (responsePrice > stopPrice ? responsePrice : undefined)
49
- : (responsePrice < stopPrice ? responsePrice : undefined);
50
- }
51
- function confirmedWave9ExchangeOrderId(order) {
52
- return typeof order.id === 'string'
53
- && order.id.length > 0
54
- && order.id.length <= 512
55
- && order.id.trim() === order.id
56
- ? order.id
57
- : undefined;
58
- }
59
- async function flattenFailedWave9LiveEntry(adapter, ledger, symbol, candidateId, identity, residualProtector = reprotectWave9LiveResidual) {
60
- let observation = await observeWave9LiveResidual(adapter, symbol, identity.side);
61
- if (observation.status !== 'visible') {
62
- const alreadyFlat = await confirmWave9LivePositionFlat(adapter, symbol);
63
- if (alreadyFlat.confirmed) {
64
- let terminalPersistenceError;
65
- try {
66
- await ledger.markClosed(candidateId);
67
- }
68
- catch (err) {
69
- terminalPersistenceError = formatError(err);
70
- }
71
- return {
72
- confirmedFlat: true,
73
- confirmationDetail: alreadyFlat.detail,
74
- closeAttempted: false,
75
- ...(terminalPersistenceError ? { terminalPersistenceError } : {}),
76
- };
77
- }
78
- // A terminal market fill can become visible during the stable-flat
79
- // window. Re-read once as positive identity evidence; the failed flat
80
- // proof itself never authorizes a symbol-wide mutation.
81
- observation = await observeWave9LiveResidual(adapter, symbol, identity.side);
82
- }
83
- const residualQuantity = observation.status === 'visible'
84
- ? observation.quantity
85
- : undefined;
86
- if (!Number.isFinite(identity.maximumQuantity)
87
- || identity.maximumQuantity <= 0
88
- || residualQuantity === undefined
89
- || !Number.isFinite(residualQuantity)
90
- || residualQuantity <= 0
91
- || residualQuantity > identity.maximumQuantity) {
92
- let stopPrice = 0;
93
- try {
94
- stopPrice = ledger.getByCandidateId(candidateId)?.initialStop ?? 0;
95
- }
96
- catch {
97
- // Keep the identity refusal as the primary error.
98
- }
99
- return {
100
- confirmedFlat: false,
101
- confirmationDetail: `Wave 9 exposure identity fence refused symbol-wide flatten: ${observation.detail}; ` +
102
- `maximum attributable quantity ${identity.maximumQuantity}`,
103
- closeAttempted: false,
104
- residualProtection: {
105
- status: 'manual_intervention',
106
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: refusing native-stop replacement or ` +
107
- `symbol-wide flatten because current exposure is not exactly candidate-bound ` +
108
- `(${observation.detail}; maximum ${identity.maximumQuantity})`,
109
- candidateId,
110
- symbol,
111
- stopPrice,
112
- ...(residualQuantity !== undefined ? { residualQuantity } : {}),
113
- },
114
- };
115
- }
116
- let flattenError;
117
- try {
118
- await adapter.closePosition(symbol, 'bracket_attach_failed');
119
- }
120
- catch (err) {
121
- flattenError = formatError(err);
122
- }
123
- const confirmation = await confirmWave9LivePositionFlat(adapter, symbol);
124
- const terminalSafeFlat = confirmation.confirmed && flattenError === undefined;
125
- let terminalPersistenceError;
126
- if (terminalSafeFlat) {
127
- try {
128
- await ledger.markClosed(candidateId);
129
- }
130
- catch (err) {
131
- terminalPersistenceError = formatError(err);
132
- }
133
- }
134
- let residualProtection;
135
- if (!confirmation.confirmed) {
136
- try {
137
- residualProtection = await residualProtector(adapter, ledger, candidateId, symbol);
138
- }
139
- catch (err) {
140
- let stopPrice = 0;
141
- try {
142
- stopPrice = ledger.getByCandidateId(candidateId)?.initialStop ?? 0;
143
- }
144
- catch {
145
- // Preserve the original re-protection failure in the escalation.
146
- }
147
- residualProtection = {
148
- status: 'manual_intervention',
149
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: residual re-protection threw: ${formatError(err)}`,
150
- candidateId,
151
- symbol,
152
- stopPrice,
153
- };
154
- }
155
- }
156
- return {
157
- confirmedFlat: terminalSafeFlat,
158
- confirmationDetail: confirmation.confirmed && flattenError
159
- ? `trusted flat snapshots were observed, but the emergency close mutation was not acknowledged ` +
160
- `(${flattenError}); positive-fill settlement may still be lagging`
161
- : confirmation.detail,
162
- closeAttempted: true,
163
- ...(flattenError ? { flattenError } : {}),
164
- ...(terminalPersistenceError ? { terminalPersistenceError } : {}),
165
- ...(residualProtection ? { residualProtection } : {}),
166
- };
167
- }
168
- function canonicalWave9Symbol(symbol) {
169
- return symbol.toUpperCase().replace(/[^A-Z0-9]/g, '').replace(/USDTUSDT$/, 'USDT');
170
- }
171
- async function observeWave9LiveResidual(adapter, symbol, side) {
172
- let positions;
173
- try {
174
- positions = await adapter.getPositionsOrNull(symbol);
175
- }
176
- catch (err) {
177
- return { status: 'unknown', detail: `position read threw: ${formatError(err)}` };
178
- }
179
- if (positions === null || !Array.isArray(positions)) {
180
- return { status: 'unknown', detail: 'position state is unknown' };
181
- }
182
- const target = canonicalWave9Symbol(symbol);
183
- const expectedPositionSide = side === 'buy' ? 'long' : 'short';
184
- const visible = [];
185
- for (const position of positions) {
186
- if (!position
187
- || typeof position.symbol !== 'string'
188
- || typeof position.contracts !== 'number'
189
- || !Number.isFinite(position.contracts)
190
- || position.contracts < 0) {
191
- return { status: 'unknown', detail: 'position snapshot contained malformed rows' };
192
- }
193
- if (canonicalWave9Symbol(position.symbol) !== target || position.contracts === 0)
194
- continue;
195
- if (position.side !== expectedPositionSide) {
196
- return {
197
- status: 'unknown',
198
- detail: `visible residual side ${position.side} does not match ${expectedPositionSide}`,
199
- };
200
- }
201
- visible.push(position);
202
- }
203
- if (visible.length === 0)
204
- return { status: 'flat', detail: 'one trusted snapshot is currently empty' };
205
- if (visible.length !== 1) {
206
- return { status: 'unknown', detail: 'multiple matching residual position rows were returned' };
207
- }
208
- return {
209
- status: 'visible',
210
- detail: `exchange reports ${visible[0].contracts} residual contracts`,
211
- quantity: visible[0].contracts,
212
- };
213
- }
214
- function candidateBoundFoundOrder(resolution, clientOrderId, symbol, side) {
215
- if (resolution.status !== 'found')
216
- return undefined;
217
- const order = resolution.order;
218
- if (order.clientOrderId !== clientOrderId
219
- || canonicalWave9Symbol(order.symbol) !== canonicalWave9Symbol(symbol)
220
- || order.side !== side
221
- || order.type !== 'market'
222
- || confirmedWave9ExchangeOrderId(order) === undefined
223
- || (order.status !== 'open' && order.status !== 'closed' && order.status !== 'canceled')
224
- || typeof order.amount !== 'number'
225
- || !Number.isFinite(order.amount)
226
- || order.amount <= 0
227
- || typeof order.filled !== 'number'
228
- || !Number.isFinite(order.filled)
229
- || order.filled < 0
230
- || order.filled > order.amount + 1e-9 * Math.max(1, order.amount))
231
- return undefined;
232
- return order;
233
- }
234
- async function resolveWave9EntryCid(adapter, clientOrderId, symbol) {
235
- if (!adapter.resolveOrderByClientId) {
236
- return { status: 'unknown', detail: 'deterministic client-order resolver is unavailable' };
237
- }
238
- try {
239
- return await adapter.resolveOrderByClientId(clientOrderId, symbol);
240
- }
241
- catch (err) {
242
- return { status: 'unknown', detail: `client-order resolution threw: ${formatError(err)}` };
243
- }
244
- }
245
- function flattenRecoveryOutcome(flatten, context, durableOpenEstablished) {
246
- if (flatten.confirmedFlat && !flatten.terminalPersistenceError) {
247
- return {
248
- status: 'terminal',
249
- detail: flatten.closeAttempted
250
- ? `${context}; emergency close reached stable flat and durable ownership is CLOSED`
251
- : `${context}; exchange was already stably flat and durable ownership is CLOSED`,
252
- };
253
- }
254
- if (flatten.confirmedFlat) {
255
- return {
256
- status: 'manual_intervention',
257
- detail: `${context}; stable flat was established, but durable CLOSED persistence failed: ` +
258
- `${flatten.terminalPersistenceError}`,
259
- };
260
- }
261
- if (flatten.residualProtection?.status === 'protected') {
262
- if (!durableOpenEstablished) {
263
- return {
264
- status: 'manual_intervention',
265
- detail: `${context}; the residual has a proven frozen native stop ` +
266
- `${flatten.residualProtection.stopPrice}, but durable OPEN ownership is unavailable. ` +
267
- 'IMMEDIATE MANUAL INTERVENTION REQUIRED',
268
- };
269
- }
270
- return {
271
- status: 'protected',
272
- detail: `${context}; emergency close left a residual, whose exact frozen native stop ` +
273
- `${flatten.residualProtection.stopPrice} is proven live`,
274
- };
275
- }
276
- return {
277
- status: 'manual_intervention',
278
- detail: `${context}; ${flatten.closeAttempted
279
- ? 'emergency close did not establish stable flat'
280
- : 'symbol-wide flatten was not authorized by the exposure identity fence'} ` +
281
- `(${flatten.confirmationDetail}). ` +
282
- `${flatten.residualProtection?.detail ?? 'IMMEDIATE MANUAL INTERVENTION REQUIRED'}`,
283
- };
284
- }
285
- async function recoverWave9LiveSubmitThrow(input) {
286
- const recoverCandidateExposure = async (foundOrder, recoveryQuantity, observation, context, entryTerminalProven) => {
287
- if (!entryTerminalProven) {
288
- let openPersistenceError;
289
- try {
290
- // Establish ownership without freezing an underreported fill while
291
- // the entry order may still add to the position.
292
- await input.ledger.markOpen(input.candidateId, {
293
- ...(foundOrder ? { exchangeOrderId: foundOrder.id } : {}),
294
- });
295
- }
296
- catch (err) {
297
- openPersistenceError = formatError(err);
298
- }
299
- let stopConfirmed = false;
300
- try {
301
- stopConfirmed = await input.confirmStop({
302
- candidateId: input.candidateId,
303
- symbol: input.symbol,
304
- entryClientOrderId: input.clientOrderId,
305
- entrySide: input.side,
306
- stopPrice: input.stopPrice,
307
- filledQuantity: recoveryQuantity,
308
- });
309
- }
310
- catch {
311
- stopConfirmed = false;
312
- }
313
- return {
314
- status: 'manual_intervention',
315
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: ${context}; the deterministic entry order ` +
316
- `is not proven terminal, so its final filled quantity is unknown and emergency flatten ` +
317
- `cannot safely claim CLOSED. ${stopConfirmed
318
- ? 'A candidate-bound close-all stop is currently proven live.'
319
- : 'Exact native stop protection is unconfirmed.'}` +
320
- `${openPersistenceError ? ` Durable OPEN persistence failed (${openPersistenceError}).` : ''}`,
321
- };
322
- }
323
- const persistTerminalOpen = async () => {
324
- if (recoveryQuantity > input.authorizedQuantity) {
325
- return `final fill ${recoveryQuantity} exceeds authorized quantity ${input.authorizedQuantity}`;
326
- }
327
- const entryPrice = foundOrder
328
- ? confirmedWave9EntryPrice(foundOrder, input.side, input.stopPrice)
329
- : undefined;
330
- try {
331
- await input.ledger.markOpen(input.candidateId, {
332
- filledQuantity: recoveryQuantity,
333
- ...(foundOrder ? { exchangeOrderId: foundOrder.id } : {}),
334
- ...(entryPrice !== undefined ? { entryPrice } : {}),
335
- });
336
- return undefined;
337
- }
338
- catch (err) {
339
- const markOpenError = formatError(err);
340
- if (foundOrder
341
- && typeof input.ledger.reconcileOpenFillFromExactOrder === 'function') {
342
- try {
343
- await input.ledger.reconcileOpenFillFromExactOrder(input.candidateId, {
344
- filledQuantity: recoveryQuantity,
345
- exchangeOrderId: foundOrder.id,
346
- ...(entryPrice !== undefined ? { entryPrice } : {}),
347
- });
348
- return undefined;
349
- }
350
- catch (reconcileError) {
351
- return `${markOpenError}; exact terminal fill reconciliation failed: ${formatError(reconcileError)}`;
352
- }
353
- }
354
- return markOpenError;
355
- }
356
- };
357
- const openPersistenceError = await persistTerminalOpen();
358
- const maximumRecoverableQuantity = Math.min(recoveryQuantity, input.authorizedQuantity);
359
- let currentObservation = observation;
360
- if (currentObservation.status !== 'visible') {
361
- const flat = await confirmWave9LivePositionFlat(input.adapter, input.symbol);
362
- if (flat.confirmed) {
363
- if (openPersistenceError) {
364
- return {
365
- status: 'manual_intervention',
366
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: exact terminal fill and stable flatness ` +
367
- `were proven, but durable OPEN persistence failed (${openPersistenceError})`,
368
- };
369
- }
370
- try {
371
- await input.ledger.markClosed(input.candidateId);
372
- return {
373
- status: 'terminal',
374
- detail: `${context}; exact terminal fill is no longer present and exchange flatness ` +
375
- `remained stable (${flat.detail}); durable ownership is CLOSED`,
376
- };
377
- }
378
- catch (err) {
379
- return {
380
- status: 'manual_intervention',
381
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: exact terminal fill and stable flatness ` +
382
- `were proven, but CLOSED persistence failed (${formatError(err)})`,
383
- };
384
- }
385
- }
386
- // Visibility can lag the exact order lookup. Re-read only as positive
387
- // identity evidence after the stable-flat window; never interpret a
388
- // failed/unknown flat proof as permission to close a symbol.
389
- currentObservation = await observeWave9LiveResidual(input.adapter, input.symbol, input.side);
390
- }
391
- const quantity = currentObservation.status === 'visible'
392
- ? currentObservation.quantity
393
- : undefined;
394
- if (quantity === undefined
395
- || !Number.isFinite(quantity)
396
- || quantity <= 0
397
- || quantity > maximumRecoverableQuantity) {
398
- return {
399
- status: 'manual_intervention',
400
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: exact terminal entry fill is proven, but ` +
401
- `current same-symbol exposure is not safely candidate-bound (${currentObservation.detail}; ` +
402
- `maximum attributable quantity ${maximumRecoverableQuantity}). Refusing native-stop ` +
403
- `replacement or symbol-wide flatten.${openPersistenceError
404
- ? ` Durable OPEN persistence also failed (${openPersistenceError}).`
405
- : ' Durable ownership remains OPEN.'}`,
406
- };
407
- }
408
- if (!openPersistenceError) {
409
- let stopConfirmed = false;
410
- try {
411
- stopConfirmed = await input.confirmStop({
412
- candidateId: input.candidateId,
413
- symbol: input.symbol,
414
- entryClientOrderId: input.clientOrderId,
415
- entrySide: input.side,
416
- stopPrice: input.stopPrice,
417
- filledQuantity: recoveryQuantity,
418
- });
419
- }
420
- catch {
421
- stopConfirmed = false;
422
- }
423
- if (stopConfirmed) {
424
- return {
425
- status: 'protected',
426
- detail: `${context}; the deterministic entry was recovered OPEN with an exact proven native stop`,
427
- ...(foundOrder ? { order: foundOrder } : {}),
428
- };
429
- }
430
- }
431
- // A positive exact fill establishes durable ownership, but does not by
432
- // itself authorize a symbol-wide mutation. The helper re-observes current
433
- // exposure and runs close/re-protection only when side and quantity still
434
- // prove that the residual can belong to this candidate.
435
- const flatten = await flattenFailedWave9LiveEntry(input.adapter, input.ledger, input.symbol, input.candidateId, {
436
- side: input.side,
437
- maximumQuantity: Math.min(recoveryQuantity, input.authorizedQuantity),
438
- }, input.residualProtector);
439
- const outcome = flattenRecoveryOutcome(flatten, openPersistenceError
440
- ? `${context}; OPEN persistence failed (${openPersistenceError}); ${observation.detail}`
441
- : `${context}; exact stop was unavailable; ${observation.detail}`, openPersistenceError === undefined);
442
- return foundOrder && outcome.status === 'protected'
443
- ? { ...outcome, order: foundOrder }
444
- : outcome;
445
- };
446
- const exactStopProven = async (quantity) => {
447
- let stopConfirmed = false;
448
- try {
449
- stopConfirmed = await input.confirmStop({
450
- candidateId: input.candidateId,
451
- symbol: input.symbol,
452
- entryClientOrderId: input.clientOrderId,
453
- entrySide: input.side,
454
- stopPrice: input.stopPrice,
455
- filledQuantity: quantity,
456
- });
457
- }
458
- catch {
459
- stopConfirmed = false;
460
- }
461
- return stopConfirmed;
462
- };
463
- const terminalizeOpenEntry = async (order) => {
464
- try {
465
- await input.adapter.cancelOrder(order.id, input.symbol);
466
- }
467
- catch (err) {
468
- return { error: `exact open-entry cancellation failed: ${formatError(err)}` };
469
- }
470
- const afterCancel = await resolveWave9EntryCid(input.adapter, input.clientOrderId, input.symbol);
471
- if (afterCancel.status === 'confirmed_absent') {
472
- return order.filled === 0
473
- ? { confirmedAbsent: true }
474
- : { error: 'positive-fill entry disappeared after cancellation; final fill is unknown' };
475
- }
476
- const resolved = candidateBoundFoundOrder(afterCancel, input.clientOrderId, input.symbol, input.side);
477
- if (!resolved) {
478
- return {
479
- error: afterCancel.status === 'unknown'
480
- ? `post-cancel entry state is unknown (${afterCancel.detail})`
481
- : 'post-cancel entry identity is mismatched',
482
- };
483
- }
484
- return resolved.status === 'open'
485
- ? { error: 'exact entry order remains open after cancellation' }
486
- : { order: resolved };
487
- };
488
- const acknowledgedOrder = input.acknowledgedOrder
489
- ? candidateBoundFoundOrder({ status: 'found', order: input.acknowledgedOrder }, input.clientOrderId, input.symbol, input.side)
490
- : undefined;
491
- let resolution = acknowledgedOrder
492
- ? { status: 'found', order: acknowledgedOrder }
493
- : await resolveWave9EntryCid(input.adapter, input.clientOrderId, input.symbol);
494
- let foundOrder = candidateBoundFoundOrder(resolution, input.clientOrderId, input.symbol, input.side);
495
- if (resolution.status === 'found' && !foundOrder) {
496
- return {
497
- status: 'manual_intervention',
498
- detail: 'IMMEDIATE MANUAL INTERVENTION REQUIRED: client-order lookup returned mismatched candidate identity',
499
- };
500
- }
501
- let residual = await observeWave9LiveResidual(input.adapter, input.symbol, input.side);
502
- let foundFilled = foundOrder
503
- && Number.isFinite(foundOrder.filled)
504
- && foundOrder.filled > 0
505
- ? foundOrder.filled
506
- : undefined;
507
- if (foundFilled !== undefined) {
508
- if (foundOrder.status === 'open') {
509
- const terminalized = await terminalizeOpenEntry(foundOrder);
510
- if (!terminalized.order || terminalized.order.filled <= 0) {
511
- try {
512
- await input.ledger.markOpen(input.candidateId, {
513
- exchangeOrderId: foundOrder.id,
514
- });
515
- }
516
- catch {
517
- // The manual outcome remains load-bearing even if persistence fails.
518
- }
519
- return {
520
- status: 'manual_intervention',
521
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: positive partial fill has an unproven ` +
522
- `final quantity (${terminalized.error ?? 'post-cancel identity unavailable'}); ` +
523
- 'durable ownership remains OPEN without a frozen filled quantity',
524
- };
525
- }
526
- foundOrder = terminalized.order;
527
- foundFilled = terminalized.order.filled;
528
- }
529
- return recoverCandidateExposure(foundOrder, foundFilled, residual, `submit threw (${formatError(input.submitError)}) and the exact CID shows a positive fill`, true);
530
- }
531
- if (resolution.status === 'unknown' && residual.status === 'visible') {
532
- return recoverCandidateExposure(undefined, residual.quantity, residual, `submit threw and CID resolution is unknown with a same-side residual`, false);
533
- }
534
- // A proven candidate-bound close-all stop is itself strong evidence that
535
- // LiveAdapter progressed past submission even if position visibility lags.
536
- if (resolution.status === 'unknown'
537
- && await exactStopProven(input.authorizedQuantity)) {
538
- return recoverCandidateExposure(undefined, input.authorizedQuantity, residual, 'submit acknowledgement is unknown, but its exact candidate-bound native stop is live', false);
539
- }
540
- if (foundOrder?.status === 'open' && foundOrder.filled === 0) {
541
- const terminalized = await terminalizeOpenEntry(foundOrder);
542
- if (terminalized.error) {
543
- return {
544
- status: 'manual_intervention',
545
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: the deterministic zero-fill entry remains ` +
546
- `nonterminal (${terminalized.error})`,
547
- };
548
- }
549
- if (terminalized.confirmedAbsent) {
550
- resolution = { status: 'confirmed_absent' };
551
- foundOrder = undefined;
552
- }
553
- else if (terminalized.order) {
554
- resolution = { status: 'found', order: terminalized.order };
555
- foundOrder = terminalized.order;
556
- }
557
- }
558
- const flat = await confirmWave9LivePositionFlat(input.adapter, input.symbol);
559
- let finalResolution = await resolveWave9EntryCid(input.adapter, input.clientOrderId, input.symbol);
560
- let finalOrder = candidateBoundFoundOrder(finalResolution, input.clientOrderId, input.symbol, input.side);
561
- if (finalResolution.status === 'found' && !finalOrder) {
562
- return {
563
- status: 'manual_intervention',
564
- detail: 'IMMEDIATE MANUAL INTERVENTION REQUIRED: final client-order lookup returned mismatched candidate identity',
565
- };
566
- }
567
- if (finalOrder?.status === 'open') {
568
- const openOrder = finalOrder;
569
- const terminalized = await terminalizeOpenEntry(openOrder);
570
- if (terminalized.error || (!terminalized.order && !terminalized.confirmedAbsent)) {
571
- if (openOrder.filled > 0) {
572
- try {
573
- await input.ledger.markOpen(input.candidateId, {
574
- exchangeOrderId: openOrder.id,
575
- });
576
- }
577
- catch {
578
- // The explicit manual outcome below remains authoritative.
579
- }
580
- }
581
- return {
582
- status: 'manual_intervention',
583
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: final exact entry order is nonterminal ` +
584
- `(${terminalized.error ?? 'post-cancel state unavailable'})`,
585
- };
586
- }
587
- if (terminalized.confirmedAbsent) {
588
- finalResolution = { status: 'confirmed_absent' };
589
- finalOrder = undefined;
590
- }
591
- else {
592
- finalOrder = terminalized.order;
593
- finalResolution = { status: 'found', order: terminalized.order };
594
- }
595
- }
596
- residual = await observeWave9LiveResidual(input.adapter, input.symbol, input.side);
597
- if (finalOrder && Number.isFinite(finalOrder.filled) && finalOrder.filled > 0) {
598
- return recoverCandidateExposure(finalOrder, finalOrder.filled, residual, 'the exact CID resolved to a positive fill after the settlement window', true);
599
- }
600
- if (finalResolution.status === 'unknown' && residual.status === 'visible') {
601
- return recoverCandidateExposure(undefined, residual.quantity, residual, 'CID resolution remained unknown and a residual appeared during the settlement window', false);
602
- }
603
- if (finalResolution.status === 'unknown'
604
- && await exactStopProven(input.authorizedQuantity)) {
605
- return recoverCandidateExposure(undefined, input.authorizedQuantity, residual, 'CID resolution remained unknown, but an exact candidate-bound native stop appeared', false);
606
- }
607
- const terminalOrder = finalOrder?.status === 'canceled' && finalOrder.filled === 0;
608
- let terminalFlat = flat;
609
- if (finalResolution.status === 'confirmed_absent' || terminalOrder) {
610
- // Bind terminalization to a fresh stable-flat window *after* exact CID
611
- // terminal proof. The earlier settlement window can go stale while the
612
- // final lookup/cancellation runs.
613
- terminalFlat = await confirmWave9LivePositionFlat(input.adapter, input.symbol);
614
- }
615
- if (terminalFlat.confirmed
616
- && (finalResolution.status === 'confirmed_absent' || terminalOrder)) {
617
- try {
618
- await input.ledger.markClosed(input.candidateId);
619
- return {
620
- status: 'terminal',
621
- detail: `submit threw (${formatError(input.submitError)}), but the exact client order is ` +
622
- `${terminalOrder ? 'canceled with zero fill' : 'confirmed absent'} and the exchange ` +
623
- `remained stably flat`,
624
- };
625
- }
626
- catch (err) {
627
- return {
628
- status: 'manual_intervention',
629
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: entry absence and stable flat were proven, ` +
630
- `but CLOSED persistence failed (${formatError(err)})`,
631
- };
632
- }
633
- }
634
- return {
635
- status: 'manual_intervention',
636
- detail: `IMMEDIATE MANUAL INTERVENTION REQUIRED: submit outcome remains ambiguous; client-order ` +
637
- `resolution is ${finalResolution.status} and stable flat is ${terminalFlat.confirmed
638
- ? 'present but insufficient without deterministic absence'
639
- : `unconfirmed (${terminalFlat.detail})`}. The entry capability is consumed and must not be retried`,
640
- };
641
- }
642
- /** Build a PortfolioSnapshot from current simulator state (paper mode). */
643
- function buildPortfolioSnapshotFromSimulator(simulator) {
644
- const state = simulator.getState();
645
- const quoteCurrency = state.config.quoteCurrency;
646
- const walletBal = state.wallet[quoteCurrency];
647
- return {
648
- positions: state.positions.map(p => {
649
- const ticker = simulator.getLastTicker(p.symbol);
650
- return {
651
- symbol: p.symbol,
652
- side: p.side,
653
- quantity: p.quantity,
654
- entryPrice: p.entryPrice,
655
- markPrice: ticker?.last ?? p.entryPrice,
656
- };
657
- }),
658
- walletTotal: walletBal?.total ?? 0,
659
- walletAvailable: walletBal?.available ?? 0,
660
- sessionStartNav: simulator.getSessionStartNav(),
661
- equity: simulator.computeEquity(),
662
- isLive: false,
663
- };
664
- }
665
- /** Build a PortfolioSnapshot from adapter (live mode). */
666
- async function buildPortfolioSnapshotFromAdapter(adapter) {
667
- // Null-honest balance read where the adapter offers one (LiveAdapter) —
668
- // getBalance() collapses a FAILED fetch to a phantom-zero object, which
669
- // reads as walletTotal=0 against the real sessionStartNav → ~−100%
670
- // drawdown → RED zone rejects the entry for the wrong reason (and sizing
671
- // runs against walletAvailable=0).
672
- const balanceRead = adapter.getBalanceOrNull?.() ?? adapter.getBalance();
673
- const [balance, positions] = await Promise.all([
674
- balanceRead,
675
- adapter.getPositionsOrNull(),
676
- ]);
677
- // null = positions fetch FAILED (429 / weight-paced / transient). If we
678
- // collapsed that to [], existing open positions become invisible to the
679
- // pre-trade risk check below — gross exposure / max-open-positions / net
680
- // tilt would all be under-counted and a trade that should be blocked could
681
- // pass. Refuse to size off a phantom-flat account; abort so the agent
682
- // retries next heartbeat. Same null≠empty contract as the close/flatten path.
683
- if (positions === null) {
684
- throw new Error('Cannot build portfolio snapshot — position fetch failed (exchange data ' +
685
- 'unavailable, likely Binance 429 or transient). Order NOT placed; retry next heartbeat.');
686
- }
687
- // Same contract for the balance leg: null = fetch failed, NOT a zero wallet.
688
- if (balance === null) {
689
- throw new Error('Cannot build portfolio snapshot — balance fetch failed (exchange data ' +
690
- 'unavailable, likely Binance 429 or transient). Order NOT placed; retry next heartbeat.');
691
- }
692
- const walletTotal = getQuoteWalletBalance(balance);
693
- // Use the session-start NAV captured once at initialization, not the current balance.
694
- // If not available (shouldn't happen after init), fall back to current balance.
695
- const sessionNav = adapter.getSessionStartNav?.() ?? walletTotal;
696
- // Live Binance wallet already includes margin; equity = wallet + unrealized P&L.
697
- let liveEquity = walletTotal;
698
- for (const p of positions) {
699
- const mark = p.markPrice ?? p.entryPrice;
700
- const pnl = p.side === 'long'
701
- ? (mark - p.entryPrice) * p.contracts
702
- : (p.entryPrice - mark) * p.contracts;
703
- liveEquity += pnl;
704
- }
705
- return {
706
- positions: positions.map(p => ({
707
- symbol: p.symbol,
708
- side: p.side,
709
- quantity: p.contracts,
710
- entryPrice: p.entryPrice,
711
- markPrice: p.markPrice,
712
- })),
713
- walletTotal,
714
- walletAvailable: getQuoteBalance(balance.free),
715
- sessionStartNav: sessionNav,
716
- equity: liveEquity,
717
- isLive: true,
718
- };
719
- }
720
- export async function createOrderTool(args, deps) {
721
- const wave9Claimed = isWave9ManagedPosition({
722
- setupType: args.setup_type,
723
- missionId: args.mission_id,
724
- })
725
- || args.wave9_admission_token !== undefined
726
- || args.candidate_id !== undefined;
727
- if (wave9Claimed && deps.wave9VenueCheck?.() !== 'binance') {
728
- return { error: 'Wave 9 order refused: execution is pinned to Binance USD-M futures.' };
729
- }
730
- if (wave9Claimed && !deps.operationLock) {
731
- return { error: 'Wave 9 order refused: process-local trading operation lock is unavailable.' };
732
- }
733
- const initialWave9Mode = wave9Claimed ? currentWave9ExecutionMode(deps) : null;
734
- // Wave 9 is a dedicated targetless capability path. Never let a generic
735
- // order impersonate it, and require the adapter + mutable runtime mode to
736
- // agree exactly before accepting either PAPER or LIVE execution.
737
- if (wave9Claimed) {
738
- if (args.setup_type !== WAVE9_BUNDLE_SETUP_TYPE) {
739
- return { error: `Wave 9 order refused: setup_type must be exactly ${WAVE9_BUNDLE_SETUP_TYPE}.` };
740
- }
741
- if (!initialWave9Mode) {
742
- return {
743
- error: 'Wave 9 order refused: capability execution requires an exact PAPER/paper-adapter ' +
744
- 'or LIVE/live-adapter runtime match.',
745
- };
746
- }
747
- if ((deps.approvalMode ?? 'off') !== 'off') {
748
- return { error: 'Wave 9 order refused: approval mode must be off for immediate mechanical execution.' };
749
- }
750
- if (!deps.wave9AdmissionGuard || !args.wave9_admission_token || !args.candidate_id || !args.mission_id) {
751
- return { error: 'Wave 9 order refused: a dedicated short-lived admission token and exact candidate/mission identity are required.' };
752
- }
753
- if (args.type.toLowerCase() !== 'market' || args.price !== undefined || args.target_price !== undefined) {
754
- return { error: 'Wave 9 order refused: the frozen policy requires a targetless market order with no limit price.' };
755
- }
756
- const canonical = args.symbol.toUpperCase().replace(/[^A-Z0-9]/g, '').replace(/USDTUSDT$/, 'USDT');
757
- if (!canonical.endsWith('USDT') || canonical.length <= 4) {
758
- return { error: `Wave 9 order refused: unsupported execution symbol ${args.symbol}.` };
759
- }
760
- if (args.stopPrice === undefined || !Number.isFinite(args.stopPrice) || args.stopPrice <= 0) {
761
- return { error: 'Wave 9 order refused: a positive finite initial stop is required.' };
762
- }
763
- if (initialWave9Mode === 'LIVE') {
764
- if (!deps.captureWave9AccountFingerprint
765
- || !deps.confirmWave9LiveStop
766
- || !deps.wave9LiveLedger) {
767
- return {
768
- error: 'Wave 9 live order refused: authoritative live account capture, native-stop confirmation, ' +
769
- 'and the durable execution ledger are required.',
770
- };
771
- }
772
- if (!bracketsEnabled(loadBracketMode())) {
773
- return { error: 'Wave 9 live order refused: exchange-native bracket protection is not enabled.' };
774
- }
775
- }
776
- // Persist one unified USD-M key regardless of whether an older agent
777
- // echoes BTCUSDT or BTC/USDT. Guard matching remains canonical.
778
- args = { ...args, symbol: `${canonical.slice(0, -4)}/USDT:USDT` };
779
- }
780
- // ---- Readiness gate (live mode only) ----
781
- if (deps.adapter.readiness !== 'READY') {
782
- return { error: `Trading blocked: adapter is ${deps.adapter.readiness}. Wait for initialization to complete.` };
783
- }
784
- // ---- Assessment-schema gate (SKILL.md v2.10.0 — live mode only) ----
785
- // In live mode the agent must include thesis + setup_type + regime +
786
- // regime_confidence + scorecard_verdict + confluence_score. This is the
787
- // same data the heartbeat reassessment uses to detect scorecard reversal /
788
- // regime flip — if the agent can't produce it at entry, it can't produce
789
- // it mid-trade either.
790
- const gateCheck = validateCreateOrder(args, deps.adapter.isLive && !wave9Claimed);
791
- if (!gateCheck.ok) {
792
- return { error: gateCheck.error ?? 'Invalid create_order arguments.' };
793
- }
794
- // Validate side
795
- const side = args.side.toLowerCase();
796
- if (side !== 'buy' && side !== 'sell') {
797
- return { error: `Invalid side: ${args.side}. Must be 'buy' or 'sell'.` };
798
- }
799
- // Validate type
800
- const type = args.type.toLowerCase();
801
- if (type !== 'market' && type !== 'limit') {
802
- return { error: `Invalid type: ${args.type}. Must be 'market' or 'limit'.` };
803
- }
804
- // Validate amount
805
- if (!args.amount || args.amount <= 0) {
806
- return { error: 'Amount must be a positive number.' };
807
- }
808
- // Validate price for limit orders
809
- if (type === 'limit' && (!args.price || args.price <= 0)) {
810
- return { error: 'Limit orders require a positive price.' };
811
- }
812
- if (args.mission_id !== undefined
813
- && (typeof args.mission_id !== 'string'
814
- || !/^[A-Za-z0-9_-]{1,128}$/.test(args.mission_id))) {
815
- return { error: 'mission_id must be 1-128 letters, numbers, underscores, or hyphens.' };
816
- }
817
- if (!wave9Claimed
818
- && deps.adapter.isLive
819
- && deps.checkWave9LiveSymbolOwnership
820
- && !deps.operationLock) {
821
- return {
822
- error: 'Generic live create_order refused: the shared trading operation lock is required ' +
823
- 'while Wave 9 LIVE ownership checks are enabled.',
824
- };
825
- }
826
- if (!wave9Claimed && !deps.adapter.isLive) {
827
- try {
828
- deps.refreshPaperState?.();
829
- const positions = await deps.adapter.getPositionsOrNull();
830
- if (positions === null) {
831
- return { error: 'create_order refused: paper position state unavailable for Wave 9 isolation check.' };
832
- }
833
- if (positions.some(isWave9ManagedPosition)) {
834
- return {
835
- error: 'Generic create_order refused: the paper account contains an atomic Wave 9 position. ' +
836
- 'The Wave 9 forward book must remain isolated until all of its positions are flat.',
837
- };
838
- }
839
- }
840
- catch (err) {
841
- return { error: `create_order refused: Wave 9 isolation check failed (${formatError(err)}).` };
842
- }
843
- }
844
- // In paper mode, fetch latest ticker + order book for realistic fills
845
- let ticker = null;
846
- if (!deps.adapter.isLive) {
847
- const paperDeps = { binanceApi: deps.binanceApi, simulator: deps.adapter.getSimulator() };
848
- const [priceResult] = await Promise.all([
849
- fetchCurrentPrice(args.symbol, paperDeps),
850
- fetchOrderBook(args.symbol, paperDeps),
851
- ]);
852
- if (isError(priceResult))
853
- return priceResult;
854
- ticker = priceResult;
855
- }
856
- else {
857
- // Live mode: fetch ticker for risk check reference price
858
- const lastPrice = await deps.adapter.getLastPrice(args.symbol);
859
- if (lastPrice != null) {
860
- ticker = {
861
- symbol: args.symbol, last: lastPrice,
862
- bid: lastPrice, ask: lastPrice, baseVolume: 0, quoteVolume: 0,
863
- change: 0, percentage: 0, timestamp: Date.now(), datetime: new Date().toISOString(),
864
- };
865
- }
866
- }
867
- if (!ticker) {
868
- return { error: `Failed to fetch current price for ${args.symbol}` };
869
- }
870
- // Once market data exists, consume the capability before any runtime
871
- // geometry or generic-gate decision. A delayed stop that is now on the
872
- // wrong side is an execution divergence and must latch the daily event.
873
- let wave9Lease;
874
- if (wave9Claimed) {
875
- try {
876
- wave9Lease = await deps.operationLock.withAccountLock(`wave9-begin-create:${args.candidate_id ?? args.symbol}`, async () => {
877
- if (currentWave9ExecutionMode(deps) !== initialWave9Mode) {
878
- throw new Error('adapter or trading mode changed before Wave 9 admission began');
879
- }
880
- let accountFingerprint;
881
- if (initialWave9Mode === 'LIVE') {
882
- const ownership = deps.checkWave9LiveSymbolOwnership
883
- ? await deps.checkWave9LiveSymbolOwnership(args.symbol)
884
- : await inspectWave9LiveSymbolOwnership(deps.adapter, deps.wave9LiveLedger, args.symbol);
885
- if (ownership.status !== 'unowned'
886
- || ownership.reason === 'authoritative_symbol_flat') {
887
- throw new Error(`candidate-symbol ownership must be refreshed after ` +
888
- `${ownership.status}:${ownership.reason}`);
889
- }
890
- accountFingerprint = (await deps.captureWave9AccountFingerprint()).fingerprint;
891
- }
892
- else {
893
- deps.refreshPaperState?.();
894
- deps.adapter.getSimulator().getSessionStartNav();
895
- accountFingerprint = captureWave9PaperAccountSnapshot(deps.adapter).fingerprint;
896
- }
897
- return deps.wave9AdmissionGuard.beginEntry({
898
- token: args.wave9_admission_token,
899
- candidateId: args.candidate_id,
900
- missionId: args.mission_id,
901
- symbol: args.symbol,
902
- side,
903
- type,
904
- quantity: args.amount,
905
- stopPrice: args.stopPrice,
906
- setupType: args.setup_type,
907
- accountFingerprint,
908
- tradingMode: initialWave9Mode,
909
- });
910
- });
911
- }
912
- catch (err) {
913
- return { error: `Wave 9 admission refused: ${formatError(err)}` };
914
- }
915
- }
916
- const rejectWave9Divergence = (message, code) => {
917
- if (wave9Lease) {
918
- deps.wave9AdmissionGuard.abort(wave9Lease, code);
919
- wave9Lease = undefined;
920
- }
921
- return { error: `${wave9Claimed ? 'Wave 9 runtime divergence; daily event latched. ' : ''}${message}` };
922
- };
923
- // From beginEntry onward, every unexpected failure must release the
924
- // in-flight capability. Explicit geometry/risk divergences still use the
925
- // latching helper above; infrastructure/pure-computation throws are
926
- // retryable because no exchange mutation has been attempted.
927
- try {
928
- // ---- Pre-trade risk gate ----
929
- const orderPrice = type === 'limit' ? args.price : ticker.last;
930
- if (wave9Claimed && args.stopPrice != null) {
931
- if (!Number.isFinite(args.stopPrice) || args.stopPrice <= 0) {
932
- return rejectWave9Divergence('stopPrice must be a positive finite number.', 'invalid_stop_price');
933
- }
934
- if ((side === 'buy' && args.stopPrice >= orderPrice) ||
935
- (side === 'sell' && args.stopPrice <= orderPrice)) {
936
- return rejectWave9Divergence(`stopPrice is on the wrong side of entry ${orderPrice} for a ${side} order.`, 'actual_market_stop_geometry_diverged');
937
- }
938
- }
939
- // ---- Generic-path protective geometry gate (issue #200) ----
940
- // The wrong-side check above ran ONLY for wave9 orders; a generic entry with
941
- // its stop/invalidation on the profit side passed validation and was closed
942
- // by the stop-watcher seconds later (7 sub-5-minute kills on the 2026-07 HL
943
- // soak — one spawned the −$117.86 journal fabrication of issue #199).
944
- if (!wave9Claimed) {
945
- const geometryError = validateProtectiveGeometry({
946
- side: side,
947
- refPrice: orderPrice,
948
- stopPrice: args.stopPrice,
949
- invalidationPrice: args.invalidation_price,
950
- targetPrice: args.target_price,
951
- });
952
- if (geometryError) {
953
- return { error: `create_order rejected (protective geometry): ${geometryError}` };
954
- }
955
- }
956
- const proposed = {
957
- symbol: args.symbol,
958
- side: side,
959
- type: type,
960
- amount: args.amount,
961
- price: orderPrice,
962
- stopPrice: args.stopPrice,
963
- targetPrice: args.target_price,
964
- };
965
- // Build portfolio snapshot based on mode
966
- let portfolio;
967
- let consecutiveLosses = 0;
968
- if (!deps.adapter.isLive) {
969
- const simulator = deps.adapter.getSimulator();
970
- portfolio = buildPortfolioSnapshotFromSimulator(simulator);
971
- consecutiveLosses = computeConsecutiveLosses(simulator.getState().tradeHistory);
972
- }
973
- else {
974
- portfolio = await buildPortfolioSnapshotFromAdapter(deps.adapter);
975
- // In live mode, consecutive losses would come from intelligence DB — use 0 for now
976
- }
977
- // Bracket enforcement only applies in live mode when the feature is enabled.
978
- // Paper mode uses the stop-watcher and doesn't care about these flags.
979
- let bracketEnforcement;
980
- if (deps.adapter.isLive && bracketsEnabled(loadBracketMode())) {
981
- bracketEnforcement = wave9Claimed
982
- ? { requireStopLoss: true, requireTakeProfit: false }
983
- : loadBracketRequirements();
984
- }
985
- const riskCheck = preTradeRiskCheck(proposed, portfolio, getDefaultPreTradeLimits(), {
986
- volFactor: !deps.adapter.isLive ? deps.adapter.getSimulator().getVolFactor() : 1.0,
987
- consecutiveLosses,
988
- bracketEnforcement,
989
- });
990
- if (!riskCheck.allowed) {
991
- const reasons = riskCheck.violations.map(v => v.message).join('; ');
992
- return rejectWave9Divergence(`Order REJECTED by risk gate [${riskCheck.drawdownZone} zone]: ${reasons}`, `generic_pretrade_divergence:${riskCheck.violations.map((v) => v.rule).join(',')}`);
993
- }
994
- // Build position metadata from optional args (only if any metadata provided)
995
- // Sanitize numeric metadata — reject non-finite values, clamp ranges
996
- const num = (v) => typeof v === 'number' && Number.isFinite(v) ? v : undefined;
997
- const clamp = (v, min, max) => {
998
- const n = num(v);
999
- return n != null ? Math.max(min, Math.min(max, n)) : undefined;
1000
- };
1001
- const hasMetadata = args.mission_id || args.setup_type || args.thesis || args.target_price != null
1002
- || args.regime || args.scorecard_verdict || args.confluence_score != null
1003
- || args.invalidation_price != null || args.realization_rule != null;
1004
- const metadata = hasMetadata ? {
1005
- missionId: args.mission_id,
1006
- setupType: args.setup_type,
1007
- ...(wave9Claimed ? {
1008
- candidateId: args.candidate_id,
1009
- strategy: args.candidate_id.split('|')[2],
1010
- accruedFundingUsd: 0,
1011
- fundingThroughTime: args.candidate_id.split('|')[0],
1012
- } : {}),
1013
- thesis: args.thesis?.slice(0, 500), // Cap length
1014
- stopPrice: num(args.stopPrice),
1015
- targetPrice: num(args.target_price),
1016
- regime: args.regime,
1017
- regimeConfidence: clamp(args.regime_confidence, 0, 100),
1018
- scorecardVerdict: args.scorecard_verdict,
1019
- confluenceScore: clamp(args.confluence_score, 0, 10),
1020
- invalidationPrice: num(args.invalidation_price),
1021
- realizationRule: sanitizeRealizationRule(args.realization_rule),
1022
- } : undefined;
1023
- // ---- Approval-mode branches ----
1024
- // Both shadow and per_trade need full v2.10.0 metadata so the proposal is
1025
- // operator-meaningful. Metadata is the agent's responsibility; ingest creds
1026
- // are the operator's. Separate so the error messages can be precise.
1027
- const argsMetadataComplete = !!(args.thesis &&
1028
- args.setup_type &&
1029
- args.target_price != null &&
1030
- args.stopPrice != null &&
1031
- args.regime &&
1032
- typeof args.regime_confidence === 'number' &&
1033
- args.scorecard_verdict &&
1034
- typeof args.confluence_score === 'number');
1035
- const proposalPathWired = !!(deps.proposalManager && deps.userId);
1036
- const approvalMode = deps.approvalMode ?? 'off';
1037
- // per_trade mode: propose and EARLY-RETURN. The agent's call doesn't fire;
1038
- // the operator approves and ProposalDecisionListener fires via this same
1039
- // tool with proposalManager omitted (which takes the real path below).
1040
- //
1041
- // If proposalManager isn't wired (operator set approval.mode=per_trade in
1042
- // config but ingest creds aren't present), we fall through to the normal
1043
- // fire path — the startup warning is the operator's signal. We never trap
1044
- // the agent inside an unfireable per_trade branch.
1045
- if (approvalMode === 'per_trade' && proposalPathWired) {
1046
- if (!argsMetadataComplete) {
1047
- return {
1048
- error: 'approval.mode=per_trade requires the full v2.10.0 metadata block on every create_order: ' +
1049
- 'thesis, setup_type, target_price, stopPrice, regime, regime_confidence, ' +
1050
- 'scorecard_verdict, confluence_score. The operator approves on this metadata.',
1051
- };
1052
- }
1053
- try {
1054
- const result = deps.proposalManager.propose(deps.userId, {
1055
- symbol: args.symbol,
1056
- side: side,
1057
- orderType: type,
1058
- size: args.amount,
1059
- proposedEntry: orderPrice,
1060
- stopPrice: args.stopPrice,
1061
- targetPrice: args.target_price,
1062
- riskUsd: Math.abs(orderPrice - args.stopPrice) * args.amount,
1063
- thesis: args.thesis,
1064
- setupType: args.setup_type,
1065
- regime: args.regime,
1066
- regimeConfidence: args.regime_confidence,
1067
- scorecardVerdict: args.scorecard_verdict,
1068
- confluenceScore: args.confluence_score,
1069
- ...(args.supersedes_id ? { supersedesId: args.supersedes_id } : {}),
1070
- // Real, operator-actionable. Default would also resolve to 'real' but
1071
- // we set it explicitly so the intent reads at the call site.
1072
- origin: 'real',
1073
- });
1074
- return {
1075
- status: 'pending_approval',
1076
- proposal_id: result.proposalUuid,
1077
- hard_expires_at: result.hardExpiresAt.toISOString(),
1078
- setup_bucket: result.setupBucket,
1079
- message: `Order awaits operator approval (${result.setupBucket} bucket, expires ` +
1080
- `${result.hardExpiresAt.toISOString()}). Do not call create_order again ` +
1081
- `for this setup; the listener will fire on approval. If the operator ` +
1082
- `requests modifications in chat, call create_order with supersedes_id=` +
1083
- `<id from the chat-context header>.`,
1084
- };
1085
- }
1086
- catch (err) {
1087
- return { error: `Proposal submission failed: ${formatError(err)}` };
1088
- }
1089
- }
1090
- // shadow mode: dual-write a telemetry proposal AND continue to the real
1091
- // fire below. Never blocks the hot path on a propose() failure.
1092
- if (approvalMode === 'shadow' && proposalPathWired && argsMetadataComplete) {
1093
- try {
1094
- deps.proposalManager.propose(deps.userId, {
1095
- symbol: args.symbol,
1096
- side: side,
1097
- orderType: type,
1098
- size: args.amount,
1099
- proposedEntry: orderPrice,
1100
- stopPrice: args.stopPrice,
1101
- targetPrice: args.target_price,
1102
- riskUsd: Math.abs(orderPrice - args.stopPrice) * args.amount,
1103
- thesis: args.thesis,
1104
- setupType: args.setup_type,
1105
- regime: args.regime,
1106
- regimeConfidence: args.regime_confidence,
1107
- scorecardVerdict: args.scorecard_verdict,
1108
- confluenceScore: args.confluence_score,
1109
- ...(args.supersedes_id ? { supersedesId: args.supersedes_id } : {}),
1110
- // Phase A telemetry — agent's create_order ALSO fires the real order
1111
- // synchronously below. Hidden from operator UI by origin='shadow'.
1112
- origin: 'shadow',
1113
- });
1114
- }
1115
- catch (err) {
1116
- // eslint-disable-next-line no-console
1117
- console.warn(`[approval-shadow] propose() threw: ${formatError(err)}`);
1118
- }
1119
- }
1120
- try {
1121
- // Stash the metadata BEFORE submission, keyed by a pre-generated
1122
- // clientOrderId (fixed 2026-07-07; supersedes the 2026-07-05 post-return
1123
- // stash). For market orders the user-data WS fill routinely arrives
1124
- // BEFORE the REST ack resolves — observed on prod (FIL 2026-07-06
1125
- // 20:28:17: WS capture consumed nothing and journaled metadata=none while
1126
- // the post-return stash landed milliseconds later). The WS
1127
- // ORDER_TRADE_UPDATE carries the clientOrderId (`o.c`), so
1128
- // onWsFillObserved falls back to it when the exchange orderId lookup
1129
- // misses; promote() below adds the exchange-orderId alias once the REST
1130
- // ack returns for the normal (WS-after-REST) ordering. If the submission
1131
- // throws, the unused stash entry simply expires (24h TTL, pruned).
1132
- let stashCid = wave9Claimed && initialWave9Mode === 'LIVE'
1133
- ? wave9ClientOrderId(args.candidate_id)
1134
- : undefined;
1135
- if (deps.autoCapture?.pendingEntries && metadata) {
1136
- stashCid ??= randomUUID();
1137
- deps.autoCapture.pendingEntries.put({
1138
- orderId: stashCid,
1139
- clientOrderId: stashCid,
1140
- symbol: args.symbol,
1141
- side: side,
1142
- metadata,
1143
- });
1144
- }
1145
- if (wave9Lease) {
1146
- if (currentWave9ExecutionMode(deps) !== initialWave9Mode) {
1147
- deps.wave9AdmissionGuard.abort(wave9Lease, 'trading_mode_changed_before_mutation', { retryable: true, latchEvent: false });
1148
- wave9Lease = undefined;
1149
- return { error: 'Wave 9 order refused: adapter or trading mode changed before mutation.' };
1150
- }
1151
- if (deps.wave9VenueCheck?.() !== 'binance') {
1152
- deps.wave9AdmissionGuard.abort(wave9Lease, 'execution_venue_changed_before_mutation', { retryable: true, latchEvent: false });
1153
- wave9Lease = undefined;
1154
- return { error: 'Wave 9 order refused: execution venue changed away from Binance before mutation.' };
1155
- }
1156
- let activation;
1157
- try {
1158
- activation = deps.wave9ActivationCheck
1159
- ? await deps.wave9ActivationCheck()
1160
- : { available: false, active: false };
1161
- }
1162
- catch {
1163
- activation = { available: false, active: false };
1164
- }
1165
- if (!activation.available || !activation.active) {
1166
- deps.wave9AdmissionGuard.abort(wave9Lease, 'activation_unavailable_or_inactive_before_mutation', { retryable: true, latchEvent: false });
1167
- wave9Lease = undefined;
1168
- return { error: 'Wave 9 order refused: strategy was toggled off or activation could not be re-verified.' };
1169
- }
1170
- if (deps.adapter.readiness !== 'READY') {
1171
- deps.wave9AdmissionGuard.abort(wave9Lease, 'adapter_not_ready_before_mutation', { retryable: true, latchEvent: false });
1172
- wave9Lease = undefined;
1173
- return { error: `Wave 9 order refused: adapter changed to ${deps.adapter.readiness} before mutation.` };
1174
- }
1175
- if (initialWave9Mode === 'LIVE' && !bracketsEnabled(loadBracketMode())) {
1176
- deps.wave9AdmissionGuard.abort(wave9Lease, 'native_brackets_disabled_before_mutation', { retryable: true, latchEvent: false });
1177
- wave9Lease = undefined;
1178
- return { error: 'Wave 9 live order refused: native bracket protection was disabled before mutation.' };
1179
- }
1180
- }
1181
- const submit = async () => {
1182
- if (!wave9Claimed && !deps.adapter.isLive) {
1183
- deps.refreshPaperState?.();
1184
- const positions = await deps.adapter.getPositionsOrNull();
1185
- if (positions === null) {
1186
- throw new Error('generic create_order final isolation check could not read paper positions');
1187
- }
1188
- if (positions.some(isWave9ManagedPosition)) {
1189
- throw new Error('Generic create_order refused at mutation boundary: the paper account now contains an atomic Wave 9 position.');
1190
- }
1191
- }
1192
- if (!wave9Claimed && deps.adapter.isLive && deps.checkWave9LiveSymbolOwnership) {
1193
- let ownership;
1194
- try {
1195
- ownership = await deps.checkWave9LiveSymbolOwnership(args.symbol);
1196
- }
1197
- catch (err) {
1198
- throw new Error(`Generic create_order refused at mutation boundary: Wave 9 LIVE ownership ` +
1199
- `check failed for ${args.symbol} (${formatError(err)}).`);
1200
- }
1201
- if (ownership.status === 'owned' || ownership.status === 'ambiguous') {
1202
- throw new Error(`Generic create_order refused at mutation boundary: ${args.symbol} has ` +
1203
- `${ownership.status} Wave 9 LIVE ownership (${ownership.reason}).`);
1204
- }
1205
- }
1206
- return deps.adapter.createOrder(args.symbol, side, type, args.amount, args.price, metadata, stashCid ? { clientOrderId: stashCid } : undefined);
1207
- };
1208
- const submitWave9 = async () => {
1209
- if (!wave9Lease)
1210
- throw new Error('Wave 9 admission lease is unavailable at mutation boundary.');
1211
- if (currentWave9ExecutionMode(deps) !== initialWave9Mode) {
1212
- deps.wave9AdmissionGuard.abort(wave9Lease, 'trading_mode_changed_during_activation_check', { retryable: true, latchEvent: false });
1213
- wave9Lease = undefined;
1214
- throw new Error('Wave 9 order refused: adapter or trading mode changed during activation verification.');
1215
- }
1216
- if (deps.wave9VenueCheck?.() !== 'binance') {
1217
- deps.wave9AdmissionGuard.abort(wave9Lease, 'execution_venue_changed_at_mutation_boundary', { retryable: true, latchEvent: false });
1218
- wave9Lease = undefined;
1219
- throw new Error('Wave 9 order refused: execution venue changed away from Binance at mutation boundary.');
1220
- }
1221
- let currentFingerprint;
1222
- if (initialWave9Mode === 'LIVE') {
1223
- const ownership = deps.checkWave9LiveSymbolOwnership
1224
- ? await deps.checkWave9LiveSymbolOwnership(args.symbol)
1225
- : await inspectWave9LiveSymbolOwnership(deps.adapter, deps.wave9LiveLedger, args.symbol);
1226
- if (ownership.status !== 'unowned'
1227
- || ownership.reason === 'authoritative_symbol_flat') {
1228
- deps.wave9AdmissionGuard.abort(wave9Lease, `live_symbol_ownership_changed_before_mutation:${ownership.status}:${ownership.reason}`, { retryable: true, latchEvent: false });
1229
- wave9Lease = undefined;
1230
- throw new Error(`Wave 9 live order refused: candidate-symbol ownership changed before mutation ` +
1231
- `(${ownership.status}:${ownership.reason}); refresh get_wave9_status.`);
1232
- }
1233
- currentFingerprint = (await deps.captureWave9AccountFingerprint()).fingerprint;
1234
- }
1235
- else {
1236
- deps.refreshPaperState?.();
1237
- currentFingerprint = captureWave9PaperAccountSnapshot(deps.adapter).fingerprint;
1238
- }
1239
- deps.wave9AdmissionGuard.revalidate(wave9Lease, currentFingerprint);
1240
- if (initialWave9Mode === 'LIVE') {
1241
- const preEntryFlat = await confirmWave9LivePositionFlat(deps.adapter, args.symbol);
1242
- if (!preEntryFlat.confirmed) {
1243
- deps.wave9AdmissionGuard.abort(wave9Lease, `live_candidate_symbol_not_stably_flat_before_entry:${preEntryFlat.detail}`, { retryable: true, latchEvent: false });
1244
- wave9Lease = undefined;
1245
- throw new Error(`Wave 9 live order refused: candidate symbol was not authoritatively stably flat ` +
1246
- `immediately before entry (${preEntryFlat.detail}). No order was submitted; refresh ` +
1247
- 'get_wave9_status.');
1248
- }
1249
- const refuseAfterFlatProof = (reason, detail) => {
1250
- if (wave9Lease) {
1251
- deps.wave9AdmissionGuard.abort(wave9Lease, reason, { retryable: true, latchEvent: false });
1252
- }
1253
- wave9Lease = undefined;
1254
- throw new Error(`${detail} No order was submitted; refresh get_wave9_status.`);
1255
- };
1256
- let postFlatActivation;
1257
- try {
1258
- postFlatActivation = deps.wave9ActivationCheck
1259
- ? await deps.wave9ActivationCheck()
1260
- : { available: false, active: false };
1261
- }
1262
- catch {
1263
- postFlatActivation = { available: false, active: false };
1264
- }
1265
- if (!postFlatActivation.available || !postFlatActivation.active) {
1266
- refuseAfterFlatProof('activation_unavailable_or_inactive_after_flat_proof', 'Wave 9 live order refused: the strategy was toggled off or activation could not be re-verified after the stable-flat proof.');
1267
- }
1268
- if (currentWave9ExecutionMode(deps) !== initialWave9Mode) {
1269
- refuseAfterFlatProof('trading_mode_changed_after_flat_proof', 'Wave 9 live order refused: adapter or trading mode changed after the stable-flat proof.');
1270
- }
1271
- if (deps.wave9VenueCheck?.() !== 'binance') {
1272
- refuseAfterFlatProof('execution_venue_changed_after_flat_proof', 'Wave 9 live order refused: execution venue changed away from Binance after the stable-flat proof.');
1273
- }
1274
- if (deps.adapter.readiness !== 'READY') {
1275
- refuseAfterFlatProof('adapter_not_ready_after_flat_proof', `Wave 9 live order refused: adapter changed to ${deps.adapter.readiness} after the stable-flat proof.`);
1276
- }
1277
- if (!bracketsEnabled(loadBracketMode())) {
1278
- refuseAfterFlatProof('native_brackets_disabled_after_flat_proof', 'Wave 9 live order refused: native bracket protection was disabled after the stable-flat proof.');
1279
- }
1280
- let postFlatFingerprint;
1281
- try {
1282
- postFlatFingerprint = (await deps.captureWave9AccountFingerprint()).fingerprint;
1283
- }
1284
- catch (error) {
1285
- refuseAfterFlatProof(`live_account_fingerprint_unavailable_after_flat_proof:${formatError(error)}`, `Wave 9 live order refused: the LIVE account fingerprint could not be refreshed ` +
1286
- `after the stable-flat proof (${formatError(error)}).`);
1287
- }
1288
- try {
1289
- deps.wave9AdmissionGuard.revalidate(wave9Lease, postFlatFingerprint);
1290
- }
1291
- catch (error) {
1292
- refuseAfterFlatProof(`authorization_revalidation_failed_after_flat_proof:${formatError(error)}`, `Wave 9 live order refused: authorization or LIVE account identity changed during ` +
1293
- `the stable-flat proof (${formatError(error)}).`);
1294
- }
1295
- const [eventTime, , strategy, direction] = args.candidate_id.split('|');
1296
- await deps.wave9LiveLedger.recordAttempt({
1297
- candidateId: args.candidate_id,
1298
- missionId: args.mission_id,
1299
- eventTime,
1300
- symbol: args.symbol,
1301
- strategy: strategy,
1302
- direction: direction,
1303
- side: side,
1304
- quantity: args.amount,
1305
- initialStop: args.stopPrice,
1306
- clientOrderId: stashCid,
1307
- });
1308
- }
1309
- const finishLiveEntryRecovery = (recovery, source) => {
1310
- if (recovery.status === 'protected') {
1311
- deps.wave9AdmissionGuard.commit(wave9Lease);
1312
- wave9Lease = undefined;
1313
- if (recovery.order)
1314
- return recovery.order;
1315
- throw new Error(`Wave 9 LIVE ${source}, but the entry was recovered OPEN and protected. ` +
1316
- `Do not retry this candidate. ${recovery.detail}`);
1317
- }
1318
- deps.wave9AdmissionGuard.abort(wave9Lease, recovery.status === 'terminal'
1319
- ? 'live_entry_recovery_confirmed_terminal'
1320
- : 'live_entry_recovery_manual_intervention');
1321
- wave9Lease = undefined;
1322
- throw new Error(recovery.status === 'terminal'
1323
- ? `Wave 9 LIVE ${source} resolved safely and is terminal. ${recovery.detail}`
1324
- : `CRITICAL: ${recovery.detail}`);
1325
- };
1326
- let order;
1327
- try {
1328
- order = await submit();
1329
- }
1330
- catch (submitError) {
1331
- if (initialWave9Mode !== 'LIVE')
1332
- throw submitError;
1333
- const recovery = await recoverWave9LiveSubmitThrow({
1334
- adapter: deps.adapter,
1335
- ledger: deps.wave9LiveLedger,
1336
- candidateId: args.candidate_id,
1337
- clientOrderId: stashCid,
1338
- symbol: args.symbol,
1339
- side: side,
1340
- stopPrice: args.stopPrice,
1341
- authorizedQuantity: args.amount,
1342
- submitError,
1343
- confirmStop: deps.confirmWave9LiveStop,
1344
- residualProtector: deps.reprotectWave9LiveResidual,
1345
- });
1346
- return finishLiveEntryRecovery(recovery, 'submit acknowledgement failed');
1347
- }
1348
- const filledQuantity = typeof order.filled === 'number'
1349
- && Number.isFinite(order.filled)
1350
- && order.filled > 0
1351
- ? order.filled
1352
- : undefined;
1353
- if (filledQuantity === undefined) {
1354
- if (initialWave9Mode === 'LIVE') {
1355
- const recovery = await recoverWave9LiveSubmitThrow({
1356
- adapter: deps.adapter,
1357
- ledger: deps.wave9LiveLedger,
1358
- candidateId: args.candidate_id,
1359
- clientOrderId: stashCid,
1360
- symbol: args.symbol,
1361
- side: side,
1362
- stopPrice: args.stopPrice,
1363
- authorizedQuantity: args.amount,
1364
- submitError: new Error('market acknowledgement lacked a positive confirmed fill quantity'),
1365
- acknowledgedOrder: order,
1366
- confirmStop: deps.confirmWave9LiveStop,
1367
- residualProtector: deps.reprotectWave9LiveResidual,
1368
- });
1369
- return finishLiveEntryRecovery(recovery, 'market acknowledgement lacked a positive confirmed fill quantity');
1370
- }
1371
- deps.wave9AdmissionGuard.abort(wave9Lease, 'market_order_not_filled');
1372
- wave9Lease = undefined;
1373
- throw new Error('Wave 9 market order lacks a positive confirmed fill quantity; admission batch latched.');
1374
- }
1375
- if (initialWave9Mode === 'LIVE'
1376
- && !candidateBoundFoundOrder({ status: 'found', order }, stashCid, args.symbol, side)) {
1377
- const recovery = await recoverWave9LiveSubmitThrow({
1378
- adapter: deps.adapter,
1379
- ledger: deps.wave9LiveLedger,
1380
- candidateId: args.candidate_id,
1381
- clientOrderId: stashCid,
1382
- symbol: args.symbol,
1383
- side: side,
1384
- stopPrice: args.stopPrice,
1385
- authorizedQuantity: args.amount,
1386
- submitError: new Error('market acknowledgement had malformed or mismatched candidate identity'),
1387
- confirmStop: deps.confirmWave9LiveStop,
1388
- residualProtector: deps.reprotectWave9LiveResidual,
1389
- });
1390
- return finishLiveEntryRecovery(recovery, 'market acknowledgement had malformed or mismatched candidate identity');
1391
- }
1392
- if (initialWave9Mode === 'LIVE' && order.status === 'open') {
1393
- const recovery = await recoverWave9LiveSubmitThrow({
1394
- adapter: deps.adapter,
1395
- ledger: deps.wave9LiveLedger,
1396
- candidateId: args.candidate_id,
1397
- clientOrderId: stashCid,
1398
- symbol: args.symbol,
1399
- side: side,
1400
- stopPrice: args.stopPrice,
1401
- authorizedQuantity: args.amount,
1402
- submitError: new Error('market acknowledgement left an open entry remainder'),
1403
- acknowledgedOrder: order,
1404
- confirmStop: deps.confirmWave9LiveStop,
1405
- residualProtector: deps.reprotectWave9LiveResidual,
1406
- });
1407
- return finishLiveEntryRecovery(recovery, 'market acknowledgement left an open entry remainder');
1408
- }
1409
- if (initialWave9Mode === 'LIVE') {
1410
- const entryPrice = confirmedWave9EntryPrice(order, side, args.stopPrice);
1411
- const exchangeOrderId = confirmedWave9ExchangeOrderId(order);
1412
- let openPersistenceError;
1413
- try {
1414
- await deps.wave9LiveLedger.markOpen(args.candidate_id, {
1415
- filledQuantity,
1416
- ...(exchangeOrderId ? { exchangeOrderId } : {}),
1417
- ...(entryPrice !== undefined ? { entryPrice } : {}),
1418
- });
1419
- }
1420
- catch (err) {
1421
- openPersistenceError = formatError(err);
1422
- }
1423
- if (openPersistenceError) {
1424
- const flatten = await flattenFailedWave9LiveEntry(deps.adapter, deps.wave9LiveLedger, args.symbol, args.candidate_id, {
1425
- side: side,
1426
- maximumQuantity: Math.min(filledQuantity, args.amount),
1427
- }, deps.reprotectWave9LiveResidual);
1428
- deps.wave9AdmissionGuard.abort(wave9Lease, flatten.confirmedFlat
1429
- ? 'live_open_persistence_failed_confirmed_flat'
1430
- : 'live_open_persistence_failed_residual_exposure');
1431
- wave9Lease = undefined;
1432
- const flattenError = flatten.flattenError
1433
- ? ` Close submission error: ${flatten.flattenError}.`
1434
- : '';
1435
- if (flatten.confirmedFlat && !flatten.terminalPersistenceError) {
1436
- throw new Error(`Wave 9 confirmed fill could not be durably marked OPEN (${openPersistenceError}); ` +
1437
- `${flatten.closeAttempted
1438
- ? 'the position was auto-flattened and confirmed flat'
1439
- : 'the exchange position was already confirmed stably flat'}, and its replay tombstone was ` +
1440
- `persisted terminal.${flattenError}`);
1441
- }
1442
- if (flatten.confirmedFlat) {
1443
- throw new Error(`Wave 9 confirmed fill could not be durably marked OPEN (${openPersistenceError}). ` +
1444
- `The exchange is confirmed flat, but durable CLOSED persistence also failed ` +
1445
- `(${flatten.terminalPersistenceError}).${flattenError}`);
1446
- }
1447
- throw new Error(`CRITICAL: Wave 9 confirmed fill could not be durably marked OPEN ` +
1448
- `(${openPersistenceError}), and ${flatten.closeAttempted
1449
- ? 'emergency flatten is not confirmed flat'
1450
- : 'the exposure identity fence refused symbol-wide flatten'} ` +
1451
- `(${flatten.confirmationDetail}). In-memory OPEN ownership was retained where possible; ` +
1452
- `the entry capability is consumed and latched.${flattenError}${residualProtectionMessage(flatten)}`);
1453
- }
1454
- let stopConfirmed = false;
1455
- try {
1456
- stopConfirmed = await deps.confirmWave9LiveStop({
1457
- candidateId: args.candidate_id,
1458
- symbol: args.symbol,
1459
- entryClientOrderId: stashCid,
1460
- entrySide: side,
1461
- stopPrice: args.stopPrice,
1462
- filledQuantity,
1463
- });
1464
- }
1465
- catch {
1466
- stopConfirmed = false;
1467
- }
1468
- if (!stopConfirmed) {
1469
- const flatten = await flattenFailedWave9LiveEntry(deps.adapter, deps.wave9LiveLedger, args.symbol, args.candidate_id, {
1470
- side: side,
1471
- maximumQuantity: Math.min(filledQuantity, args.amount),
1472
- }, deps.reprotectWave9LiveResidual);
1473
- deps.wave9AdmissionGuard.abort(wave9Lease, flatten.confirmedFlat
1474
- ? 'live_stop_unconfirmed_confirmed_flat'
1475
- : 'live_stop_unconfirmed_residual_exposure');
1476
- wave9Lease = undefined;
1477
- const flattenError = flatten.flattenError
1478
- ? ` Close submission error: ${flatten.flattenError}.`
1479
- : '';
1480
- if (flatten.confirmedFlat && !flatten.terminalPersistenceError) {
1481
- throw new Error(`Wave 9 live stop was not confirmed; ${flatten.closeAttempted
1482
- ? 'the filled position was auto-flattened and confirmed flat'
1483
- : 'the exchange position was already confirmed stably flat'}, and its durable ` +
1484
- `execution row is terminal.${flattenError}`);
1485
- }
1486
- if (flatten.confirmedFlat) {
1487
- throw new Error(`Wave 9 live stop was not confirmed. The exchange is confirmed flat, but durable ` +
1488
- `CLOSED persistence failed (${flatten.terminalPersistenceError}).${flattenError}`);
1489
- }
1490
- throw new Error(`CRITICAL: Wave 9 live stop was not confirmed and ${flatten.closeAttempted
1491
- ? 'emergency flatten is not confirmed flat'
1492
- : 'the exposure identity fence refused symbol-wide flatten'} ` +
1493
- `(${flatten.confirmationDetail}). Durable ownership remains OPEN; the entry ` +
1494
- `capability is consumed and latched.${flattenError}${residualProtectionMessage(flatten)}`);
1495
- }
1496
- }
1497
- deps.wave9AdmissionGuard.commit(wave9Lease);
1498
- wave9Lease = undefined;
1499
- return order;
1500
- };
1501
- const order = wave9Claimed
1502
- ? await deps.operationLock.withAccountLock(`wave9-create-final:${args.symbol}`, submitWave9)
1503
- : deps.operationLock
1504
- ? await deps.operationLock.withAccountLock(`generic-create-final:${args.symbol}`, submit)
1505
- : await submit();
1506
- // Auto-capture entry to the Position Decision Journal — fail-open, never
1507
- // block the trading hot path on a webapp ingest blip.
1508
- if (deps.autoCapture) {
1509
- if (stashCid &&
1510
- deps.autoCapture.pendingEntries &&
1511
- typeof order.id === 'string' &&
1512
- order.id.length > 0) {
1513
- deps.autoCapture.pendingEntries.promote(stashCid, order.id);
1514
- }
1515
- const filledNow = typeof order.filled === 'number' && order.filled > 0;
1516
- if (filledNow) {
1517
- // Order filled at submit (market, or limit-that-crossed). Capture
1518
- // synchronously — same as the original PR1 path.
1519
- onCreateOrderFilled(deps.autoCapture, { symbol: args.symbol, side: side, metadata }, order).catch((err) => {
1520
- // eslint-disable-next-line no-console
1521
- console.warn(`[position-auto-capture] entry capture threw: ${formatError(err)}`);
1522
- });
1523
- }
1524
- }
1525
- return order;
1526
- }
1527
- catch (err) {
1528
- if (wave9Lease) {
1529
- deps.wave9AdmissionGuard.abort(wave9Lease, `order_execution_failed:${formatError(err)}`);
1530
- wave9Lease = undefined;
1531
- }
1532
- return { error: formatError(err) };
1533
- }
1534
- }
1535
- catch (err) {
1536
- if (!wave9Claimed)
1537
- throw err;
1538
- if (wave9Lease) {
1539
- deps.wave9AdmissionGuard.abort(wave9Lease, `pre_mutation_processing_failed:${formatError(err)}`, { retryable: true, latchEvent: false });
1540
- wave9Lease = undefined;
1541
- }
1542
- return { error: formatError(err) };
1543
- }
1544
- }